Browse Source

align with latest release queue

Frank-Rainer Grahl 9 months ago
parent
commit
0b46d647ee
64 changed files with 68126 additions and 3499 deletions
  1. 53 0
      frg/work-js/mozilla-release/patches/1382577-60a1.patch
  2. 197 0
      frg/work-js/mozilla-release/patches/1382604-60a1.patch
  3. 3 2
      frg/work-js/mozilla-release/patches/1385998-3-60a1.patch
  4. 338 0
      frg/work-js/mozilla-release/patches/1403188-61a1.patch
  5. 4 4
      frg/work-js/mozilla-release/patches/1408790-58a1.patch
  6. 1383 0
      frg/work-js/mozilla-release/patches/1420499-59a1.patch
  7. 36 0
      frg/work-js/mozilla-release/patches/1425866-61a1.patch
  8. 2026 0
      frg/work-js/mozilla-release/patches/1428170-60a1.patch
  9. 31 0
      frg/work-js/mozilla-release/patches/1429271-59a1.patch
  10. 48 0
      frg/work-js/mozilla-release/patches/1433929-1-60a1.patch
  11. 890 0
      frg/work-js/mozilla-release/patches/1433929-2-60a1.patch
  12. 38 0
      frg/work-js/mozilla-release/patches/1433929-3no4-60a1.patch
  13. 72 0
      frg/work-js/mozilla-release/patches/1433929-5-60a1.patch
  14. 12 0
      frg/work-js/mozilla-release/patches/1434225-60a1.patch
  15. 667 0
      frg/work-js/mozilla-release/patches/1434295-60a1.patch
  16. 356 0
      frg/work-js/mozilla-release/patches/1437147-60a1.patch
  17. 2235 0
      frg/work-js/mozilla-release/patches/1440321-1a-aboutdebugging-61a1.patch
  18. 2815 0
      frg/work-js/mozilla-release/patches/1440321-1b-canvasdebugger-61a1.patch
  19. 30 0
      frg/work-js/mozilla-release/patches/1440321-1c-commandline-61a1.patch
  20. 51 0
      frg/work-js/mozilla-release/patches/1440321-1d-debugger-61a1.patch
  21. 216 0
      frg/work-js/mozilla-release/patches/1440321-1e-dom-61a1.patch
  22. 5197 0
      frg/work-js/mozilla-release/patches/1440321-1f-framework-61a1.patch
  23. 34 0
      frg/work-js/mozilla-release/patches/1440321-1g-inspector-61a1.patch
  24. 834 0
      frg/work-js/mozilla-release/patches/1440321-1h-jsonview-61a1.patch
  25. 5777 0
      frg/work-js/mozilla-release/patches/1440321-1i-memory-61a1.patch
  26. 8649 0
      frg/work-js/mozilla-release/patches/1440321-1j-performance-61a1.patch
  27. 535 0
      frg/work-js/mozilla-release/patches/1440321-1k-scratchpad-61a1.patch
  28. 2950 0
      frg/work-js/mozilla-release/patches/1440321-1l-shadereditor-61a1.patch
  29. 10544 0
      frg/work-js/mozilla-release/patches/1440321-1m-shared-61a1.patch
  30. 330 0
      frg/work-js/mozilla-release/patches/1440321-1n-sourceeditor-61a1.patch
  31. 3369 0
      frg/work-js/mozilla-release/patches/1440321-1o-storage-61a1.patch
  32. 2649 0
      frg/work-js/mozilla-release/patches/1440321-1p-styleeditor-61a1.patch
  33. 3160 0
      frg/work-js/mozilla-release/patches/1440321-1q-webaudioeditor-61a1.patch
  34. 1772 0
      frg/work-js/mozilla-release/patches/1440321-1r-webconsole-61a1.patch
  35. 73 0
      frg/work-js/mozilla-release/patches/1440321-2-61a1.patch
  36. 29 0
      frg/work-js/mozilla-release/patches/1440321-3-61a1.patch
  37. 53 0
      frg/work-js/mozilla-release/patches/1440321-4-61a1.patch
  38. 1355 0
      frg/work-js/mozilla-release/patches/1441225-60a1.patch
  39. 43 0
      frg/work-js/mozilla-release/patches/1441528-60a1.patch
  40. 705 0
      frg/work-js/mozilla-release/patches/1441896-60a1.patch
  41. 556 0
      frg/work-js/mozilla-release/patches/1442126-60a1.patch
  42. 81 0
      frg/work-js/mozilla-release/patches/1442153-61a1.patch
  43. 48 48
      frg/work-js/mozilla-release/patches/1443081-12-client-shared-61a1.patch
  44. 8 3
      frg/work-js/mozilla-release/patches/1443081-20-server-tests-61a1.patch
  45. 174 0
      frg/work-js/mozilla-release/patches/1443344-60a1.patch
  46. 86 0
      frg/work-js/mozilla-release/patches/1443393-60a1.patch
  47. 230 0
      frg/work-js/mozilla-release/patches/1444073-60a1.patch
  48. 2 2
      frg/work-js/mozilla-release/patches/1445496-61a1.patch
  49. 32 0
      frg/work-js/mozilla-release/patches/1448194-61a1.patch
  50. 132 0
      frg/work-js/mozilla-release/patches/1711872-91a1.patch
  51. 71 0
      frg/work-js/mozilla-release/patches/1721149-92a1.patch
  52. 0 67
      frg/work-js/mozilla-release/patches/L-1415120-1-59a1.patch
  53. 0 54
      frg/work-js/mozilla-release/patches/L-1415120-2-59a1.patch
  54. 0 0
      frg/work-js/mozilla-release/patches/NOBUG-20180206-jsgdb-60a1.patch
  55. 6706 0
      frg/work-js/mozilla-release/patches/NOBUG-nukemozlinker-25319.patch
  56. 35 0
      frg/work-js/mozilla-release/patches/NOBUG-removessdks-25319.patch
  57. 94 0
      frg/work-js/mozilla-release/patches/TOP-1472170-PARTIAL-NOTESTS-63a1.patch
  58. 97 0
      frg/work-js/mozilla-release/patches/TOP-1472170-PARTIAL-NOTESTS-63a1.patcha
  59. 3 3
      frg/work-js/mozilla-release/patches/TOP-NOBUG-PLASTER-fix-strip-25319.patch
  60. 0 2895
      frg/work-js/mozilla-release/patches/mozilla-central_388196.patch
  61. 0 112
      frg/work-js/mozilla-release/patches/mozilla-central_388197.patch
  62. 0 30
      frg/work-js/mozilla-release/patches/mozilla-central_388198.patch
  63. 0 110
      frg/work-js/mozilla-release/patches/mozilla-central_388199.patch
  64. 212 169
      frg/work-js/mozilla-release/patches/series

+ 53 - 0
frg/work-js/mozilla-release/patches/1382577-60a1.patch

@@ -0,0 +1,53 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1519978392 -3600
+# Node ID 8d70045ab41630ea8361597368b427ed7799f80b
+# Parent  8174cf7735c6cdebcd24577ce379b23a209ac9d3
+Bug 1382577 - Remove old-event-emitter usage from canvasdebugger; r=sole.
+
+MozReview-Commit-ID: ASxhdrMSWAQ
+
+diff --git a/devtools/client/canvasdebugger/canvasdebugger.js b/devtools/client/canvasdebugger/canvasdebugger.js
+--- a/devtools/client/canvasdebugger/canvasdebugger.js
++++ b/devtools/client/canvasdebugger/canvasdebugger.js
+@@ -3,17 +3,17 @@
+  * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+ const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
+ const promise = require("promise");
+ const Services = require("Services");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
+ const { CanvasFront } = require("devtools/shared/fronts/canvas");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ const { extend } = require("devtools/shared/extend");
+ const flags = require("devtools/shared/flags");
+ const { LocalizationHelper } = require("devtools/shared/l10n");
+ const { PluralForm } = require("devtools/shared/plural-form");
+ const { WidgetMethods, setNamedTimeout, clearNamedTimeout,
+diff --git a/devtools/client/canvasdebugger/panel.js b/devtools/client/canvasdebugger/panel.js
+--- a/devtools/client/canvasdebugger/panel.js
++++ b/devtools/client/canvasdebugger/panel.js
+@@ -2,17 +2,17 @@
+ /* vim: set ft=javascript ts=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/. */
+ "use strict";
+ 
+ const { Cc, Ci, Cu, Cr } = require("chrome");
+ const promise = require("promise");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const { CanvasFront } = require("devtools/shared/fronts/canvas");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ 
+ function CanvasDebuggerPanel(iframeWindow, toolbox) {
+   this.panelWin = iframeWindow;
+   this._toolbox = toolbox;
+   this._destroyer = null;
+ 

+ 197 - 0
frg/work-js/mozilla-release/patches/1382604-60a1.patch

@@ -0,0 +1,197 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1520243155 -3600
+# Node ID 4cc7620edb4f1e4090b9b6307ef1c788355dd83c
+# Parent  d2947a8d1390c2fd27930a30b4548911d95b119e
+Bug 1382604 - Remove old-event-emitter usage from scratchpad; r=jryans.
+
+Usage was removed from devtools/client/framework/sidebar as well since it emits
+events with the parent component.
+
+MozReview-Commit-ID: 24LLmnSZ89I
+
+diff --git a/devtools/client/framework/sidebar.js b/devtools/client/framework/sidebar.js
+--- a/devtools/client/framework/sidebar.js
++++ b/devtools/client/framework/sidebar.js
+@@ -1,17 +1,17 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ var Services = require("Services");
+ var {Task} = require("devtools/shared/task");
+-var EventEmitter = require("devtools/shared/old-event-emitter");
++var EventEmitter = require("devtools/shared/event-emitter");
+ var Telemetry = require("devtools/client/shared/telemetry");
+ 
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+ 
+ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ 
+ /**
+diff --git a/devtools/client/framework/test/browser_toolbox_sidebar.js b/devtools/client/framework/test/browser_toolbox_sidebar.js
+--- a/devtools/client/framework/test/browser_toolbox_sidebar.js
++++ b/devtools/client/framework/test/browser_toolbox_sidebar.js
+@@ -43,40 +43,40 @@ function test() {
+     gDevTools.showToolbox(target, toolDefinition.id).then(function (toolbox) {
+       let panel = toolbox.getPanel(toolDefinition.id);
+       panel.toolbox = toolbox;
+       ok(true, "Tool open");
+ 
+       let tabbox = panel.panelDoc.getElementById("sidebar");
+       panel.sidebar = new ToolSidebar(tabbox, panel, "testbug865688", true);
+ 
+-      panel.sidebar.on("new-tab-registered", function (event, id) {
++      panel.sidebar.on("new-tab-registered", function (id) {
+         registeredTabs[id] = true;
+       });
+ 
+-      panel.sidebar.once("tab1-ready", function (event) {
+-        info(event);
++      panel.sidebar.once("tab1-ready", function () {
++        info("tab1-ready");
+         readyTabs.tab1 = true;
+         allTabsReady(panel);
+       });
+ 
+-      panel.sidebar.once("tab2-ready", function (event) {
+-        info(event);
++      panel.sidebar.once("tab2-ready", function () {
++        info("tab2-ready");
+         readyTabs.tab2 = true;
+         allTabsReady(panel);
+       });
+ 
+-      panel.sidebar.once("tab3-ready", function (event) {
+-        info(event);
++      panel.sidebar.once("tab3-ready", function () {
++        info("tab3-ready");
+         readyTabs.tab3 = true;
+         allTabsReady(panel);
+       });
+ 
+-      panel.sidebar.once("tab1-selected", function (event) {
+-        info(event);
++      panel.sidebar.once("tab1-selected", function () {
++        info("tab1-selected");
+         tab1Selected = true;
+         allTabsReady(panel);
+       });
+ 
+       panel.sidebar.addTab("tab1", tab1URL, {selected: true});
+       panel.sidebar.addTab("tab2", tab2URL);
+       panel.sidebar.addTab("tab3", tab3URL);
+ 
+@@ -122,18 +122,18 @@ function test() {
+         testRemoval(panel);
+       });
+     });
+ 
+     panel.sidebar.select("tab2");
+   }
+ 
+   function testRemoval(panel) {
+-    panel.sidebar.once("tab-unregistered", function (event, id) {
+-      info(event);
++    panel.sidebar.once("tab-unregistered", function (id) {
++      info("tab-unregistered");
+       registeredTabs[id] = false;
+ 
+       is(id, "tab3", "The right tab must be removed");
+ 
+       let tabs = panel.sidebar._tabbox.querySelectorAll("tab");
+       let panels = panel.sidebar._tabbox.querySelectorAll("tabpanel");
+ 
+       is(tabs.length, 2, "There is the right number of tabs");
+diff --git a/devtools/client/framework/test/browser_toolbox_sidebar_events.js b/devtools/client/framework/test/browser_toolbox_sidebar_events.js
+--- a/devtools/client/framework/test/browser_toolbox_sidebar_events.js
++++ b/devtools/client/framework/test/browser_toolbox_sidebar_events.js
+@@ -34,33 +34,33 @@ function test() {
+   gDevTools.registerTool(toolDefinition);
+ 
+   addTab("about:blank").then(function (aTab) {
+     let target = TargetFactory.forTab(aTab);
+     gDevTools.showToolbox(target, toolDefinition.id).then(function (toolbox) {
+       let panel = toolbox.getPanel(toolDefinition.id);
+       ok(true, "Tool open");
+ 
+-      panel.once("sidebar-created", function (event, id) {
+-        collectedEvents.push(event);
++      panel.once("sidebar-created", function () {
++        collectedEvents.push("sidebar-created");
+       });
+ 
+-      panel.once("sidebar-destroyed", function (event, id) {
+-        collectedEvents.push(event);
++      panel.once("sidebar-destroyed", function () {
++        collectedEvents.push("sidebar-destroyed");
+       });
+ 
+       let tabbox = panel.panelDoc.getElementById("sidebar");
+       panel.sidebar = new ToolSidebar(tabbox, panel, "testbug1072208", true);
+ 
+-      panel.sidebar.once("show", function (event, id) {
+-        collectedEvents.push(event);
++      panel.sidebar.once("show", function () {
++        collectedEvents.push("show");
+       });
+ 
+-      panel.sidebar.once("hide", function (event, id) {
+-        collectedEvents.push(event);
++      panel.sidebar.once("hide", function () {
++        collectedEvents.push("hide");
+       });
+ 
+       panel.sidebar.once("tab1-selected", () => finishUp(panel));
+       panel.sidebar.addTab("tab1", tab1URL, {selected: true});
+       panel.sidebar.show();
+     }).catch(console.error);
+   });
+ 
+diff --git a/devtools/client/scratchpad/scratchpad-panel.js b/devtools/client/scratchpad/scratchpad-panel.js
+--- a/devtools/client/scratchpad/scratchpad-panel.js
++++ b/devtools/client/scratchpad/scratchpad-panel.js
+@@ -1,17 +1,17 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ft=javascript ts=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/. */
+ "use strict";
+ 
+ const {Cu} = require("chrome");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const promise = require("promise");
+ const defer = require("devtools/shared/defer");
+ 
+ 
+ function ScratchpadPanel(iframeWindow, toolbox) {
+   let { Scratchpad } = iframeWindow;
+   this._toolbox = toolbox;
+   this.panelWin = iframeWindow;
+diff --git a/devtools/client/scratchpad/scratchpad.js b/devtools/client/scratchpad/scratchpad.js
+--- a/devtools/client/scratchpad/scratchpad.js
++++ b/devtools/client/scratchpad/scratchpad.js
+@@ -38,17 +38,17 @@ const TAB_SIZE = "devtools.editor.tabsiz
+ const FALLBACK_CHARSET_LIST = "intl.fallbackCharsetList.ISO-8859-1";
+ 
+ const VARIABLES_VIEW_URL = "chrome://devtools/content/shared/widgets/VariablesView.xul";
+ 
+ const {require, loader} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ 
+ const Editor = require("devtools/client/sourceeditor/editor");
+ const TargetFactory = require("devtools/client/framework/target").TargetFactory;
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const {DevToolsWorker} = require("devtools/shared/worker/worker");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ const flags = require("devtools/shared/flags");
+ const promise = require("promise");
+ const defer = require("devtools/shared/defer");
+ const Services = require("Services");
+ const {gDevTools} = require("devtools/client/framework/devtools");
+ const { extend } = require("devtools/shared/extend");

+ 3 - 2
frg/work-js/mozilla-release/patches/1385998-3-60a1.patch

@@ -2,7 +2,7 @@
 # User Markus Stange <mstange@themasta.com>
 # User Markus Stange <mstange@themasta.com>
 # Date 1518751772 18000
 # Date 1518751772 18000
 # Node ID 2d086c21b2a17715efed3b8d08350b26deacb150
 # Node ID 2d086c21b2a17715efed3b8d08350b26deacb150
-# Parent  1f97131573feb6f30e1095ca9c3320caf612259b
+# Parent  bf6a4f1f64c1aa0e36f6a603a2558bbcfe95546c
 Bug 1385998 - Fix a typo in a comment.
 Bug 1385998 - Fix a typo in a comment.
 
 
 MozReview-Commit-ID: AiHDDUKGHhi
 MozReview-Commit-ID: AiHDDUKGHhi
@@ -10,7 +10,7 @@ MozReview-Commit-ID: AiHDDUKGHhi
 diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h
 diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h
 --- a/js/public/ProfilingStack.h
 --- a/js/public/ProfilingStack.h
 +++ b/js/public/ProfilingStack.h
 +++ b/js/public/ProfilingStack.h
-@@ -50,17 +50,17 @@ class PseudoStack;
+@@ -48,17 +48,17 @@ class PseudoStack;
  //  (3) Thread A is resumed.
  //  (3) Thread A is resumed.
  //
  //
  // Thread suspension is achieved using platform-specific APIs; refer to each
  // Thread suspension is achieved using platform-specific APIs; refer to each
@@ -29,3 +29,4 @@ diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h
  //      ProfileEntry data for the next frame that is about to be pushed, the
  //      ProfileEntry data for the next frame that is about to be pushed, the
  //      decrement of the stackPointer in pop() needs to happen *before* the
  //      decrement of the stackPointer in pop() needs to happen *before* the
  //      ProfileEntry for the new frame is being popuplated, and the compiler +
  //      ProfileEntry for the new frame is being popuplated, and the compiler +
+

+ 338 - 0
frg/work-js/mozilla-release/patches/1403188-61a1.patch

@@ -0,0 +1,338 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1518529166 -3600
+# Node ID 7b801fbf9bc4f4d6c3c1928d88d58ea4322e27bc
+# Parent  5d8dddd357af746cceefe6df6f1f34f5f27ce166
+Bug 1403188 - Enable browser_console_webconsole_private_browsing.js in new console frontend; r=jdescottes.
+
+MozReview-Commit-ID: EvFyvzpCUH7
+
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+@@ -176,18 +176,16 @@ skip-if = true # Bug 1437843
+ [browser_console_filters.js]
+ [browser_console_nsiconsolemessage.js]
+ [browser_console_open_or_focus.js]
+ [browser_console_restore.js]
+ [browser_console_webconsole_console_api_calls.js]
+ [browser_console_webconsole_ctrlw_close_tab.js]
+ [browser_console_webconsole_iframe_messages.js]
+ [browser_console_webconsole_private_browsing.js]
+-skip-if = true #       Bug 1403188
+-# old console skip-if = e10s # Bug 1042253 - webconsole e10s tests
+ [browser_jsterm_accessibility.js]
+ [browser_jsterm_add_edited_input_to_history.js]
+ [browser_jsterm_autocomplete_array_no_index.js]
+ [browser_jsterm_autocomplete_cached_results.js]
+ [browser_jsterm_autocomplete_crossdomain_iframe.js]
+ [browser_jsterm_autocomplete_escape_key.js]
+ [browser_jsterm_autocomplete_extraneous_closing_brackets.js]
+ [browser_jsterm_autocomplete_helpers.js]
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_webconsole_private_browsing.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_webconsole_private_browsing.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_webconsole_private_browsing.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_webconsole_private_browsing.js
+@@ -5,191 +5,111 @@
+ 
+ /* import-globals-from head.js */
+ 
+ // Bug 874061: test for how the browser and web consoles display messages coming
+ // from private windows. See bug for description of expected behavior.
+ 
+ "use strict";
+ 
+-function test() {
+-  const TEST_URI = "data:text/html;charset=utf8,<p>hello world! bug 874061" +
+-                   "<button onclick='console.log(\"foobar bug 874061\");" +
+-                   "fooBazBaz.yummy()'>click</button>";
+-  let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
+-                            .getService(Ci.nsIConsoleAPIStorage);
+-  let privateWindow, privateBrowser, privateTab, privateContent;
+-  let hud, expectedMessages, nonPrivateMessage;
+-
+-  // This test is slightly more involved: it opens the web console twice,
+-  // a new private window once, and the browser console twice. We can get
+-  // a timeout with debug builds on slower machines.
+-  requestLongerTimeout(2);
+-  start();
++const NON_PRIVATE_MESSAGE = "This is not a private message";
++const PRIVATE_MESSAGE = "This is a private message";
++const PRIVATE_UNDEFINED_FN = "privateException";
++const PRIVATE_EXCEPTION = `${PRIVATE_UNDEFINED_FN} is not defined`;
+ 
+-  function start() {
+-    gBrowser.selectedTab =
+-      BrowserTestUtils.addTab(gBrowser, "data:text/html;charset=utf8," +
+-                                        "<p>hello world! I am not private!");
+-    gBrowser.selectedBrowser.addEventListener("load", onLoadTab, true);
+-  }
+-
+-  function onLoadTab() {
+-    gBrowser.selectedBrowser.removeEventListener("load", onLoadTab, true);
+-    info("onLoadTab()");
++const NON_PRIVATE_TEST_URI = "data:text/html;charset=utf8,Not private";
++const PRIVATE_TEST_URI = `data:text/html;charset=utf8,Test console in private windows
++  <script>
++    function logMessages() {
++      /* Wrap the exception so we don't throw in ContentTask. */
++      setTimeout(() => {
++        console.log("${PRIVATE_MESSAGE}");
++        ${PRIVATE_UNDEFINED_FN}();
++      }, 10);
++    }
++  </script>`;
+ 
+-    // Make sure we have a clean state to start with.
+-    Services.console.reset();
+-    ConsoleAPIStorage.clearEvents();
+-
+-    // Add a non-private message to the browser console.
+-    ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
+-      content.console.log("bug874061-not-private");
+-    });
+-
+-    nonPrivateMessage = {
+-      name: "console message from a non-private window",
+-      text: "bug874061-not-private",
+-      category: CATEGORY_WEBDEV,
+-      severity: SEVERITY_LOG,
+-    };
++add_task(async function() {
++  await addTab(NON_PRIVATE_TEST_URI);
+ 
+-    privateWindow = OpenBrowserWindow({ private: true });
+-    ok(privateWindow, "new private window");
+-    ok(PrivateBrowsingUtils.isWindowPrivate(privateWindow), "window's private");
+-
+-    whenDelayedStartupFinished(privateWindow, onPrivateWindowReady);
+-  }
+-
+-  function onPrivateWindowReady() {
+-    info("private browser window opened");
+-    privateBrowser = privateWindow.gBrowser;
++  const privateWindow = await openNewBrowserWindow({ private: true });
++  ok(PrivateBrowsingUtils.isWindowPrivate(privateWindow), "window is private");
++  const privateBrowser = privateWindow.gBrowser;
++  privateBrowser.selectedTab = privateBrowser.addTab(PRIVATE_TEST_URI);
++  const privateTab = privateBrowser.selectedTab;
+ 
+-    privateTab = privateBrowser.selectedTab = privateBrowser.addTab(TEST_URI);
+-    privateBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+-      info("private tab opened");
+-      privateBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+-      privateContent = privateBrowser.selectedBrowser.contentWindow;
+-      ok(PrivateBrowsingUtils.isBrowserPrivate(privateBrowser.selectedBrowser),
+-         "tab window is private");
+-      openConsole(privateTab).then(consoleOpened);
+-    }, true);
+-  }
++  info("private tab opened");
++  ok(PrivateBrowsingUtils.isBrowserPrivate(privateBrowser.selectedBrowser),
++    "tab window is private");
++
++  let hud = await openConsole(privateTab);
++  ok(hud, "web console opened");
+ 
+-  function addMessages() {
+-    let button = privateContent.document.querySelector("button");
+-    ok(button, "button in page");
+-    EventUtils.synthesizeMouse(button, 2, 2, {}, privateContent);
+-  }
++  const onLogMessage = waitForMessage(hud, PRIVATE_MESSAGE);
++  const onErrorMessage = waitForMessage(hud, PRIVATE_EXCEPTION, ".error");
++  logPrivateMessages(privateBrowser.selectedBrowser);
+ 
+-  function consoleOpened(injectedHud) {
+-    hud = injectedHud;
+-    ok(hud, "web console opened");
++  await onLogMessage;
++  await onErrorMessage;
++  ok(true, "Messages are displayed as expected");
+ 
+-    addMessages();
+-    expectedMessages = [
+-      {
+-        name: "script error",
+-        text: "fooBazBaz is not defined",
+-        category: CATEGORY_JS,
+-        severity: SEVERITY_ERROR,
+-      },
+-      {
+-        name: "console message",
+-        text: "foobar bug 874061",
+-        category: CATEGORY_WEBDEV,
+-        severity: SEVERITY_LOG,
+-      },
+-    ];
++  info("test cached messages");
++  await closeConsole(privateTab);
++  info("web console closed");
++  hud = await openConsole(privateTab);
++  ok(hud, "web console reopened");
++
++  await waitFor(() => findMessage(hud, PRIVATE_MESSAGE));
++  await waitFor(() => findMessage(hud, PRIVATE_EXCEPTION, ".message.error"));
++  ok(true, "Messages are still displayed after closing and reopening the console");
+ 
+-    // Make sure messages are displayed in the web console as they happen, even
+-    // if this is a private tab.
+-    waitForMessages({
+-      webconsole: hud,
+-      messages: expectedMessages,
+-    }).then(testCachedMessages);
+-  }
++  info("Test browser console");
++  await closeConsole(privateTab);
++  info("web console closed");
++  hud = await HUDService.toggleBrowserConsole();
+ 
+-  function testCachedMessages() {
+-    info("testCachedMessages()");
+-    closeConsole(privateTab).then(() => {
+-      info("web console closed");
+-      openConsole(privateTab).then(consoleReopened);
+-    });
+-  }
+-
+-  function consoleReopened(injectedHud) {
+-    hud = injectedHud;
+-    ok(hud, "web console reopened");
++  // Make sure that the cached messages from private tabs are not displayed in the
++  // browser console.
++  assertNoPrivateMessages(hud);
+ 
+-    // Make sure that cached messages are displayed in the web console, even
+-    // if this is a private tab.
+-    waitForMessages({
+-      webconsole: hud,
+-      messages: expectedMessages,
+-    }).then(testBrowserConsole);
+-  }
++  // Add a non-private message to the console.
++  const onBrowserConsoleNonPrivateMessage = waitForMessage(hud, NON_PRIVATE_MESSAGE);
++  ContentTask.spawn(gBrowser.selectedBrowser, NON_PRIVATE_MESSAGE, function(msg) {
++    content.console.log(msg);
++  });
++  await onBrowserConsoleNonPrivateMessage;
+ 
+-  function testBrowserConsole() {
+-    info("testBrowserConsole()");
+-    closeConsole(privateTab).then(() => {
+-      info("web console closed");
+-      HUDService.toggleBrowserConsole().then(onBrowserConsoleOpen);
+-    });
+-  }
++  const onBrowserConsolePrivateLogMessage = waitForMessage(hud, PRIVATE_MESSAGE);
++  const onBrowserConsolePrivateErrorMessage =
++    waitForMessage(hud, PRIVATE_EXCEPTION, ".error");
++  logPrivateMessages(privateBrowser.selectedBrowser);
+ 
+-  // Make sure that the cached messages from private tabs are not displayed in
+-  // the browser console.
+-  function checkNoPrivateMessages() {
+-    let text = hud.outputNode.textContent;
+-    is(text.indexOf("fooBazBaz"), -1, "no exception displayed");
+-    is(text.indexOf("bug 874061"), -1, "no console message displayed");
+-  }
++  await onBrowserConsolePrivateLogMessage;
++  await onBrowserConsolePrivateErrorMessage;
++  ok(true, "Messages are displayed as expected");
+ 
+-  function onBrowserConsoleOpen(injectedHud) {
+-    hud = injectedHud;
+-    ok(hud, "browser console opened");
+-
+-    checkNoPrivateMessages();
+-    addMessages();
+-    expectedMessages.push(nonPrivateMessage);
++  info("close the private window and check if private messages are removed");
++  const onPrivateMessagesCleared = hud.jsterm.once("private-messages-cleared");
++  privateWindow.BrowserTryToCloseWindow();
++  await onPrivateMessagesCleared;
+ 
+-    // Make sure that live messages are displayed in the browser console, even
+-    // from private tabs.
+-    waitForMessages({
+-      webconsole: hud,
+-      messages: expectedMessages,
+-    }).then(testPrivateWindowClose);
+-  }
++  ok(findMessage(hud, NON_PRIVATE_MESSAGE),
++    "non-private messages are still shown after private window closed");
++  assertNoPrivateMessages(hud);
+ 
+-  function testPrivateWindowClose() {
+-    info("close the private window and check if private messages are removed");
+-    hud.jsterm.once("private-messages-cleared", () => {
+-      isnot(hud.outputNode.textContent.indexOf("bug874061-not-private"), -1,
+-            "non-private messages are still shown after private window closed");
+-      checkNoPrivateMessages();
++  info("close the browser console");
++  await HUDService.toggleBrowserConsole();
+ 
+-      info("close the browser console");
+-      HUDService.toggleBrowserConsole().then(() => {
+-        info("reopen the browser console");
+-        executeSoon(() =>
+-          HUDService.toggleBrowserConsole().then(onBrowserConsoleReopen));
+-      });
+-    });
+-    privateWindow.BrowserTryToCloseWindow();
+-  }
++  info("reopen the browser console");
++  hud = await HUDService.toggleBrowserConsole();
++  ok(hud, "browser console reopened");
++
++  assertNoPrivateMessages(hud);
++});
+ 
+-  function onBrowserConsoleReopen(injectedHud) {
+-    hud = injectedHud;
+-    ok(hud, "browser console reopened");
++function logPrivateMessages(browser) {
++  ContentTask.spawn(browser, null, () => content.wrappedJSObject.logMessages());
++}
+ 
+-    // Make sure that the non-private message is still shown after reopen.
+-    waitForMessages({
+-      webconsole: hud,
+-      messages: [nonPrivateMessage],
+-    }).then(() => {
+-      // Make sure that no private message is displayed after closing the
+-      // private window and reopening the Browser Console.
+-      checkNoPrivateMessages();
+-      executeSoon(finishTest);
+-    });
+-  }
++function assertNoPrivateMessages(hud) {
++  is(findMessage(hud, PRIVATE_MESSAGE), null, "no console message displayed");
++  is(findMessage(hud, PRIVATE_EXCEPTION), null, "no exception displayed");
+ }
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/head.js b/devtools/client/webconsole/new-console-output/test/mochitest/head.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/head.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/head.js
+@@ -522,21 +522,22 @@ function simulateLinkClick(element, clic
+   });
+   return Promise.race([onOpenLink, onTimeout]);
+ }
+ 
+ /**
+  * Open a new browser window and return a promise that resolves when the new window has
+  * fired the "browser-delayed-startup-finished" event.
+  *
++ * @param {Object} options: An options object that will be passed to OpenBrowserWindow.
+  * @returns Promise
+  *          A Promise that resolves when the window is ready.
+  */
+-function openNewBrowserWindow() {
+-  let win = OpenBrowserWindow();
++function openNewBrowserWindow(options) {
++  let win = OpenBrowserWindow(options);
+   return new Promise(resolve => {
+     Services.obs.addObserver(function observer(subject, topic) {
+       if (win == subject) {
+         Services.obs.removeObserver(observer, topic);
+         resolve(win);
+       }
+     }, "browser-delayed-startup-finished");
+   });

+ 4 - 4
frg/work-js/mozilla-release/patches/1408790-58a1.patch

@@ -2,7 +2,7 @@
 # User Liam <canada8715@gmail.com>
 # User Liam <canada8715@gmail.com>
 # Date 1508111112 21600
 # Date 1508111112 21600
 # Node ID ac9d12e5587fe2486a2a8c95ef911768841195e2
 # Node ID ac9d12e5587fe2486a2a8c95ef911768841195e2
-# Parent  75829119496da93cbe620679c4f10f3aa8e4606b
+# Parent  26bb2873d6762fc72b18199c29d8fc9f3387b5cf
 Bug 1408790 - The non-selected elements with ids should be lightened to the same color as the non-selected classes r=gl
 Bug 1408790 - The non-selected elements with ids should be lightened to the same color as the non-selected classes r=gl
 
 
 MozReview-Commit-ID: AjcnYsxxqAs
 MozReview-Commit-ID: AjcnYsxxqAs
@@ -32,7 +32,7 @@ diff --git a/devtools/client/themes/breadcrumbs.css b/devtools/client/themes/bre
    height: 24px; /* Set height to prevent starting small waiting for content */
    height: 24px; /* Set height to prevent starting small waiting for content */
  }
  }
  
  
-@@ -227,22 +235,19 @@
+@@ -150,22 +158,19 @@
  .theme-dark .breadcrumbs-widget-item {
  .theme-dark .breadcrumbs-widget-item {
    color: var(--theme-selection-color);
    color: var(--theme-selection-color);
  }
  }
@@ -55,5 +55,5 @@ diff --git a/devtools/client/themes/breadcrumbs.css b/devtools/client/themes/bre
    color: var(--theme-highlight-lightorange);
    color: var(--theme-highlight-lightorange);
  }
  }
  
  
- .theme-dark .breadcrumbs-widget-item:not([checked]):hover label {
-   color: white;
+ /* Firebug theme support for breadcrumbs widget. */
+ 

+ 1383 - 0
frg/work-js/mozilla-release/patches/1420499-59a1.patch

@@ -0,0 +1,1383 @@
+# HG changeset patch
+# User Gabriel Luong <gabriel.luong@gmail.com>
+# Date 1512714605 18000
+# Node ID 23d7b2413b2bb23128bee1623be3ca27c4a8e0f5
+# Parent  09cb9581af78a35fda09385dbf7272c3d732d453
+Bug 1420499 - Update Codemirror to 5.32.0. r=bgrins
+
+diff --git a/devtools/client/sourceeditor/codemirror/README b/devtools/client/sourceeditor/codemirror/README
+--- a/devtools/client/sourceeditor/codemirror/README
++++ b/devtools/client/sourceeditor/codemirror/README
+@@ -1,16 +1,16 @@
+ This is the CodeMirror editor packaged for the Mozilla Project. CodeMirror
+ is a JavaScript component that provides a code editor in the browser. When
+ a mode is available for the language you are coding in, it will color your
+ code, and optionally help with indentation.
+ 
+ # Upgrade
+ 
+-Currently used version is 5.31.0. To upgrade: download a new version of
++Currently used version is 5.32.0. To upgrade: download a new version of
+ CodeMirror from the project's page [1] and replace all JavaScript and
+ CSS files inside the codemirror directory [2].
+ 
+ Then to recreate codemirror.bundle.js:
+  > cd devtools/client/sourceeditor
+  > npm install
+  > webpack
+ 
+diff --git a/devtools/client/sourceeditor/codemirror/addon/comment/comment.js b/devtools/client/sourceeditor/codemirror/addon/comment/comment.js
+--- a/devtools/client/sourceeditor/codemirror/addon/comment/comment.js
++++ b/devtools/client/sourceeditor/codemirror/addon/comment/comment.js
+@@ -167,20 +167,16 @@
+     var startString = options.blockCommentStart || mode.blockCommentStart;
+     var endString = options.blockCommentEnd || mode.blockCommentEnd;
+     if (!startString || !endString) return false;
+     var lead = options.blockCommentLead || mode.blockCommentLead;
+     var startLine = self.getLine(start), open = startLine.indexOf(startString)
+     if (open == -1) return false
+     var endLine = end == start ? startLine : self.getLine(end)
+     var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);
+-    if (close == -1 && start != end) {
+-      endLine = self.getLine(--end);
+-      close = endLine.indexOf(endString);
+-    }
+     var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)
+     if (close == -1 ||
+         !/comment/.test(self.getTokenTypeAt(insideStart)) ||
+         !/comment/.test(self.getTokenTypeAt(insideEnd)) ||
+         self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1)
+       return false;
+ 
+     // Avoid killing block comments completely outside the selection.
+diff --git a/devtools/client/sourceeditor/codemirror/addon/edit/closebrackets.js b/devtools/client/sourceeditor/codemirror/addon/edit/closebrackets.js
+--- a/devtools/client/sourceeditor/codemirror/addon/edit/closebrackets.js
++++ b/devtools/client/sourceeditor/codemirror/addon/edit/closebrackets.js
+@@ -128,17 +128,18 @@
+           curType = "skipThree";
+         else
+           curType = "skip";
+       } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
+                  cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch &&
+                  (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) {
+         curType = "addFour";
+       } else if (identical) {
+-        if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, ch)) curType = "both";
++        var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
++        if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
+         else return CodeMirror.Pass;
+       } else if (opening && (cm.getLine(cur.line).length == cur.ch ||
+                              isClosingBracket(next, pairs) ||
+                              /\s/.test(next))) {
+         curType = "both";
+       } else {
+         return CodeMirror.Pass;
+       }
+@@ -180,29 +181,14 @@
+   }
+ 
+   function charsAround(cm, pos) {
+     var str = cm.getRange(Pos(pos.line, pos.ch - 1),
+                           Pos(pos.line, pos.ch + 1));
+     return str.length == 2 ? str : null;
+   }
+ 
+-  // Project the token type that will exists after the given char is
+-  // typed, and use it to determine whether it would cause the start
+-  // of a string token.
+-  function enteringString(cm, pos, ch) {
+-    var line = cm.getLine(pos.line);
+-    var token = cm.getTokenAt(pos);
+-    if (/\bstring2?\b/.test(token.type) || stringStartsAfter(cm, pos)) return false;
+-    var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4);
+-    stream.pos = stream.start = token.start;
+-    for (;;) {
+-      var type1 = cm.getMode().token(stream, token.state);
+-      if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1);
+-      stream.start = stream.pos;
+-    }
+-  }
+-
+   function stringStartsAfter(cm, pos) {
+     var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
+-    return /\bstring/.test(token.type) && token.start == pos.ch
++    return /\bstring/.test(token.type) && token.start == pos.ch &&
++      (pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
+   }
+ });
+diff --git a/devtools/client/sourceeditor/codemirror/addon/edit/continuelist.js b/devtools/client/sourceeditor/codemirror/addon/edit/continuelist.js
+--- a/devtools/client/sourceeditor/codemirror/addon/edit/continuelist.js
++++ b/devtools/client/sourceeditor/codemirror/addon/edit/continuelist.js
+@@ -39,14 +39,53 @@
+         replacements[i] = "\n";
+       } else {
+         var indent = match[1], after = match[5];
+         var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
+           ? match[2].replace("x", " ")
+           : (parseInt(match[3], 10) + 1) + match[4];
+ 
+         replacements[i] = "\n" + indent + bullet + after;
++
++        incrementRemainingMarkdownListNumbers(cm, pos);
+       }
+     }
+ 
+     cm.replaceSelections(replacements);
+   };
++
++  // Auto-updating Markdown list numbers when a new item is added to the
++  // middle of a list
++  function incrementRemainingMarkdownListNumbers(cm, pos) {
++    var startLine = pos.line, lookAhead = 0, skipCount = 0;
++    var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1];
++
++    do {
++      lookAhead += 1;
++      var nextLineNumber = startLine + lookAhead;
++      var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine);
++
++      if (nextItem) {
++        var nextIndent = nextItem[1];
++        var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount);
++        var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber;
++
++        if (startIndent === nextIndent) {
++          if (newNumber === nextNumber) itemNumber = nextNumber + 1;
++          if (newNumber > nextNumber) itemNumber = newNumber + 1;
++          cm.replaceRange(
++            nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]),
++          {
++            line: nextLineNumber, ch: 0
++          }, {
++            line: nextLineNumber, ch: nextLine.length
++          });
++        } else {
++          if (startIndent.length > nextIndent.length) return;
++          // This doesn't run if the next line immediatley indents, as it is
++          // not clear of the users intention (new indented item or same level)
++          if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return;
++          skipCount += 1;
++        }
++      }
++    } while (nextItem);
++  }
+ });
+diff --git a/devtools/client/sourceeditor/codemirror/addon/hint/show-hint.js b/devtools/client/sourceeditor/codemirror/addon/hint/show-hint.js
+--- a/devtools/client/sourceeditor/codemirror/addon/hint/show-hint.js
++++ b/devtools/client/sourceeditor/codemirror/addon/hint/show-hint.js
+@@ -116,35 +116,29 @@
+     },
+ 
+     finishUpdate: function(data, first) {
+       if (this.data) CodeMirror.signal(this.data, "update");
+ 
+       var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
+       if (this.widget) this.widget.close();
+ 
+-      if (data && this.data && isNewCompletion(this.data, data)) return;
+       this.data = data;
+ 
+       if (data && data.list.length) {
+         if (picked && data.list.length == 1) {
+           this.pick(data, 0);
+         } else {
+           this.widget = new Widget(this, data);
+           CodeMirror.signal(data, "shown");
+         }
+       }
+     }
+   };
+ 
+-  function isNewCompletion(old, nw) {
+-    var moved = CodeMirror.cmpPos(nw.from, old.from)
+-    return moved > 0 && old.to.ch - old.from.ch != nw.to.ch - nw.from.ch
+-  }
+-
+   function parseOptions(cm, pos, options) {
+     var editor = cm.options.hintOptions;
+     var out = {};
+     for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
+     if (editor) for (var prop in editor)
+       if (editor[prop] !== undefined) out[prop] = editor[prop];
+     if (options) for (var prop in options)
+       if (options[prop] !== undefined) out[prop] = options[prop];
+diff --git a/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js b/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js
+--- a/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js
++++ b/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js
+@@ -154,17 +154,17 @@
+         return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),
+                 to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}
+       } else {
+         var cutFrom = string.length - lines[0].length
+         if (string.slice(cutFrom) != lines[0]) continue search
+         for (var i = 1; i < lines.length - 1; i++)
+           if (fold(doc.getLine(line + i)) != lines[i]) continue search
+         var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]
+-        if (end.slice(0, lastLine.length) != lastLine) continue search
++        if (endString.slice(0, lastLine.length) != lastLine) continue search
+         return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
+                 to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}
+       }
+     }
+   }
+ 
+   function searchStringBackward(doc, query, start, caseFold) {
+     if (!query.length) return null
+diff --git a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+--- a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
++++ b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+@@ -3511,18 +3511,20 @@ var CodeMirror =
+ 	        { updateWidgetHeight(cur.rest[j]) } }
+ 	    }
+ 	  }
+ 	}
+ 
+ 	// Read and store the height of line widgets associated with the
+ 	// given line.
+ 	function updateWidgetHeight(line) {
+-	  if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i)
+-	    { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } }
++	  if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
++	    var w = line.widgets[i], parent = w.node.parentNode
++	    if (parent) { w.height = parent.offsetHeight }
++	  } }
+ 	}
+ 
+ 	// Compute the lines that are visible in a given viewport (defaults
+ 	// the the current scroll position). viewport may contain top,
+ 	// height, and ensure (see op.scrollToPos) properties.
+ 	function visibleLines(display, doc, viewport) {
+ 	  var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop
+ 	  top = Math.floor(top - paddingTop(display))
+@@ -7298,44 +7300,48 @@ var CodeMirror =
+ 	  return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
+ 	    || lookupKey(name, cm.options.keyMap, handle, cm)
+ 	}
+ 
+ 	// Note that, despite the name, this function is also used to check
+ 	// for bound mouse clicks.
+ 
+ 	var stopSeq = new Delayed
++
+ 	function dispatchKey(cm, name, e, handle) {
+ 	  var seq = cm.state.keySeq
+ 	  if (seq) {
+ 	    if (isModifierKey(name)) { return "handled" }
+-	    stopSeq.set(50, function () {
+-	      if (cm.state.keySeq == seq) {
+-	        cm.state.keySeq = null
+-	        cm.display.input.reset()
+-	      }
+-	    })
+-	    name = seq + " " + name
+-	  }
++	    if (/\'$/.test(name))
++	      { cm.state.keySeq = null }
++	    else
++	      { stopSeq.set(50, function () {
++	        if (cm.state.keySeq == seq) {
++	          cm.state.keySeq = null
++	          cm.display.input.reset()
++	        }
++	      }) }
++	    if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
++	  }
++	  return dispatchKeyInner(cm, name, e, handle)
++	}
++
++	function dispatchKeyInner(cm, name, e, handle) {
+ 	  var result = lookupKeyForEditor(cm, name, handle)
+ 
+ 	  if (result == "multi")
+ 	    { cm.state.keySeq = name }
+ 	  if (result == "handled")
+ 	    { signalLater(cm, "keyHandled", cm, name, e) }
+ 
+ 	  if (result == "handled" || result == "multi") {
+ 	    e_preventDefault(e)
+ 	    restartBlink(cm)
+ 	  }
+ 
+-	  if (seq && !result && /\'$/.test(name)) {
+-	    e_preventDefault(e)
+-	    return true
+-	  }
+ 	  return !!result
+ 	}
+ 
+ 	// Handle a key from the keydown event.
+ 	function handleKeyBinding(cm, e) {
+ 	  var name = keyName(e, true)
+ 	  if (!name) { return false }
+ 
+@@ -9877,17 +9883,17 @@ var CodeMirror =
+ 	CodeMirror.defineDocExtension = function (name, func) {
+ 	  Doc.prototype[name] = func
+ 	}
+ 
+ 	CodeMirror.fromTextArea = fromTextArea
+ 
+ 	addLegacyProps(CodeMirror)
+ 
+-	CodeMirror.version = "5.31.0"
++	CodeMirror.version = "5.32.0"
+ 
+ 	return CodeMirror;
+ 
+ 	})));
+ 
+ /***/ }),
+ /* 3 */
+ /***/ (function(module, exports, __webpack_require__) {
+@@ -10048,17 +10054,17 @@ var CodeMirror =
+ 	        return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),
+ 	                to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}
+ 	      } else {
+ 	        var cutFrom = string.length - lines[0].length
+ 	        if (string.slice(cutFrom) != lines[0]) continue search
+ 	        for (var i = 1; i < lines.length - 1; i++)
+ 	          if (fold(doc.getLine(line + i)) != lines[i]) continue search
+ 	        var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]
+-	        if (end.slice(0, lastLine.length) != lastLine) continue search
++	        if (endString.slice(0, lastLine.length) != lastLine) continue search
+ 	        return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
+ 	                to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}
+ 	      }
+ 	    }
+ 	  }
+ 
+ 	  function searchStringBackward(doc, query, start, caseFold) {
+ 	    if (!query.length) return null
+@@ -10736,17 +10742,18 @@ var CodeMirror =
+ 	          curType = "skipThree";
+ 	        else
+ 	          curType = "skip";
+ 	      } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
+ 	                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch &&
+ 	                 (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) {
+ 	        curType = "addFour";
+ 	      } else if (identical) {
+-	        if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, ch)) curType = "both";
++	        var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
++	        if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
+ 	        else return CodeMirror.Pass;
+ 	      } else if (opening && (cm.getLine(cur.line).length == cur.ch ||
+ 	                             isClosingBracket(next, pairs) ||
+ 	                             /\s/.test(next))) {
+ 	        curType = "both";
+ 	      } else {
+ 	        return CodeMirror.Pass;
+ 	      }
+@@ -10788,35 +10795,20 @@ var CodeMirror =
+ 	  }
+ 
+ 	  function charsAround(cm, pos) {
+ 	    var str = cm.getRange(Pos(pos.line, pos.ch - 1),
+ 	                          Pos(pos.line, pos.ch + 1));
+ 	    return str.length == 2 ? str : null;
+ 	  }
+ 
+-	  // Project the token type that will exists after the given char is
+-	  // typed, and use it to determine whether it would cause the start
+-	  // of a string token.
+-	  function enteringString(cm, pos, ch) {
+-	    var line = cm.getLine(pos.line);
+-	    var token = cm.getTokenAt(pos);
+-	    if (/\bstring2?\b/.test(token.type) || stringStartsAfter(cm, pos)) return false;
+-	    var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4);
+-	    stream.pos = stream.start = token.start;
+-	    for (;;) {
+-	      var type1 = cm.getMode().token(stream, token.state);
+-	      if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1);
+-	      stream.start = stream.pos;
+-	    }
+-	  }
+-
+ 	  function stringStartsAfter(cm, pos) {
+ 	    var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
+-	    return /\bstring/.test(token.type) && token.start == pos.ch
++	    return /\bstring/.test(token.type) && token.start == pos.ch &&
++	      (pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
+ 	  }
+ 	});
+ 
+ 
+ /***/ }),
+ /* 7 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+@@ -10989,20 +10981,16 @@ var CodeMirror =
+ 	    var startString = options.blockCommentStart || mode.blockCommentStart;
+ 	    var endString = options.blockCommentEnd || mode.blockCommentEnd;
+ 	    if (!startString || !endString) return false;
+ 	    var lead = options.blockCommentLead || mode.blockCommentLead;
+ 	    var startLine = self.getLine(start), open = startLine.indexOf(startString)
+ 	    if (open == -1) return false
+ 	    var endLine = end == start ? startLine : self.getLine(end)
+ 	    var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);
+-	    if (close == -1 && start != end) {
+-	      endLine = self.getLine(--end);
+-	      close = endLine.indexOf(endString);
+-	    }
+ 	    var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)
+ 	    if (close == -1 ||
+ 	        !/comment/.test(self.getTokenTypeAt(insideStart)) ||
+ 	        !/comment/.test(self.getTokenTypeAt(insideEnd)) ||
+ 	        self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1)
+ 	      return false;
+ 
+ 	    // Avoid killing block comments completely outside the selection.
+@@ -11083,18 +11071,16 @@ var CodeMirror =
+ 	    // Extend the 'normal' keywords with the TypeScript language extensions
+ 	    if (isTS) {
+ 	      var type = {type: "variable", style: "type"};
+ 	      var tsKeywords = {
+ 	        // object-like things
+ 	        "interface": kw("class"),
+ 	        "implements": C,
+ 	        "namespace": C,
+-	        "module": kw("module"),
+-	        "enum": kw("module"),
+ 
+ 	        // scope modifiers
+ 	        "public": kw("modifier"),
+ 	        "private": kw("modifier"),
+ 	        "protected": kw("modifier"),
+ 	        "abstract": kw("modifier"),
+ 	        "readonly": kw("modifier"),
+ 
+@@ -11191,17 +11177,17 @@ var CodeMirror =
+ 	    } else if (wordRE.test(ch)) {
+ 	      stream.eatWhile(wordRE);
+ 	      var word = stream.current()
+ 	      if (state.lastType != ".") {
+ 	        if (keywords.propertyIsEnumerable(word)) {
+ 	          var kw = keywords[word]
+ 	          return ret(kw.type, kw.style, word)
+ 	        }
+-	        if (word == "async" && stream.match(/^\s*[\(\w]/, false))
++	        if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\(\w]/, false))
+ 	          return ret("async", "keyword", word)
+ 	      }
+ 	      return ret("variable", "variable", word)
+ 	    }
+ 	  }
+ 
+ 	  function tokenString(quote) {
+ 	    return function(stream, state) {
+@@ -11408,33 +11394,35 @@ var CodeMirror =
+ 	      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
+ 	    }
+ 	    if (type == "function") return cont(functiondef);
+ 	    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
+ 	    if (type == "variable") {
+ 	      if (isTS && value == "type") {
+ 	        cx.marked = "keyword"
+ 	        return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
+-	      } if (isTS && value == "declare") {
++	      } else if (isTS && value == "declare") {
+ 	        cx.marked = "keyword"
+ 	        return cont(statement)
++	      } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) {
++	        cx.marked = "keyword"
++	        return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+ 	      } else {
+ 	        return cont(pushlex("stat"), maybelabel);
+ 	      }
+ 	    }
+ 	    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
+ 	                                      block, poplex, poplex);
+ 	    if (type == "case") return cont(expression, expect(":"));
+ 	    if (type == "default") return cont(expect(":"));
+ 	    if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
+ 	                                     statement, poplex, popcontext);
+ 	    if (type == "class") return cont(pushlex("form"), className, poplex);
+ 	    if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
+ 	    if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
+-	    if (type == "module") return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+ 	    if (type == "async") return cont(statement)
+ 	    if (value == "@") return cont(expression, statement)
+ 	    return pass(pushlex("stat"), expression, expect(";"), poplex);
+ 	  }
+ 	  function expression(type) {
+ 	    return expressionInner(type, false);
+ 	  }
+ 	  function expressionNoComma(type) {
+@@ -11474,16 +11462,18 @@ var CodeMirror =
+ 	    return maybeoperatorNoComma(type, value, false);
+ 	  }
+ 	  function maybeoperatorNoComma(type, value, noComma) {
+ 	    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
+ 	    var expr = noComma == false ? expression : expressionNoComma;
+ 	    if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
+ 	    if (type == "operator") {
+ 	      if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
++	      if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
++	        return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
+ 	      if (value == "?") return cont(expression, expect(":"), expr);
+ 	      return cont(expr);
+ 	    }
+ 	    if (type == "quasi") { return pass(quasi, me); }
+ 	    if (type == ";") return;
+ 	    if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
+ 	    if (type == ".") return cont(property, me);
+ 	    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
+@@ -11600,16 +11590,28 @@ var CodeMirror =
+ 	    return pass(statement, block);
+ 	  }
+ 	  function maybetype(type, value) {
+ 	    if (isTS) {
+ 	      if (type == ":") return cont(typeexpr);
+ 	      if (value == "?") return cont(maybetype);
+ 	    }
+ 	  }
++	  function mayberettype(type) {
++	    if (isTS && type == ":") {
++	      if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
++	      else return cont(typeexpr)
++	    }
++	  }
++	  function isKW(_, value) {
++	    if (value == "is") {
++	      cx.marked = "keyword"
++	      return cont()
++	    }
++	  }
+ 	  function typeexpr(type, value) {
+ 	    if (type == "variable" || value == "void") {
+ 	      if (value == "keyof") {
+ 	        cx.marked = "keyword"
+ 	        return cont(typeexpr)
+ 	      } else {
+ 	        cx.marked = "type"
+ 	        return cont(afterType)
+@@ -11643,16 +11645,22 @@ var CodeMirror =
+ 	    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+ 	    if (value == "|" || type == ".") return cont(typeexpr)
+ 	    if (type == "[") return cont(expect("]"), afterType)
+ 	    if (value == "extends") return cont(typeexpr)
+ 	  }
+ 	  function maybeTypeArgs(_, value) {
+ 	    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+ 	  }
++	  function typeparam() {
++	    return pass(typeexpr, maybeTypeDefault)
++	  }
++	  function maybeTypeDefault(_, value) {
++	    if (value == "=") return cont(typeexpr)
++	  }
+ 	  function vardef() {
+ 	    return pass(pattern, maybetype, maybeAssign, vardefCont);
+ 	  }
+ 	  function pattern(type, value) {
+ 	    if (type == "modifier") return cont(pattern)
+ 	    if (type == "variable") { register(value); return cont(); }
+ 	    if (type == "spread") return cont(pattern);
+ 	    if (type == "[") return contCommasep(pattern, "]");
+@@ -11696,34 +11704,34 @@ var CodeMirror =
+ 	    return pass(expression, expect(";"), forspec3);
+ 	  }
+ 	  function forspec3(type) {
+ 	    if (type != ")") cont(expression);
+ 	  }
+ 	  function functiondef(type, value) {
+ 	    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
+ 	    if (type == "variable") {register(value); return cont(functiondef);}
+-	    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
+-	    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
++	    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
++	    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
+ 	  }
+ 	  function funarg(type, value) {
+ 	    if (value == "@") cont(expression, funarg)
+ 	    if (type == "spread" || type == "modifier") return cont(funarg);
+ 	    return pass(pattern, maybetype, maybeAssign);
+ 	  }
+ 	  function classExpression(type, value) {
+ 	    // Class expressions may have an optional name.
+ 	    if (type == "variable") return className(type, value);
+ 	    return classNameAfter(type, value);
+ 	  }
+ 	  function className(type, value) {
+ 	    if (type == "variable") {register(value); return cont(classNameAfter);}
+ 	  }
+ 	  function classNameAfter(type, value) {
+-	    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, classNameAfter)
++	    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
+ 	    if (value == "extends" || value == "implements" || (isTS && type == ","))
+ 	      return cont(isTS ? typeexpr : expression, classNameAfter);
+ 	    if (type == "{") return cont(pushlex("}"), classBody, poplex);
+ 	  }
+ 	  function classBody(type, value) {
+ 	    if (type == "modifier" || type == "async" ||
+ 	        (type == "variable" &&
+ 	         (value == "static" || value == "get" || value == "set") &&
+@@ -14455,17 +14463,17 @@ var CodeMirror =
+ 	    keywords: words("abstract assert break case catch class const continue default " +
+ 	                    "do else enum extends final finally float for goto if implements import " +
+ 	                    "instanceof interface native new package private protected public " +
+ 	                    "return static strictfp super switch synchronized this throw throws transient " +
+ 	                    "try volatile while @interface"),
+ 	    types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
+ 	                 "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
+ 	    blockKeywords: words("catch class do else finally for if switch try while"),
+-	    defKeywords: words("class interface package enum @interface"),
++	    defKeywords: words("class interface enum @interface"),
+ 	    typeFirstDefinitions: true,
+ 	    atoms: words("true false null"),
+ 	    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+ 	    hooks: {
+ 	      "@": function(stream) {
+ 	        // Don't match the @interface keyword.
+ 	        if (stream.match('interface', false)) return false;
+ 
+@@ -15169,26 +15177,26 @@ var CodeMirror =
+ 	    if (!killRing.length) return addToRing(str);
+ 	    killRing[killRing.length - 1] += str;
+ 	  }
+ 	  function getFromRing(n) { return killRing[killRing.length - (n ? Math.min(n, 1) : 1)] || ""; }
+ 	  function popFromRing() { if (killRing.length > 1) killRing.pop(); return getFromRing(); }
+ 
+ 	  var lastKill = null;
+ 
+-	  function kill(cm, from, to, mayGrow, text) {
++	  function kill(cm, from, to, ring, text) {
+ 	    if (text == null) text = cm.getRange(from, to);
+ 
+-	    if (mayGrow && lastKill && lastKill.cm == cm && posEq(from, lastKill.pos) && cm.isClean(lastKill.gen))
++	    if (ring == "grow" && lastKill && lastKill.cm == cm && posEq(from, lastKill.pos) && cm.isClean(lastKill.gen))
+ 	      growRingTop(text);
+-	    else
++	    else if (ring !== false)
+ 	      addToRing(text);
+ 	    cm.replaceRange("", from, to, "+delete");
+ 
+-	    if (mayGrow) lastKill = {cm: cm, pos: from, gen: cm.changeGeneration()};
++	    if (ring == "grow") lastKill = {cm: cm, pos: from, gen: cm.changeGeneration()};
+ 	    else lastKill = null;
+ 	  }
+ 
+ 	  // Boundaries of various units
+ 
+ 	  function byChar(cm, pos, dir) {
+ 	    return cm.findPosH(pos, dir, "char", true);
+ 	  }
+@@ -15290,32 +15298,32 @@ var CodeMirror =
+ 	  function move(by, dir) {
+ 	    var f = function(cm) {
+ 	      cm.extendSelection(findEnd(cm, cm.getCursor(), by, dir));
+ 	    };
+ 	    f.motion = true;
+ 	    return f;
+ 	  }
+ 
+-	  function killTo(cm, by, dir) {
++	  function killTo(cm, by, dir, ring) {
+ 	    var selections = cm.listSelections(), cursor;
+ 	    var i = selections.length;
+ 	    while (i--) {
+ 	      cursor = selections[i].head;
+-	      kill(cm, cursor, findEnd(cm, cursor, by, dir), true);
+-	    }
+-	  }
+-
+-	  function killRegion(cm) {
++	      kill(cm, cursor, findEnd(cm, cursor, by, dir), ring);
++	    }
++	  }
++
++	  function killRegion(cm, ring) {
+ 	    if (cm.somethingSelected()) {
+ 	      var selections = cm.listSelections(), selection;
+ 	      var i = selections.length;
+ 	      while (i--) {
+ 	        selection = selections[i];
+-	        kill(cm, selection.anchor, selection.head);
++	        kill(cm, selection.anchor, selection.head, ring);
+ 	      }
+ 	      return true;
+ 	    }
+ 	  }
+ 
+ 	  function addPrefix(cm, digit) {
+ 	    if (cm.state.emacsPrefix) {
+ 	      if (digit != "-") cm.state.emacsPrefix += digit;
+@@ -15415,66 +15423,66 @@ var CodeMirror =
+ 	    clearMark(cm);
+ 	  }
+ 
+ 	  CodeMirror.emacs = {kill: kill, killRegion: killRegion, repeated: repeated};
+ 
+ 	  // Actual keymap
+ 
+ 	  var keyMap = CodeMirror.keyMap.emacs = CodeMirror.normalizeKeyMap({
+-	    "Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"));},
++	    "Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"), true);},
+ 	    "Ctrl-K": repeated(function(cm) {
+ 	      var start = cm.getCursor(), end = cm.clipPos(Pos(start.line));
+ 	      var text = cm.getRange(start, end);
+ 	      if (!/\S/.test(text)) {
+ 	        text += "\n";
+ 	        end = Pos(start.line + 1, 0);
+ 	      }
+-	      kill(cm, start, end, true, text);
++	      kill(cm, start, end, "grow", text);
+ 	    }),
+ 	    "Alt-W": function(cm) {
+ 	      addToRing(cm.getSelection());
+ 	      clearMark(cm);
+ 	    },
+ 	    "Ctrl-Y": function(cm) {
+ 	      var start = cm.getCursor();
+ 	      cm.replaceRange(getFromRing(getPrefix(cm)), start, start, "paste");
+ 	      cm.setSelection(start, cm.getCursor());
+ 	    },
+ 	    "Alt-Y": function(cm) {cm.replaceSelection(popFromRing(), "around", "paste");},
+ 
+ 	    "Ctrl-Space": setMark, "Ctrl-Shift-2": setMark,
+ 
+ 	    "Ctrl-F": move(byChar, 1), "Ctrl-B": move(byChar, -1),
+ 	    "Right": move(byChar, 1), "Left": move(byChar, -1),
+-	    "Ctrl-D": function(cm) { killTo(cm, byChar, 1); },
+-	    "Delete": function(cm) { killRegion(cm) || killTo(cm, byChar, 1); },
+-	    "Ctrl-H": function(cm) { killTo(cm, byChar, -1); },
+-	    "Backspace": function(cm) { killRegion(cm) || killTo(cm, byChar, -1); },
++	    "Ctrl-D": function(cm) { killTo(cm, byChar, 1, false); },
++	    "Delete": function(cm) { killRegion(cm, false) || killTo(cm, byChar, 1, false); },
++	    "Ctrl-H": function(cm) { killTo(cm, byChar, -1, false); },
++	    "Backspace": function(cm) { killRegion(cm, false) || killTo(cm, byChar, -1, false); },
+ 
+ 	    "Alt-F": move(byWord, 1), "Alt-B": move(byWord, -1),
+-	    "Alt-D": function(cm) { killTo(cm, byWord, 1); },
+-	    "Alt-Backspace": function(cm) { killTo(cm, byWord, -1); },
++	    "Alt-D": function(cm) { killTo(cm, byWord, 1, "grow"); },
++	    "Alt-Backspace": function(cm) { killTo(cm, byWord, -1, "grow"); },
+ 
+ 	    "Ctrl-N": move(byLine, 1), "Ctrl-P": move(byLine, -1),
+ 	    "Down": move(byLine, 1), "Up": move(byLine, -1),
+ 	    "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
+ 	    "End": "goLineEnd", "Home": "goLineStart",
+ 
+ 	    "Alt-V": move(byPage, -1), "Ctrl-V": move(byPage, 1),
+ 	    "PageUp": move(byPage, -1), "PageDown": move(byPage, 1),
+ 
+ 	    "Ctrl-Up": move(byParagraph, -1), "Ctrl-Down": move(byParagraph, 1),
+ 
+ 	    "Alt-A": move(bySentence, -1), "Alt-E": move(bySentence, 1),
+-	    "Alt-K": function(cm) { killTo(cm, bySentence, 1); },
+-
+-	    "Ctrl-Alt-K": function(cm) { killTo(cm, byExpr, 1); },
+-	    "Ctrl-Alt-Backspace": function(cm) { killTo(cm, byExpr, -1); },
+-	    "Ctrl-Alt-F": move(byExpr, 1), "Ctrl-Alt-B": move(byExpr, -1),
++	    "Alt-K": function(cm) { killTo(cm, bySentence, 1, "grow"); },
++
++	    "Ctrl-Alt-K": function(cm) { killTo(cm, byExpr, 1, "grow"); },
++	    "Ctrl-Alt-Backspace": function(cm) { killTo(cm, byExpr, -1, "grow"); },
++	    "Ctrl-Alt-F": move(byExpr, 1), "Ctrl-Alt-B": move(byExpr, -1, "grow"),
+ 
+ 	    "Shift-Ctrl-Alt-2": function(cm) {
+ 	      var cursor = cm.getCursor();
+ 	      cm.setSelection(findEnd(cm, cursor, byExpr, 1), cursor);
+ 	    },
+ 	    "Ctrl-Alt-T": function(cm) {
+ 	      var leftStart = byExpr(cm, cm.getCursor(), -1), leftEnd = byExpr(cm, leftStart, 1);
+ 	      var rightEnd = byExpr(cm, leftEnd, 1), rightStart = byExpr(cm, rightEnd, -1);
+@@ -15537,17 +15545,17 @@ var CodeMirror =
+ 	      cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
+ 	    },
+ 	    "Ctrl-X Ctrl-S": "save",
+ 	    "Ctrl-X Ctrl-W": "save",
+ 	    "Ctrl-X S": "saveAll",
+ 	    "Ctrl-X F": "open",
+ 	    "Ctrl-X U": repeated("undo"),
+ 	    "Ctrl-X K": "close",
+-	    "Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); },
++	    "Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), "grow"); },
+ 	    "Ctrl-X H": "selectAll",
+ 
+ 	    "Ctrl-Q Tab": repeated("insertTab"),
+ 	    "Ctrl-U": addPrefixMap
+ 	  });
+ 
+ 	  var prefixMap = {"Ctrl-G": clearPrefix};
+ 	  function regPrefix(d) {
+diff --git a/devtools/client/sourceeditor/codemirror/keymap/emacs.js b/devtools/client/sourceeditor/codemirror/keymap/emacs.js
+--- a/devtools/client/sourceeditor/codemirror/keymap/emacs.js
++++ b/devtools/client/sourceeditor/codemirror/keymap/emacs.js
+@@ -25,26 +25,26 @@
+     if (!killRing.length) return addToRing(str);
+     killRing[killRing.length - 1] += str;
+   }
+   function getFromRing(n) { return killRing[killRing.length - (n ? Math.min(n, 1) : 1)] || ""; }
+   function popFromRing() { if (killRing.length > 1) killRing.pop(); return getFromRing(); }
+ 
+   var lastKill = null;
+ 
+-  function kill(cm, from, to, mayGrow, text) {
++  function kill(cm, from, to, ring, text) {
+     if (text == null) text = cm.getRange(from, to);
+ 
+-    if (mayGrow && lastKill && lastKill.cm == cm && posEq(from, lastKill.pos) && cm.isClean(lastKill.gen))
++    if (ring == "grow" && lastKill && lastKill.cm == cm && posEq(from, lastKill.pos) && cm.isClean(lastKill.gen))
+       growRingTop(text);
+-    else
++    else if (ring !== false)
+       addToRing(text);
+     cm.replaceRange("", from, to, "+delete");
+ 
+-    if (mayGrow) lastKill = {cm: cm, pos: from, gen: cm.changeGeneration()};
++    if (ring == "grow") lastKill = {cm: cm, pos: from, gen: cm.changeGeneration()};
+     else lastKill = null;
+   }
+ 
+   // Boundaries of various units
+ 
+   function byChar(cm, pos, dir) {
+     return cm.findPosH(pos, dir, "char", true);
+   }
+@@ -146,32 +146,32 @@
+   function move(by, dir) {
+     var f = function(cm) {
+       cm.extendSelection(findEnd(cm, cm.getCursor(), by, dir));
+     };
+     f.motion = true;
+     return f;
+   }
+ 
+-  function killTo(cm, by, dir) {
++  function killTo(cm, by, dir, ring) {
+     var selections = cm.listSelections(), cursor;
+     var i = selections.length;
+     while (i--) {
+       cursor = selections[i].head;
+-      kill(cm, cursor, findEnd(cm, cursor, by, dir), true);
++      kill(cm, cursor, findEnd(cm, cursor, by, dir), ring);
+     }
+   }
+ 
+-  function killRegion(cm) {
++  function killRegion(cm, ring) {
+     if (cm.somethingSelected()) {
+       var selections = cm.listSelections(), selection;
+       var i = selections.length;
+       while (i--) {
+         selection = selections[i];
+-        kill(cm, selection.anchor, selection.head);
++        kill(cm, selection.anchor, selection.head, ring);
+       }
+       return true;
+     }
+   }
+ 
+   function addPrefix(cm, digit) {
+     if (cm.state.emacsPrefix) {
+       if (digit != "-") cm.state.emacsPrefix += digit;
+@@ -271,66 +271,66 @@
+     clearMark(cm);
+   }
+ 
+   CodeMirror.emacs = {kill: kill, killRegion: killRegion, repeated: repeated};
+ 
+   // Actual keymap
+ 
+   var keyMap = CodeMirror.keyMap.emacs = CodeMirror.normalizeKeyMap({
+-    "Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"));},
++    "Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"), true);},
+     "Ctrl-K": repeated(function(cm) {
+       var start = cm.getCursor(), end = cm.clipPos(Pos(start.line));
+       var text = cm.getRange(start, end);
+       if (!/\S/.test(text)) {
+         text += "\n";
+         end = Pos(start.line + 1, 0);
+       }
+-      kill(cm, start, end, true, text);
++      kill(cm, start, end, "grow", text);
+     }),
+     "Alt-W": function(cm) {
+       addToRing(cm.getSelection());
+       clearMark(cm);
+     },
+     "Ctrl-Y": function(cm) {
+       var start = cm.getCursor();
+       cm.replaceRange(getFromRing(getPrefix(cm)), start, start, "paste");
+       cm.setSelection(start, cm.getCursor());
+     },
+     "Alt-Y": function(cm) {cm.replaceSelection(popFromRing(), "around", "paste");},
+ 
+     "Ctrl-Space": setMark, "Ctrl-Shift-2": setMark,
+ 
+     "Ctrl-F": move(byChar, 1), "Ctrl-B": move(byChar, -1),
+     "Right": move(byChar, 1), "Left": move(byChar, -1),
+-    "Ctrl-D": function(cm) { killTo(cm, byChar, 1); },
+-    "Delete": function(cm) { killRegion(cm) || killTo(cm, byChar, 1); },
+-    "Ctrl-H": function(cm) { killTo(cm, byChar, -1); },
+-    "Backspace": function(cm) { killRegion(cm) || killTo(cm, byChar, -1); },
++    "Ctrl-D": function(cm) { killTo(cm, byChar, 1, false); },
++    "Delete": function(cm) { killRegion(cm, false) || killTo(cm, byChar, 1, false); },
++    "Ctrl-H": function(cm) { killTo(cm, byChar, -1, false); },
++    "Backspace": function(cm) { killRegion(cm, false) || killTo(cm, byChar, -1, false); },
+ 
+     "Alt-F": move(byWord, 1), "Alt-B": move(byWord, -1),
+-    "Alt-D": function(cm) { killTo(cm, byWord, 1); },
+-    "Alt-Backspace": function(cm) { killTo(cm, byWord, -1); },
++    "Alt-D": function(cm) { killTo(cm, byWord, 1, "grow"); },
++    "Alt-Backspace": function(cm) { killTo(cm, byWord, -1, "grow"); },
+ 
+     "Ctrl-N": move(byLine, 1), "Ctrl-P": move(byLine, -1),
+     "Down": move(byLine, 1), "Up": move(byLine, -1),
+     "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
+     "End": "goLineEnd", "Home": "goLineStart",
+ 
+     "Alt-V": move(byPage, -1), "Ctrl-V": move(byPage, 1),
+     "PageUp": move(byPage, -1), "PageDown": move(byPage, 1),
+ 
+     "Ctrl-Up": move(byParagraph, -1), "Ctrl-Down": move(byParagraph, 1),
+ 
+     "Alt-A": move(bySentence, -1), "Alt-E": move(bySentence, 1),
+-    "Alt-K": function(cm) { killTo(cm, bySentence, 1); },
++    "Alt-K": function(cm) { killTo(cm, bySentence, 1, "grow"); },
+ 
+-    "Ctrl-Alt-K": function(cm) { killTo(cm, byExpr, 1); },
+-    "Ctrl-Alt-Backspace": function(cm) { killTo(cm, byExpr, -1); },
+-    "Ctrl-Alt-F": move(byExpr, 1), "Ctrl-Alt-B": move(byExpr, -1),
++    "Ctrl-Alt-K": function(cm) { killTo(cm, byExpr, 1, "grow"); },
++    "Ctrl-Alt-Backspace": function(cm) { killTo(cm, byExpr, -1, "grow"); },
++    "Ctrl-Alt-F": move(byExpr, 1), "Ctrl-Alt-B": move(byExpr, -1, "grow"),
+ 
+     "Shift-Ctrl-Alt-2": function(cm) {
+       var cursor = cm.getCursor();
+       cm.setSelection(findEnd(cm, cursor, byExpr, 1), cursor);
+     },
+     "Ctrl-Alt-T": function(cm) {
+       var leftStart = byExpr(cm, cm.getCursor(), -1), leftEnd = byExpr(cm, leftStart, 1);
+       var rightEnd = byExpr(cm, leftEnd, 1), rightStart = byExpr(cm, rightEnd, -1);
+@@ -393,17 +393,17 @@
+       cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
+     },
+     "Ctrl-X Ctrl-S": "save",
+     "Ctrl-X Ctrl-W": "save",
+     "Ctrl-X S": "saveAll",
+     "Ctrl-X F": "open",
+     "Ctrl-X U": repeated("undo"),
+     "Ctrl-X K": "close",
+-    "Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); },
++    "Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), "grow"); },
+     "Ctrl-X H": "selectAll",
+ 
+     "Ctrl-Q Tab": repeated("insertTab"),
+     "Ctrl-U": addPrefixMap
+   });
+ 
+   var prefixMap = {"Ctrl-G": clearPrefix};
+   function regPrefix(d) {
+diff --git a/devtools/client/sourceeditor/codemirror/lib/codemirror.css b/devtools/client/sourceeditor/codemirror/lib/codemirror.css
+--- a/devtools/client/sourceeditor/codemirror/lib/codemirror.css
++++ b/devtools/client/sourceeditor/codemirror/lib/codemirror.css
+@@ -140,18 +140,18 @@
+ 
+ .cm-s-default .cm-error {color: #f00;}
+ .cm-invalidchar {color: #f00;}
+ 
+ .CodeMirror-composing { border-bottom: 2px solid; }
+ 
+ /* Default styles for common addons */
+ 
+-div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+-div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
++div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
++div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
+ .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+ .CodeMirror-activeline-background {background: #e8f2ff;}
+ 
+ /* STOP */
+ 
+ /* The rest of this file contains styles related to the mechanics of
+    the editor. You probably shouldn't touch them. */
+ 
+diff --git a/devtools/client/sourceeditor/codemirror/lib/codemirror.js b/devtools/client/sourceeditor/codemirror/lib/codemirror.js
+--- a/devtools/client/sourceeditor/codemirror/lib/codemirror.js
++++ b/devtools/client/sourceeditor/codemirror/lib/codemirror.js
+@@ -3269,18 +3269,20 @@ function updateHeightsInViewport(cm) {
+         { updateWidgetHeight(cur.rest[j]) } }
+     }
+   }
+ }
+ 
+ // Read and store the height of line widgets associated with the
+ // given line.
+ function updateWidgetHeight(line) {
+-  if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i)
+-    { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } }
++  if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
++    var w = line.widgets[i], parent = w.node.parentNode
++    if (parent) { w.height = parent.offsetHeight }
++  } }
+ }
+ 
+ // Compute the lines that are visible in a given viewport (defaults
+ // the the current scroll position). viewport may contain top,
+ // height, and ensure (see op.scrollToPos) properties.
+ function visibleLines(display, doc, viewport) {
+   var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop
+   top = Math.floor(top - paddingTop(display))
+@@ -7056,44 +7058,48 @@ function lookupKeyForEditor(cm, name, ha
+   return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
+     || lookupKey(name, cm.options.keyMap, handle, cm)
+ }
+ 
+ // Note that, despite the name, this function is also used to check
+ // for bound mouse clicks.
+ 
+ var stopSeq = new Delayed
++
+ function dispatchKey(cm, name, e, handle) {
+   var seq = cm.state.keySeq
+   if (seq) {
+     if (isModifierKey(name)) { return "handled" }
+-    stopSeq.set(50, function () {
+-      if (cm.state.keySeq == seq) {
+-        cm.state.keySeq = null
+-        cm.display.input.reset()
+-      }
+-    })
+-    name = seq + " " + name
+-  }
++    if (/\'$/.test(name))
++      { cm.state.keySeq = null }
++    else
++      { stopSeq.set(50, function () {
++        if (cm.state.keySeq == seq) {
++          cm.state.keySeq = null
++          cm.display.input.reset()
++        }
++      }) }
++    if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
++  }
++  return dispatchKeyInner(cm, name, e, handle)
++}
++
++function dispatchKeyInner(cm, name, e, handle) {
+   var result = lookupKeyForEditor(cm, name, handle)
+ 
+   if (result == "multi")
+     { cm.state.keySeq = name }
+   if (result == "handled")
+     { signalLater(cm, "keyHandled", cm, name, e) }
+ 
+   if (result == "handled" || result == "multi") {
+     e_preventDefault(e)
+     restartBlink(cm)
+   }
+ 
+-  if (seq && !result && /\'$/.test(name)) {
+-    e_preventDefault(e)
+-    return true
+-  }
+   return !!result
+ }
+ 
+ // Handle a key from the keydown event.
+ function handleKeyBinding(cm, e) {
+   var name = keyName(e, true)
+   if (!name) { return false }
+ 
+@@ -9635,13 +9641,13 @@ CodeMirror.defineExtension = function (n
+ CodeMirror.defineDocExtension = function (name, func) {
+   Doc.prototype[name] = func
+ }
+ 
+ CodeMirror.fromTextArea = fromTextArea
+ 
+ addLegacyProps(CodeMirror)
+ 
+-CodeMirror.version = "5.31.0"
++CodeMirror.version = "5.32.0"
+ 
+ return CodeMirror;
+ 
+ })));
+\ No newline at end of file
+diff --git a/devtools/client/sourceeditor/codemirror/mode/clike/clike.js b/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
+--- a/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
++++ b/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
+@@ -427,17 +427,17 @@ CodeMirror.defineMode("clike", function(
+     keywords: words("abstract assert break case catch class const continue default " +
+                     "do else enum extends final finally float for goto if implements import " +
+                     "instanceof interface native new package private protected public " +
+                     "return static strictfp super switch synchronized this throw throws transient " +
+                     "try volatile while @interface"),
+     types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
+                  "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
+     blockKeywords: words("catch class do else finally for if switch try while"),
+-    defKeywords: words("class interface package enum @interface"),
++    defKeywords: words("class interface enum @interface"),
+     typeFirstDefinitions: true,
+     atoms: words("true false null"),
+     number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+     hooks: {
+       "@": function(stream) {
+         // Don't match the @interface keyword.
+         if (stream.match('interface', false)) return false;
+ 
+diff --git a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
+--- a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
++++ b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
+@@ -42,18 +42,16 @@ CodeMirror.defineMode("javascript", func
+     // Extend the 'normal' keywords with the TypeScript language extensions
+     if (isTS) {
+       var type = {type: "variable", style: "type"};
+       var tsKeywords = {
+         // object-like things
+         "interface": kw("class"),
+         "implements": C,
+         "namespace": C,
+-        "module": kw("module"),
+-        "enum": kw("module"),
+ 
+         // scope modifiers
+         "public": kw("modifier"),
+         "private": kw("modifier"),
+         "protected": kw("modifier"),
+         "abstract": kw("modifier"),
+         "readonly": kw("modifier"),
+ 
+@@ -150,17 +148,17 @@ CodeMirror.defineMode("javascript", func
+     } else if (wordRE.test(ch)) {
+       stream.eatWhile(wordRE);
+       var word = stream.current()
+       if (state.lastType != ".") {
+         if (keywords.propertyIsEnumerable(word)) {
+           var kw = keywords[word]
+           return ret(kw.type, kw.style, word)
+         }
+-        if (word == "async" && stream.match(/^\s*[\(\w]/, false))
++        if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\(\w]/, false))
+           return ret("async", "keyword", word)
+       }
+       return ret("variable", "variable", word)
+     }
+   }
+ 
+   function tokenString(quote) {
+     return function(stream, state) {
+@@ -367,33 +365,35 @@ CodeMirror.defineMode("javascript", func
+       return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
+     }
+     if (type == "function") return cont(functiondef);
+     if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
+     if (type == "variable") {
+       if (isTS && value == "type") {
+         cx.marked = "keyword"
+         return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
+-      } if (isTS && value == "declare") {
++      } else if (isTS && value == "declare") {
+         cx.marked = "keyword"
+         return cont(statement)
++      } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) {
++        cx.marked = "keyword"
++        return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+       } else {
+         return cont(pushlex("stat"), maybelabel);
+       }
+     }
+     if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
+                                       block, poplex, poplex);
+     if (type == "case") return cont(expression, expect(":"));
+     if (type == "default") return cont(expect(":"));
+     if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
+                                      statement, poplex, popcontext);
+     if (type == "class") return cont(pushlex("form"), className, poplex);
+     if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
+     if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
+-    if (type == "module") return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+     if (type == "async") return cont(statement)
+     if (value == "@") return cont(expression, statement)
+     return pass(pushlex("stat"), expression, expect(";"), poplex);
+   }
+   function expression(type) {
+     return expressionInner(type, false);
+   }
+   function expressionNoComma(type) {
+@@ -433,16 +433,18 @@ CodeMirror.defineMode("javascript", func
+     return maybeoperatorNoComma(type, value, false);
+   }
+   function maybeoperatorNoComma(type, value, noComma) {
+     var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
+     var expr = noComma == false ? expression : expressionNoComma;
+     if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
+     if (type == "operator") {
+       if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
++      if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
++        return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
+       if (value == "?") return cont(expression, expect(":"), expr);
+       return cont(expr);
+     }
+     if (type == "quasi") { return pass(quasi, me); }
+     if (type == ";") return;
+     if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
+     if (type == ".") return cont(property, me);
+     if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
+@@ -559,16 +561,28 @@ CodeMirror.defineMode("javascript", func
+     return pass(statement, block);
+   }
+   function maybetype(type, value) {
+     if (isTS) {
+       if (type == ":") return cont(typeexpr);
+       if (value == "?") return cont(maybetype);
+     }
+   }
++  function mayberettype(type) {
++    if (isTS && type == ":") {
++      if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
++      else return cont(typeexpr)
++    }
++  }
++  function isKW(_, value) {
++    if (value == "is") {
++      cx.marked = "keyword"
++      return cont()
++    }
++  }
+   function typeexpr(type, value) {
+     if (type == "variable" || value == "void") {
+       if (value == "keyof") {
+         cx.marked = "keyword"
+         return cont(typeexpr)
+       } else {
+         cx.marked = "type"
+         return cont(afterType)
+@@ -602,16 +616,22 @@ CodeMirror.defineMode("javascript", func
+     if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+     if (value == "|" || type == ".") return cont(typeexpr)
+     if (type == "[") return cont(expect("]"), afterType)
+     if (value == "extends") return cont(typeexpr)
+   }
+   function maybeTypeArgs(_, value) {
+     if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+   }
++  function typeparam() {
++    return pass(typeexpr, maybeTypeDefault)
++  }
++  function maybeTypeDefault(_, value) {
++    if (value == "=") return cont(typeexpr)
++  }
+   function vardef() {
+     return pass(pattern, maybetype, maybeAssign, vardefCont);
+   }
+   function pattern(type, value) {
+     if (type == "modifier") return cont(pattern)
+     if (type == "variable") { register(value); return cont(); }
+     if (type == "spread") return cont(pattern);
+     if (type == "[") return contCommasep(pattern, "]");
+@@ -655,34 +675,34 @@ CodeMirror.defineMode("javascript", func
+     return pass(expression, expect(";"), forspec3);
+   }
+   function forspec3(type) {
+     if (type != ")") cont(expression);
+   }
+   function functiondef(type, value) {
+     if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
+     if (type == "variable") {register(value); return cont(functiondef);}
+-    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
+-    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
++    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
++    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
+   }
+   function funarg(type, value) {
+     if (value == "@") cont(expression, funarg)
+     if (type == "spread" || type == "modifier") return cont(funarg);
+     return pass(pattern, maybetype, maybeAssign);
+   }
+   function classExpression(type, value) {
+     // Class expressions may have an optional name.
+     if (type == "variable") return className(type, value);
+     return classNameAfter(type, value);
+   }
+   function className(type, value) {
+     if (type == "variable") {register(value); return cont(classNameAfter);}
+   }
+   function classNameAfter(type, value) {
+-    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, classNameAfter)
++    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
+     if (value == "extends" || value == "implements" || (isTS && type == ","))
+       return cont(isTS ? typeexpr : expression, classNameAfter);
+     if (type == "{") return cont(pushlex("}"), classBody, poplex);
+   }
+   function classBody(type, value) {
+     if (type == "modifier" || type == "async" ||
+         (type == "variable" &&
+          (value == "static" || value == "get" || value == "set") &&
+diff --git a/devtools/client/sourceeditor/test/codemirror/emacs_test.js b/devtools/client/sourceeditor/test/codemirror/emacs_test.js
+--- a/devtools/client/sourceeditor/test/codemirror/emacs_test.js
++++ b/devtools/client/sourceeditor/test/codemirror/emacs_test.js
+@@ -126,16 +126,18 @@
+       Pos(0, 6), "Ctrl-Alt-T", txt("do [bar]foo dah"));
+ 
+   sim("clearMark", "abcde", Pos(0, 2), "Ctrl-Space", "Ctrl-F", "Ctrl-F",
+       "Ctrl-G", "Ctrl-W", txt("abcde"));
+ 
+   sim("delRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Delete", txt("cde"));
+   sim("backspaceRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Backspace", txt("cde"));
+ 
++  sim("backspaceDoesntAddToRing", "foobar", "Ctrl-F", "Ctrl-F", "Ctrl-F", "Ctrl-K", "Backspace", "Backspace", "Ctrl-Y", txt("fbar"));
++
+   testCM("save", function(cm) {
+     var saved = false;
+     CodeMirror.commands.save = function(cm) { saved = cm.getValue(); };
+     cm.triggerOnKeyDown(fakeEvent("Ctrl-X"));
+     cm.triggerOnKeyDown(fakeEvent("Ctrl-S"));
+     is(saved, "hi");
+   }, {value: "hi", keyMap: "emacs"});
+ 
+diff --git a/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js b/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
+--- a/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
++++ b/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
+@@ -225,16 +225,19 @@
+      "  [property async]: [atom true],",
+      "  [property method]: [string 'GET']",
+      "});");
+ 
+   MT("async_variable",
+      "[keyword const] [def async] [operator =] {[property a]: [number 1]};",
+      "[keyword const] [def foo] [operator =] [string-2 `bar ${][variable async].[property a][string-2 }`];")
+ 
++  MT("async_comment",
++     "[keyword async] [comment /**/] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }");
++
+   MT("indent_switch",
+      "[keyword switch] ([variable x]) {",
+      "  [keyword default]:",
+      "    [keyword return] [number 2]",
+      "}")
+ 
+   MT("regexp_corner_case",
+      "[operator +]{} [operator /] [atom undefined];",
+@@ -363,16 +366,28 @@
+      "[keyword class] [def Foo] {",
+      "  [keyword public] [keyword abstract] [property bar]() {}",
+      "  [property constructor]([keyword readonly] [keyword private] [def x]) {}",
+      "}")
+ 
+   TS("arrow prop",
+      "({[property a]: [def p] [operator =>] [variable-2 p]})")
+ 
++  TS("generic in function call",
++     "[keyword this].[property a][operator <][type Type][operator >]([variable foo]);",
++     "[keyword this].[property a][operator <][variable Type][operator >][variable foo];")
++
++  TS("type guard",
++     "[keyword class] [def Appler] {",
++     "  [keyword static] [property assertApple]([def fruit]: [type Fruit]): [variable-2 fruit] [keyword is] [type Apple] {",
++     "    [keyword if] ([operator !]([variable-2 fruit] [keyword instanceof] [variable Apple]))",
++     "      [keyword throw] [keyword new] [variable Error]();",
++     "  }",
++     "}")
++
+   var jsonld_mode = CodeMirror.getMode(
+     {indentUnit: 2},
+     {name: "javascript", jsonld: true}
+   );
+   function LD(name) {
+     test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1));
+   }
+ 

+ 36 - 0
frg/work-js/mozilla-release/patches/1425866-61a1.patch

@@ -0,0 +1,36 @@
+# HG changeset patch
+# User Emilio Cobos Alvarez <emilio@crisal.io>
+# Date 1524786410 -7200
+# Node ID a42293258ab3dffb794f3a1e8fe2c3937dd81bb5
+# Parent  18e58f90f23b4ee1d14078787e2409bd80eba945
+Bug 1425866: Just null-check the CSS Loader for now. r=heycam
+
+After discussing with Olli there isn't any kind of severe problem out of this.
+Shadow subtrees will be disconnected just like the rest, and they shouldn't
+assume that the document hasn't been disconnected first.
+
+MozReview-Commit-ID: CX4fXOqEIFj
+
+diff --git a/dom/base/nsStyleLinkElement.cpp b/dom/base/nsStyleLinkElement.cpp
+--- a/dom/base/nsStyleLinkElement.cpp
++++ b/dom/base/nsStyleLinkElement.cpp
+@@ -254,17 +254,18 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
+   // When static documents are created, stylesheets are cloned manually.
+   if (mDontLoadStyle || !mUpdatesEnabled ||
+       thisContent->OwnerDoc()->IsStaticDocument()) {
+     return Update { };
+   }
+ 
+   nsCOMPtr<nsIDocument> doc = thisContent->IsInShadowTree() ?
+     thisContent->OwnerDoc() : thisContent->GetUncomposedDoc();
+-  if (!doc || !doc->CSSLoader()->GetEnabled()) {
++  // Loader could be null during unlink, see bug 1425866.
++  if (!doc || !doc->CSSLoader() || !doc->CSSLoader()->GetEnabled()) {
+     return Update { };
+   }
+ 
+   bool isInline;
+   nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+   nsCOMPtr<nsIURI> uri = GetStyleSheetURL(&isInline, getter_AddRefs(triggeringPrincipal));
+   if (aForceUpdate == ForceUpdate::No && mStyleSheet && !isInline && uri) {
+     if (nsIURI* oldURI = mStyleSheet->GetSheetURI()) {

+ 2026 - 0
frg/work-js/mozilla-release/patches/1428170-60a1.patch

@@ -0,0 +1,2026 @@
+# HG changeset patch
+# User Gabriel Luong <gabriel.luong@gmail.com>
+# Date 1517115877 18000
+# Node ID 3f4f6cb852a490f38a24772c08e03cab4d50222b
+# Parent  f7ddbd3ec0744a5b7009bad6671df04035173833
+Bug 1428170 - Update Codemirror to 5.33.0. r=bgrins
+
+diff --git a/devtools/client/sourceeditor/codemirror/README b/devtools/client/sourceeditor/codemirror/README
+--- a/devtools/client/sourceeditor/codemirror/README
++++ b/devtools/client/sourceeditor/codemirror/README
+@@ -1,16 +1,16 @@
+ This is the CodeMirror editor packaged for the Mozilla Project. CodeMirror
+ is a JavaScript component that provides a code editor in the browser. When
+ a mode is available for the language you are coding in, it will color your
+ code, and optionally help with indentation.
+ 
+ # Upgrade
+ 
+-Currently used version is 5.32.0. To upgrade: download a new version of
++Currently used version is 5.33.0. To upgrade: download a new version of
+ CodeMirror from the project's page [1] and replace all JavaScript and
+ CSS files inside the codemirror directory [2].
+ 
+ Then to recreate codemirror.bundle.js:
+  > cd devtools/client/sourceeditor
+  > npm install
+  > webpack
+ 
+diff --git a/devtools/client/sourceeditor/codemirror/addon/edit/closetag.js b/devtools/client/sourceeditor/codemirror/addon/edit/closetag.js
+--- a/devtools/client/sourceeditor/codemirror/addon/edit/closetag.js
++++ b/devtools/client/sourceeditor/codemirror/addon/edit/closetag.js
+@@ -48,23 +48,24 @@
+   var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
+                        "source", "track", "wbr"];
+   var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
+                     "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
+ 
+   function autoCloseGT(cm) {
+     if (cm.getOption("disableInput")) return CodeMirror.Pass;
+     var ranges = cm.listSelections(), replacements = [];
++    var opt = cm.getOption("autoCloseTags");
+     for (var i = 0; i < ranges.length; i++) {
+       if (!ranges[i].empty()) return CodeMirror.Pass;
+       var pos = ranges[i].head, tok = cm.getTokenAt(pos);
+       var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
+       if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
+ 
+-      var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html";
++      var html = inner.mode.configuration == "html";
+       var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
+       var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
+ 
+       var tagName = state.tagName;
+       if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
+       var lowerTagName = tagName.toLowerCase();
+       // Don't process the '>' at the end of an end-tag or self-closing tag
+       if (!tagName ||
+@@ -76,32 +77,35 @@
+         return CodeMirror.Pass;
+ 
+       var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
+       replacements[i] = {indent: indent,
+                          text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
+                          newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
+     }
+ 
++    var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
+     for (var i = ranges.length - 1; i >= 0; i--) {
+       var info = replacements[i];
+       cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
+       var sel = cm.listSelections().slice(0);
+       sel[i] = {head: info.newPos, anchor: info.newPos};
+       cm.setSelections(sel);
+-      if (info.indent) {
++      if (!dontIndentOnAutoClose && info.indent) {
+         cm.indentLine(info.newPos.line, null, true);
+         cm.indentLine(info.newPos.line + 1, null, true);
+       }
+     }
+   }
+ 
+   function autoCloseCurrent(cm, typingSlash) {
+     var ranges = cm.listSelections(), replacements = [];
+     var head = typingSlash ? "/" : "</";
++    var opt = cm.getOption("autoCloseTags");
++    var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
+     for (var i = 0; i < ranges.length; i++) {
+       if (!ranges[i].empty()) return CodeMirror.Pass;
+       var pos = ranges[i].head, tok = cm.getTokenAt(pos);
+       var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
+       if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
+                           tok.start != pos.ch - 1))
+         return CodeMirror.Pass;
+       // Kludge to get around the fact that we are not in XML mode
+@@ -122,19 +126,21 @@
+           return CodeMirror.Pass;
+         replacement = head + state.context.tagName;
+       }
+       if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
+       replacements[i] = replacement;
+     }
+     cm.replaceSelections(replacements);
+     ranges = cm.listSelections();
+-    for (var i = 0; i < ranges.length; i++)
+-      if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
+-        cm.indentLine(ranges[i].head.line);
++    if (!dontIndentOnAutoClose) {
++        for (var i = 0; i < ranges.length; i++)
++            if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
++                cm.indentLine(ranges[i].head.line);
++    }
+   }
+ 
+   function autoCloseSlash(cm) {
+     if (cm.getOption("disableInput")) return CodeMirror.Pass;
+     return autoCloseCurrent(cm, true);
+   }
+ 
+   CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
+diff --git a/devtools/client/sourceeditor/codemirror/addon/edit/continuelist.js b/devtools/client/sourceeditor/codemirror/addon/edit/continuelist.js
+--- a/devtools/client/sourceeditor/codemirror/addon/edit/continuelist.js
++++ b/devtools/client/sourceeditor/codemirror/addon/edit/continuelist.js
+@@ -34,23 +34,21 @@
+         if (!/>\s*$/.test(line)) cm.replaceRange("", {
+           line: pos.line, ch: 0
+         }, {
+           line: pos.line, ch: pos.ch + 1
+         });
+         replacements[i] = "\n";
+       } else {
+         var indent = match[1], after = match[5];
+-        var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
+-          ? match[2].replace("x", " ")
+-          : (parseInt(match[3], 10) + 1) + match[4];
+-
++        var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0);
++        var bullet = numbered ? (parseInt(match[3], 10) + 1) + match[4] : match[2].replace("x", " ");
+         replacements[i] = "\n" + indent + bullet + after;
+ 
+-        incrementRemainingMarkdownListNumbers(cm, pos);
++        if (numbered) incrementRemainingMarkdownListNumbers(cm, pos);
+       }
+     }
+ 
+     cm.replaceSelections(replacements);
+   };
+ 
+   // Auto-updating Markdown list numbers when a new item is added to the
+   // middle of a list
+diff --git a/devtools/client/sourceeditor/codemirror/addon/tern/tern.js b/devtools/client/sourceeditor/codemirror/addon/tern/tern.js
+--- a/devtools/client/sourceeditor/codemirror/addon/tern/tern.js
++++ b/devtools/client/sourceeditor/codemirror/addon/tern/tern.js
+@@ -609,17 +609,18 @@
+     function clear() {
+       cm.state.ternTooltip = null;
+       if (tip.parentNode) fadeOut(tip)
+       clearActivity()
+     }
+     var mouseOnTip = false, old = false;
+     CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; });
+     CodeMirror.on(tip, "mouseout", function(e) {
+-      if (!CodeMirror.contains(tip, e.relatedTarget || e.toElement)) {
++      var related = e.relatedTarget || e.toElement
++      if (!related || !CodeMirror.contains(tip, related)) {
+         if (old) clear();
+         else mouseOnTip = false;
+       }
+     });
+     setTimeout(maybeClear, ts.options.hintDelay ? ts.options.hintDelay : 1700);
+     var clearActivity = onEditorActivity(cm, clear)
+   }
+ 
+diff --git a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+--- a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
++++ b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+@@ -7843,16 +7843,17 @@ var CodeMirror =
+ 	  option("indentUnit", 2, loadMode, true)
+ 	  option("indentWithTabs", false)
+ 	  option("smartIndent", true)
+ 	  option("tabSize", 4, function (cm) {
+ 	    resetModeState(cm)
+ 	    clearCaches(cm)
+ 	    regChange(cm)
+ 	  }, true)
++
+ 	  option("lineSeparator", null, function (cm, val) {
+ 	    cm.doc.lineSep = val
+ 	    if (!val) { return }
+ 	    var newBreaks = [], lineNo = cm.doc.first
+ 	    cm.doc.iter(function (line) {
+ 	      for (var pos = 0;;) {
+ 	        var found = line.text.indexOf(val, pos)
+ 	        if (found == -1) { break }
+@@ -9883,17 +9884,17 @@ var CodeMirror =
+ 	CodeMirror.defineDocExtension = function (name, func) {
+ 	  Doc.prototype[name] = func
+ 	}
+ 
+ 	CodeMirror.fromTextArea = fromTextArea
+ 
+ 	addLegacyProps(CodeMirror)
+ 
+-	CodeMirror.version = "5.32.0"
++	CodeMirror.version = "5.33.0"
+ 
+ 	return CodeMirror;
+ 
+ 	})));
+ 
+ /***/ }),
+ /* 3 */
+ /***/ (function(module, exports, __webpack_require__) {
+@@ -11050,55 +11051,28 @@ var CodeMirror =
+ 
+ 	  // Tokenizer
+ 
+ 	  var keywords = function(){
+ 	    function kw(type) {return {type: type, style: "keyword"};}
+ 	    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
+ 	    var operator = kw("operator"), atom = {type: "atom", style: "atom"};
+ 
+-	    var jsKeywords = {
++	    return {
+ 	      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
+ 	      "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
+ 	      "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
+ 	      "function": kw("function"), "catch": kw("catch"),
+ 	      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
+ 	      "in": operator, "typeof": operator, "instanceof": operator,
+ 	      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
+ 	      "this": kw("this"), "class": kw("class"), "super": kw("atom"),
+ 	      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
+ 	      "await": C
+ 	    };
+-
+-	    // Extend the 'normal' keywords with the TypeScript language extensions
+-	    if (isTS) {
+-	      var type = {type: "variable", style: "type"};
+-	      var tsKeywords = {
+-	        // object-like things
+-	        "interface": kw("class"),
+-	        "implements": C,
+-	        "namespace": C,
+-
+-	        // scope modifiers
+-	        "public": kw("modifier"),
+-	        "private": kw("modifier"),
+-	        "protected": kw("modifier"),
+-	        "abstract": kw("modifier"),
+-	        "readonly": kw("modifier"),
+-
+-	        // types
+-	        "string": type, "number": type, "boolean": type, "any": type
+-	      };
+-
+-	      for (var attr in tsKeywords) {
+-	        jsKeywords[attr] = tsKeywords[attr];
+-	      }
+-	    }
+-
+-	    return jsKeywords;
+ 	  }();
+ 
+ 	  var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
+ 	  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
+ 
+ 	  function readRegexp(stream) {
+ 	    var escaped = false, next, inSet = false;
+ 	    while ((next = stream.next()) != null) {
+@@ -11334,16 +11308,20 @@ var CodeMirror =
+ 	      state.localVars = {name: varname, next: state.localVars};
+ 	    } else {
+ 	      if (inList(state.globalVars)) return;
+ 	      if (parserConfig.globalVars)
+ 	        state.globalVars = {name: varname, next: state.globalVars};
+ 	    }
+ 	  }
+ 
++	  function isModifier(name) {
++	    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
++	  }
++
+ 	  // Combinators
+ 
+ 	  var defaultVars = {name: "this", next: {name: "arguments"}};
+ 	  function pushcontext() {
+ 	    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
+ 	    cx.state.localVars = defaultVars;
+ 	  }
+ 	  function popcontext() {
+@@ -11390,64 +11368,67 @@ var CodeMirror =
+ 	    if (type == ";") return cont();
+ 	    if (type == "if") {
+ 	      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
+ 	        cx.state.cc.pop()();
+ 	      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
+ 	    }
+ 	    if (type == "function") return cont(functiondef);
+ 	    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
++	    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); }
+ 	    if (type == "variable") {
+ 	      if (isTS && value == "type") {
+ 	        cx.marked = "keyword"
+ 	        return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
+ 	      } else if (isTS && value == "declare") {
+ 	        cx.marked = "keyword"
+ 	        return cont(statement)
+ 	      } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) {
+ 	        cx.marked = "keyword"
+ 	        return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
++	      } else if (isTS && value == "namespace") {
++	        cx.marked = "keyword"
++	        return cont(pushlex("form"), expression, block, poplex)
+ 	      } else {
+ 	        return cont(pushlex("stat"), maybelabel);
+ 	      }
+ 	    }
+ 	    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
+ 	                                      block, poplex, poplex);
+ 	    if (type == "case") return cont(expression, expect(":"));
+ 	    if (type == "default") return cont(expect(":"));
+ 	    if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
+ 	                                     statement, poplex, popcontext);
+-	    if (type == "class") return cont(pushlex("form"), className, poplex);
+ 	    if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
+ 	    if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
+ 	    if (type == "async") return cont(statement)
+ 	    if (value == "@") return cont(expression, statement)
+ 	    return pass(pushlex("stat"), expression, expect(";"), poplex);
+ 	  }
+-	  function expression(type) {
+-	    return expressionInner(type, false);
+-	  }
+-	  function expressionNoComma(type) {
+-	    return expressionInner(type, true);
++	  function expression(type, value) {
++	    return expressionInner(type, value, false);
++	  }
++	  function expressionNoComma(type, value) {
++	    return expressionInner(type, value, true);
+ 	  }
+ 	  function parenExpr(type) {
+ 	    if (type != "(") return pass()
+ 	    return cont(pushlex(")"), expression, expect(")"), poplex)
+ 	  }
+-	  function expressionInner(type, noComma) {
++	  function expressionInner(type, value, noComma) {
+ 	    if (cx.state.fatArrowAt == cx.stream.start) {
+ 	      var body = noComma ? arrowBodyNoComma : arrowBody;
+ 	      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
+ 	      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
+ 	    }
+ 
+ 	    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
+ 	    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
+ 	    if (type == "function") return cont(functiondef, maybeop);
+-	    if (type == "class") return cont(pushlex("form"), classExpression, poplex);
++	    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
+ 	    if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
+ 	    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
+ 	    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
+ 	    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
+ 	    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
+ 	    if (type == "quasi") return pass(quasi, maybeop);
+ 	    if (type == "new") return cont(maybeTarget(noComma));
+ 	    return cont();
+@@ -11535,20 +11516,21 @@ var CodeMirror =
+ 	      if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
+ 	        cx.state.fatArrowAt = cx.stream.pos + m[0].length
+ 	      return cont(afterprop);
+ 	    } else if (type == "number" || type == "string") {
+ 	      cx.marked = jsonldMode ? "property" : (cx.style + " property");
+ 	      return cont(afterprop);
+ 	    } else if (type == "jsonld-keyword") {
+ 	      return cont(afterprop);
+-	    } else if (type == "modifier") {
++	    } else if (isTS && isModifier(value)) {
++	      cx.marked = "keyword"
+ 	      return cont(objprop)
+ 	    } else if (type == "[") {
+-	      return cont(expression, expect("]"), afterprop);
++	      return cont(expression, maybetype, expect("]"), afterprop);
+ 	    } else if (type == "spread") {
+ 	      return cont(expressionNoComma, afterprop);
+ 	    } else if (value == "*") {
+ 	      cx.marked = "keyword";
+ 	      return cont(objprop);
+ 	    } else if (type == ":") {
+ 	      return pass(afterprop)
+ 	    }
+@@ -11640,32 +11622,32 @@ var CodeMirror =
+ 	  function typearg(type) {
+ 	    if (type == "variable") return cont(typearg)
+ 	    else if (type == ":") return cont(typeexpr)
+ 	  }
+ 	  function afterType(type, value) {
+ 	    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+ 	    if (value == "|" || type == ".") return cont(typeexpr)
+ 	    if (type == "[") return cont(expect("]"), afterType)
+-	    if (value == "extends") return cont(typeexpr)
++	    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
+ 	  }
+ 	  function maybeTypeArgs(_, value) {
+ 	    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+ 	  }
+ 	  function typeparam() {
+ 	    return pass(typeexpr, maybeTypeDefault)
+ 	  }
+ 	  function maybeTypeDefault(_, value) {
+ 	    if (value == "=") return cont(typeexpr)
+ 	  }
+ 	  function vardef() {
+ 	    return pass(pattern, maybetype, maybeAssign, vardefCont);
+ 	  }
+ 	  function pattern(type, value) {
+-	    if (type == "modifier") return cont(pattern)
++	    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
+ 	    if (type == "variable") { register(value); return cont(); }
+ 	    if (type == "spread") return cont(pattern);
+ 	    if (type == "[") return contCommasep(pattern, "]");
+ 	    if (type == "{") return contCommasep(proppattern, "}");
+ 	  }
+ 	  function proppattern(type, value) {
+ 	    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
+ 	      register(value);
+@@ -11709,17 +11691,18 @@ var CodeMirror =
+ 	  function functiondef(type, value) {
+ 	    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
+ 	    if (type == "variable") {register(value); return cont(functiondef);}
+ 	    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
+ 	    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
+ 	  }
+ 	  function funarg(type, value) {
+ 	    if (value == "@") cont(expression, funarg)
+-	    if (type == "spread" || type == "modifier") return cont(funarg);
++	    if (type == "spread") return cont(funarg);
++	    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
+ 	    return pass(pattern, maybetype, maybeAssign);
+ 	  }
+ 	  function classExpression(type, value) {
+ 	    // Class expressions may have an optional name.
+ 	    if (type == "variable") return className(type, value);
+ 	    return classNameAfter(type, value);
+ 	  }
+ 	  function className(type, value) {
+@@ -11727,29 +11710,29 @@ var CodeMirror =
+ 	  }
+ 	  function classNameAfter(type, value) {
+ 	    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
+ 	    if (value == "extends" || value == "implements" || (isTS && type == ","))
+ 	      return cont(isTS ? typeexpr : expression, classNameAfter);
+ 	    if (type == "{") return cont(pushlex("}"), classBody, poplex);
+ 	  }
+ 	  function classBody(type, value) {
+-	    if (type == "modifier" || type == "async" ||
++	    if (type == "async" ||
+ 	        (type == "variable" &&
+-	         (value == "static" || value == "get" || value == "set") &&
++	         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
+ 	         cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
+ 	      cx.marked = "keyword";
+ 	      return cont(classBody);
+ 	    }
+ 	    if (type == "variable" || cx.style == "keyword") {
+ 	      cx.marked = "property";
+ 	      return cont(isTS ? classfield : functiondef, classBody);
+ 	    }
+ 	    if (type == "[")
+-	      return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody)
++	      return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
+ 	    if (value == "*") {
+ 	      cx.marked = "keyword";
+ 	      return cont(classBody);
+ 	    }
+ 	    if (type == ";") return cont(classBody);
+ 	    if (type == "}") return cont();
+ 	    if (value == "@") return cont(expression, classBody)
+ 	  }
+@@ -11957,16 +11940,17 @@ var CodeMirror =
+ 
+ 	var xmlConfig = {
+ 	  autoSelfClosers: {},
+ 	  implicitlyClosed: {},
+ 	  contextGrabbers: {},
+ 	  doNotIndent: {},
+ 	  allowUnquoted: false,
+ 	  allowMissing: false,
++	  allowMissingTagName: false,
+ 	  caseFold: false
+ 	}
+ 
+ 	CodeMirror.defineMode("xml", function(editorConf, config_) {
+ 	  var indentUnit = editorConf.indentUnit
+ 	  var config = {}
+ 	  var defaults = config_.htmlMode ? htmlConfig : xmlConfig
+ 	  for (var prop in defaults) config[prop] = defaults[prop]
+@@ -12131,16 +12115,19 @@ var CodeMirror =
+ 	      return baseState;
+ 	    }
+ 	  }
+ 	  function tagNameState(type, stream, state) {
+ 	    if (type == "word") {
+ 	      state.tagName = stream.current();
+ 	      setStyle = "tag";
+ 	      return attrState;
++	    } else if (config.allowMissingTagName && type == "endTag") {
++	      setStyle = "tag bracket";
++	      return attrState(type, stream, state);
+ 	    } else {
+ 	      setStyle = "error";
+ 	      return tagNameState;
+ 	    }
+ 	  }
+ 	  function closeTagNameState(type, stream, state) {
+ 	    if (type == "word") {
+ 	      var tagName = stream.current();
+@@ -12149,16 +12136,19 @@ var CodeMirror =
+ 	        popContext(state);
+ 	      if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
+ 	        setStyle = "tag";
+ 	        return closeState;
+ 	      } else {
+ 	        setStyle = "tag error";
+ 	        return closeStateErr;
+ 	      }
++	    } else if (config.allowMissingTagName && type == "endTag") {
++	      setStyle = "tag bracket";
++	      return closeState(type, stream, state);
+ 	    } else {
+ 	      setStyle = "error";
+ 	      return closeStateErr;
+ 	    }
+ 	  }
+ 
+ 	  function closeState(type, _stream, state) {
+ 	    if (type != "endTag") {
+@@ -12382,19 +12372,19 @@ var CodeMirror =
+ 	        return ret("meta", "meta");
+ 	      }
+ 	    } else if (/[,+>*\/]/.test(ch)) {
+ 	      return ret(null, "select-op");
+ 	    } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
+ 	      return ret("qualifier", "qualifier");
+ 	    } else if (/[:;{}\[\]\(\)]/.test(ch)) {
+ 	      return ret(null, ch);
+-	    } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
+-	               (ch == "d" && stream.match("omain(")) ||
+-	               (ch == "r" && stream.match("egexp("))) {
++	    } else if (((ch == "u" || ch == "U") && stream.match(/rl(-prefix)?\(/i)) ||
++	               ((ch == "d" || ch == "D") && stream.match("omain(", true, true)) ||
++	               ((ch == "r" || ch == "R") && stream.match("egexp(", true, true))) {
+ 	      stream.backUp(1);
+ 	      state.tokenize = tokenParenthesized;
+ 	      return ret("property", "word");
+ 	    } else if (/[\w\\\-]/.test(ch)) {
+ 	      stream.eatWhile(/[\w\\\-]/);
+ 	      return ret("property", "word");
+ 	    } else {
+ 	      return ret(null, null);
+@@ -12467,26 +12457,26 @@ var CodeMirror =
+ 
+ 	  var states = {};
+ 
+ 	  states.top = function(type, stream, state) {
+ 	    if (type == "{") {
+ 	      return pushContext(state, stream, "block");
+ 	    } else if (type == "}" && state.context.prev) {
+ 	      return popContext(state);
+-	    } else if (supportsAtComponent && /@component/.test(type)) {
++	    } else if (supportsAtComponent && /@component/i.test(type)) {
+ 	      return pushContext(state, stream, "atComponentBlock");
+-	    } else if (/^@(-moz-)?document$/.test(type)) {
++	    } else if (/^@(-moz-)?document$/i.test(type)) {
+ 	      return pushContext(state, stream, "documentTypes");
+-	    } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
++	    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
+ 	      return pushContext(state, stream, "atBlock");
+-	    } else if (/^@(font-face|counter-style)/.test(type)) {
++	    } else if (/^@(font-face|counter-style)/i.test(type)) {
+ 	      state.stateArg = type;
+ 	      return "restricted_atBlock_before";
+-	    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
++	    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
+ 	      return "keyframes";
+ 	    } else if (type && type.charAt(0) == "@") {
+ 	      return pushContext(state, stream, "at");
+ 	    } else if (type == "hash") {
+ 	      override = "builtin";
+ 	    } else if (type == "word") {
+ 	      override = "tag";
+ 	    } else if (type == "variable-definition") {
+@@ -13098,17 +13088,17 @@ var CodeMirror =
+ 	          state.tokenize = tokenCComment;
+ 	          return tokenCComment(stream, state);
+ 	        } else {
+ 	          return ["operator", "operator"];
+ 	        }
+ 	      },
+ 	      "@": function(stream) {
+ 	        if (stream.eat("{")) return [null, "interpolation"];
+-	        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
++	        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
+ 	        stream.eatWhile(/[\w\\\-]/);
+ 	        if (stream.match(/^\s*:/, false))
+ 	          return ["variable-2", "variable-definition"];
+ 	        return ["variable-2", "variable"];
+ 	      },
+ 	      "&": function() {
+ 	        return ["atom", "atom"];
+ 	      }
+@@ -13327,17 +13317,17 @@ var CodeMirror =
+ 	  function copyContext(context) {
+ 	    return new Context(CodeMirror.copyState(context.mode, context.state),
+ 	                       context.mode,
+ 	                       context.depth,
+ 	                       context.prev && copyContext(context.prev))
+ 	  }
+ 
+ 	  CodeMirror.defineMode("jsx", function(config, modeConfig) {
+-	    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false})
++	    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false, allowMissingTagName: true})
+ 	    var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || "javascript")
+ 
+ 	    function flatXMLIndent(state) {
+ 	      var tagName = state.tagName
+ 	      state.tagName = null
+ 	      var result = xmlMode.indent(state, "")
+ 	      state.tagName = tagName
+ 	      return result
+@@ -14520,16 +14510,37 @@ var CodeMirror =
+ 	        state.tokenize = null;
+ 	        break;
+ 	      }
+ 	      escaped = stream.next() == "\\" && !escaped;
+ 	    }
+ 	    return "string";
+ 	  }
+ 
++	  function tokenNestedComment(depth) {
++	    return function (stream, state) {
++	      var ch
++	      while (ch = stream.next()) {
++	        if (ch == "*" && stream.eat("/")) {
++	          if (depth == 1) {
++	            state.tokenize = null
++	            break
++	          } else {
++	            state.tokenize = tokenNestedComment(depth - 1)
++	            return state.tokenize(stream, state)
++	          }
++	        } else if (ch == "/" && stream.eat("*")) {
++	          state.tokenize = tokenNestedComment(depth + 1)
++	          return state.tokenize(stream, state)
++	        }
++	      }
++	      return "comment"
++	    }
++	  }
++
+ 	  def("text/x-scala", {
+ 	    name: "clike",
+ 	    keywords: words(
+ 
+ 	      /* scala */
+ 	      "abstract case catch class def do else extends final finally for forSome if " +
+ 	      "implicit import lazy match new null object override package private protected return " +
+ 	      "sealed super this throw trait try type val var while with yield _ " +
+@@ -14575,16 +14586,22 @@ var CodeMirror =
+ 	      "=": function(stream, state) {
+ 	        var cx = state.context
+ 	        if (cx.type == "}" && cx.align && stream.eat(">")) {
+ 	          state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)
+ 	          return "operator"
+ 	        } else {
+ 	          return false
+ 	        }
++	      },
++
++	      "/": function(stream, state) {
++	        if (!stream.eat("*")) return false
++	        state.tokenize = tokenNestedComment(1)
++	        return state.tokenize(stream, state)
+ 	      }
+ 	    },
+ 	    modeProps: {closeBrackets: {triples: '"'}}
+ 	  });
+ 
+ 	  function tokenKotlinString(tripleString){
+ 	    return function (stream, state) {
+ 	      var escaped = false, next, end = false;
+@@ -14609,17 +14626,17 @@ var CodeMirror =
+ 	      "package as typealias class interface this super val " +
+ 	      "var fun for is in This throw return " +
+ 	      "break continue object if else while do try when !in !is as? " +
+ 
+ 	      /*soft keywords*/
+ 	      "file import where by get set abstract enum open inner override private public internal " +
+ 	      "protected catch finally out final vararg reified dynamic companion constructor init " +
+ 	      "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
+-	      "external annotation crossinline const operator infix suspend"
++	      "external annotation crossinline const operator infix suspend actual expect"
+ 	    ),
+ 	    types: words(
+ 	      /* package java.lang */
+ 	      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+ 	      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+ 	      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+ 	      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
+ 	    ),
+@@ -18206,35 +18223,42 @@ var CodeMirror =
+ 	          } else {
+ 	            cm.setCursor(offsetCursor(curEnd, 0, -1));
+ 	          }
+ 	        }
+ 	      },
+ 	      incrementNumberToken: function(cm, actionArgs) {
+ 	        var cur = cm.getCursor();
+ 	        var lineStr = cm.getLine(cur.line);
+-	        var re = /-?\d+/g;
++	        var re = /(-?)(?:(0x)([\da-f]+)|(0b|0|)(\d+))/gi;
+ 	        var match;
+ 	        var start;
+ 	        var end;
+ 	        var numberStr;
+-	        var token;
+ 	        while ((match = re.exec(lineStr)) !== null) {
+-	          token = match[0];
+ 	          start = match.index;
+-	          end = start + token.length;
++	          end = start + match[0].length;
+ 	          if (cur.ch < end)break;
+ 	        }
+ 	        if (!actionArgs.backtrack && (end <= cur.ch))return;
+-	        if (token) {
++	        if (match) {
++	          var baseStr = match[2] || match[4]
++	          var digits = match[3] || match[5]
+ 	          var increment = actionArgs.increase ? 1 : -1;
+-	          var number = parseInt(token) + (increment * actionArgs.repeat);
++	          var base = {'0b': 2, '0': 8, '': 10, '0x': 16}[baseStr.toLowerCase()];
++	          var number = parseInt(match[1] + digits, base) + (increment * actionArgs.repeat);
++	          numberStr = number.toString(base);
++	          var zeroPadding = baseStr ? new Array(digits.length - numberStr.length + 1 + match[1].length).join('0') : ''
++	          if (numberStr.charAt(0) === '-') {
++	            numberStr = '-' + baseStr + zeroPadding + numberStr.substr(1);
++	          } else {
++	            numberStr = baseStr + zeroPadding + numberStr;
++	          }
+ 	          var from = Pos(cur.line, start);
+ 	          var to = Pos(cur.line, end);
+-	          numberStr = number.toString();
+ 	          cm.replaceRange(numberStr, from, to);
+ 	        } else {
+ 	          return;
+ 	        }
+ 	        cm.setCursor(Pos(cur.line, start + numberStr.length - 1));
+ 	      },
+ 	      repeatLastEdit: function(cm, actionArgs, vim) {
+ 	        var lastEditInputState = vim.lastEditInputState;
+@@ -20970,19 +20994,25 @@ var CodeMirror =
+ 	    var ranges = cm.listSelections(), newRanges = []
+ 	    for (var i = 0; i < ranges.length; i++) {
+ 	      var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
+ 	      if (!opening) return false;
+ 	      for (;;) {
+ 	        var closing = cm.scanForBracket(pos, 1);
+ 	        if (!closing) return false;
+ 	        if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
+-	          newRanges.push({anchor: Pos(opening.pos.line, opening.pos.ch + 1),
+-	                          head: closing.pos});
+-	          break;
++	          var startPos = Pos(opening.pos.line, opening.pos.ch + 1);
++	          if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&
++	              CodeMirror.cmpPos(closing.pos, range.to()) == 0) {
++	            opening = cm.scanForBracket(opening.pos, -1);
++	            if (!opening) return false;
++	          } else {
++	            newRanges.push({anchor: startPos, head: closing.pos});
++	            break;
++	          }
+ 	        }
+ 	        pos = Pos(closing.pos.line, closing.pos.ch + 1);
+ 	      }
+ 	    }
+ 	    cm.setSelections(newRanges);
+ 	    return true;
+ 	  }
+ 
+@@ -21295,37 +21325,16 @@ var CodeMirror =
+ 	      cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
+ 	  };
+ 
+ 	  cmds.showInCenter = function(cm) {
+ 	    var pos = cm.cursorCoords(null, "local");
+ 	    cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
+ 	  };
+ 
+-	  cmds.selectLinesUpward = function(cm) {
+-	    cm.operation(function() {
+-	      var ranges = cm.listSelections();
+-	      for (var i = 0; i < ranges.length; i++) {
+-	        var range = ranges[i];
+-	        if (range.head.line > cm.firstLine())
+-	          cm.addSelection(Pos(range.head.line - 1, range.head.ch));
+-	      }
+-	    });
+-	  };
+-	  cmds.selectLinesDownward = function(cm) {
+-	    cm.operation(function() {
+-	      var ranges = cm.listSelections();
+-	      for (var i = 0; i < ranges.length; i++) {
+-	        var range = ranges[i];
+-	        if (range.head.line < cm.lastLine())
+-	          cm.addSelection(Pos(range.head.line + 1, range.head.ch));
+-	      }
+-	    });
+-	  };
+-
+ 	  function getTarget(cm) {
+ 	    var from = cm.getCursor("from"), to = cm.getCursor("to");
+ 	    if (CodeMirror.cmpPos(from, to) == 0) {
+ 	      var word = wordAt(cm, from);
+ 	      if (!word.word) return;
+ 	      from = word.from;
+ 	      to = word.to;
+ 	    }
+@@ -21377,18 +21386,16 @@ var CodeMirror =
+ 	    "Ctrl-Alt-Up": "scrollLineUp",
+ 	    "Ctrl-Alt-Down": "scrollLineDown",
+ 	    "Cmd-L": "selectLine",
+ 	    "Shift-Cmd-L": "splitSelectionByLine",
+ 	    "Esc": "singleSelectionTop",
+ 	    "Cmd-Enter": "insertLineAfter",
+ 	    "Shift-Cmd-Enter": "insertLineBefore",
+ 	    "Cmd-D": "selectNextOccurrence",
+-	    "Shift-Cmd-Up": "addCursorToPrevLine",
+-	    "Shift-Cmd-Down": "addCursorToNextLine",
+ 	    "Shift-Cmd-Space": "selectScope",
+ 	    "Shift-Cmd-M": "selectBetweenBrackets",
+ 	    "Cmd-M": "goToBracket",
+ 	    "Cmd-Ctrl-Up": "swapLineUp",
+ 	    "Cmd-Ctrl-Down": "swapLineDown",
+ 	    "Cmd-/": "toggleCommentIndented",
+ 	    "Cmd-J": "joinLines",
+ 	    "Shift-Cmd-D": "duplicateLine",
+@@ -21408,18 +21415,18 @@ var CodeMirror =
+ 	    "Cmd-K Cmd-W": "deleteToSublimeMark",
+ 	    "Cmd-K Cmd-X": "swapWithSublimeMark",
+ 	    "Cmd-K Cmd-Y": "sublimeYank",
+ 	    "Cmd-K Cmd-C": "showInCenter",
+ 	    "Cmd-K Cmd-G": "clearBookmarks",
+ 	    "Cmd-K Cmd-Backspace": "delLineLeft",
+ 	    "Cmd-K Cmd-0": "unfoldAll",
+ 	    "Cmd-K Cmd-J": "unfoldAll",
+-	    "Ctrl-Shift-Up": "selectLinesUpward",
+-	    "Ctrl-Shift-Down": "selectLinesDownward",
++	    "Ctrl-Shift-Up": "addCursorToPrevLine",
++	    "Ctrl-Shift-Down": "addCursorToNextLine",
+ 	    "Cmd-F3": "findUnder",
+ 	    "Shift-Cmd-F3": "findUnderPrevious",
+ 	    "Alt-F3": "findAllUnder",
+ 	    "Shift-Cmd-[": "fold",
+ 	    "Shift-Cmd-]": "unfold",
+ 	    "Cmd-I": "findIncremental",
+ 	    "Shift-Cmd-I": "findIncrementalReverse",
+ 	    "Cmd-H": "replace",
+@@ -21439,18 +21446,16 @@ var CodeMirror =
+ 	    "Ctrl-Up": "scrollLineUp",
+ 	    "Ctrl-Down": "scrollLineDown",
+ 	    "Ctrl-L": "selectLine",
+ 	    "Shift-Ctrl-L": "splitSelectionByLine",
+ 	    "Esc": "singleSelectionTop",
+ 	    "Ctrl-Enter": "insertLineAfter",
+ 	    "Shift-Ctrl-Enter": "insertLineBefore",
+ 	    "Ctrl-D": "selectNextOccurrence",
+-	    "Alt-CtrlUp": "addCursorToPrevLine",
+-	    "Alt-CtrlDown": "addCursorToNextLine",
+ 	    "Shift-Ctrl-Space": "selectScope",
+ 	    "Shift-Ctrl-M": "selectBetweenBrackets",
+ 	    "Ctrl-M": "goToBracket",
+ 	    "Shift-Ctrl-Up": "swapLineUp",
+ 	    "Shift-Ctrl-Down": "swapLineDown",
+ 	    "Ctrl-/": "toggleCommentIndented",
+ 	    "Ctrl-J": "joinLines",
+ 	    "Shift-Ctrl-D": "duplicateLine",
+@@ -21470,18 +21475,18 @@ var CodeMirror =
+ 	    "Ctrl-K Ctrl-W": "deleteToSublimeMark",
+ 	    "Ctrl-K Ctrl-X": "swapWithSublimeMark",
+ 	    "Ctrl-K Ctrl-Y": "sublimeYank",
+ 	    "Ctrl-K Ctrl-C": "showInCenter",
+ 	    "Ctrl-K Ctrl-G": "clearBookmarks",
+ 	    "Ctrl-K Ctrl-Backspace": "delLineLeft",
+ 	    "Ctrl-K Ctrl-0": "unfoldAll",
+ 	    "Ctrl-K Ctrl-J": "unfoldAll",
+-	    "Ctrl-Alt-Up": "selectLinesUpward",
+-	    "Ctrl-Alt-Down": "selectLinesDownward",
++	    "Ctrl-Alt-Up": "addCursorToPrevLine",
++	    "Ctrl-Alt-Down": "addCursorToNextLine",
+ 	    "Ctrl-F3": "findUnder",
+ 	    "Shift-Ctrl-F3": "findUnderPrevious",
+ 	    "Alt-F3": "findAllUnder",
+ 	    "Shift-Ctrl-[": "fold",
+ 	    "Shift-Ctrl-]": "unfold",
+ 	    "Ctrl-I": "findIncremental",
+ 	    "Shift-Ctrl-I": "findIncrementalReverse",
+ 	    "Ctrl-H": "replace",
+diff --git a/devtools/client/sourceeditor/codemirror/keymap/sublime.js b/devtools/client/sourceeditor/codemirror/keymap/sublime.js
+--- a/devtools/client/sourceeditor/codemirror/keymap/sublime.js
++++ b/devtools/client/sourceeditor/codemirror/keymap/sublime.js
+@@ -178,19 +178,25 @@
+     var ranges = cm.listSelections(), newRanges = []
+     for (var i = 0; i < ranges.length; i++) {
+       var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
+       if (!opening) return false;
+       for (;;) {
+         var closing = cm.scanForBracket(pos, 1);
+         if (!closing) return false;
+         if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
+-          newRanges.push({anchor: Pos(opening.pos.line, opening.pos.ch + 1),
+-                          head: closing.pos});
+-          break;
++          var startPos = Pos(opening.pos.line, opening.pos.ch + 1);
++          if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&
++              CodeMirror.cmpPos(closing.pos, range.to()) == 0) {
++            opening = cm.scanForBracket(opening.pos, -1);
++            if (!opening) return false;
++          } else {
++            newRanges.push({anchor: startPos, head: closing.pos});
++            break;
++          }
+         }
+         pos = Pos(closing.pos.line, closing.pos.ch + 1);
+       }
+     }
+     cm.setSelections(newRanges);
+     return true;
+   }
+ 
+@@ -503,37 +509,16 @@
+       cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
+   };
+ 
+   cmds.showInCenter = function(cm) {
+     var pos = cm.cursorCoords(null, "local");
+     cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
+   };
+ 
+-  cmds.selectLinesUpward = function(cm) {
+-    cm.operation(function() {
+-      var ranges = cm.listSelections();
+-      for (var i = 0; i < ranges.length; i++) {
+-        var range = ranges[i];
+-        if (range.head.line > cm.firstLine())
+-          cm.addSelection(Pos(range.head.line - 1, range.head.ch));
+-      }
+-    });
+-  };
+-  cmds.selectLinesDownward = function(cm) {
+-    cm.operation(function() {
+-      var ranges = cm.listSelections();
+-      for (var i = 0; i < ranges.length; i++) {
+-        var range = ranges[i];
+-        if (range.head.line < cm.lastLine())
+-          cm.addSelection(Pos(range.head.line + 1, range.head.ch));
+-      }
+-    });
+-  };
+-
+   function getTarget(cm) {
+     var from = cm.getCursor("from"), to = cm.getCursor("to");
+     if (CodeMirror.cmpPos(from, to) == 0) {
+       var word = wordAt(cm, from);
+       if (!word.word) return;
+       from = word.from;
+       to = word.to;
+     }
+@@ -585,18 +570,16 @@
+     "Ctrl-Alt-Up": "scrollLineUp",
+     "Ctrl-Alt-Down": "scrollLineDown",
+     "Cmd-L": "selectLine",
+     "Shift-Cmd-L": "splitSelectionByLine",
+     "Esc": "singleSelectionTop",
+     "Cmd-Enter": "insertLineAfter",
+     "Shift-Cmd-Enter": "insertLineBefore",
+     "Cmd-D": "selectNextOccurrence",
+-    "Shift-Cmd-Up": "addCursorToPrevLine",
+-    "Shift-Cmd-Down": "addCursorToNextLine",
+     "Shift-Cmd-Space": "selectScope",
+     "Shift-Cmd-M": "selectBetweenBrackets",
+     "Cmd-M": "goToBracket",
+     "Cmd-Ctrl-Up": "swapLineUp",
+     "Cmd-Ctrl-Down": "swapLineDown",
+     "Cmd-/": "toggleCommentIndented",
+     "Cmd-J": "joinLines",
+     "Shift-Cmd-D": "duplicateLine",
+@@ -616,18 +599,18 @@
+     "Cmd-K Cmd-W": "deleteToSublimeMark",
+     "Cmd-K Cmd-X": "swapWithSublimeMark",
+     "Cmd-K Cmd-Y": "sublimeYank",
+     "Cmd-K Cmd-C": "showInCenter",
+     "Cmd-K Cmd-G": "clearBookmarks",
+     "Cmd-K Cmd-Backspace": "delLineLeft",
+     "Cmd-K Cmd-0": "unfoldAll",
+     "Cmd-K Cmd-J": "unfoldAll",
+-    "Ctrl-Shift-Up": "selectLinesUpward",
+-    "Ctrl-Shift-Down": "selectLinesDownward",
++    "Ctrl-Shift-Up": "addCursorToPrevLine",
++    "Ctrl-Shift-Down": "addCursorToNextLine",
+     "Cmd-F3": "findUnder",
+     "Shift-Cmd-F3": "findUnderPrevious",
+     "Alt-F3": "findAllUnder",
+     "Shift-Cmd-[": "fold",
+     "Shift-Cmd-]": "unfold",
+     "Cmd-I": "findIncremental",
+     "Shift-Cmd-I": "findIncrementalReverse",
+     "Cmd-H": "replace",
+@@ -647,18 +630,16 @@
+     "Ctrl-Up": "scrollLineUp",
+     "Ctrl-Down": "scrollLineDown",
+     "Ctrl-L": "selectLine",
+     "Shift-Ctrl-L": "splitSelectionByLine",
+     "Esc": "singleSelectionTop",
+     "Ctrl-Enter": "insertLineAfter",
+     "Shift-Ctrl-Enter": "insertLineBefore",
+     "Ctrl-D": "selectNextOccurrence",
+-    "Alt-CtrlUp": "addCursorToPrevLine",
+-    "Alt-CtrlDown": "addCursorToNextLine",
+     "Shift-Ctrl-Space": "selectScope",
+     "Shift-Ctrl-M": "selectBetweenBrackets",
+     "Ctrl-M": "goToBracket",
+     "Shift-Ctrl-Up": "swapLineUp",
+     "Shift-Ctrl-Down": "swapLineDown",
+     "Ctrl-/": "toggleCommentIndented",
+     "Ctrl-J": "joinLines",
+     "Shift-Ctrl-D": "duplicateLine",
+@@ -678,18 +659,18 @@
+     "Ctrl-K Ctrl-W": "deleteToSublimeMark",
+     "Ctrl-K Ctrl-X": "swapWithSublimeMark",
+     "Ctrl-K Ctrl-Y": "sublimeYank",
+     "Ctrl-K Ctrl-C": "showInCenter",
+     "Ctrl-K Ctrl-G": "clearBookmarks",
+     "Ctrl-K Ctrl-Backspace": "delLineLeft",
+     "Ctrl-K Ctrl-0": "unfoldAll",
+     "Ctrl-K Ctrl-J": "unfoldAll",
+-    "Ctrl-Alt-Up": "selectLinesUpward",
+-    "Ctrl-Alt-Down": "selectLinesDownward",
++    "Ctrl-Alt-Up": "addCursorToPrevLine",
++    "Ctrl-Alt-Down": "addCursorToNextLine",
+     "Ctrl-F3": "findUnder",
+     "Shift-Ctrl-F3": "findUnderPrevious",
+     "Alt-F3": "findAllUnder",
+     "Shift-Ctrl-[": "fold",
+     "Shift-Ctrl-]": "unfold",
+     "Ctrl-I": "findIncremental",
+     "Shift-Ctrl-I": "findIncrementalReverse",
+     "Ctrl-H": "replace",
+diff --git a/devtools/client/sourceeditor/codemirror/keymap/vim.js b/devtools/client/sourceeditor/codemirror/keymap/vim.js
+--- a/devtools/client/sourceeditor/codemirror/keymap/vim.js
++++ b/devtools/client/sourceeditor/codemirror/keymap/vim.js
+@@ -2632,35 +2632,42 @@
+           } else {
+             cm.setCursor(offsetCursor(curEnd, 0, -1));
+           }
+         }
+       },
+       incrementNumberToken: function(cm, actionArgs) {
+         var cur = cm.getCursor();
+         var lineStr = cm.getLine(cur.line);
+-        var re = /-?\d+/g;
++        var re = /(-?)(?:(0x)([\da-f]+)|(0b|0|)(\d+))/gi;
+         var match;
+         var start;
+         var end;
+         var numberStr;
+-        var token;
+         while ((match = re.exec(lineStr)) !== null) {
+-          token = match[0];
+           start = match.index;
+-          end = start + token.length;
++          end = start + match[0].length;
+           if (cur.ch < end)break;
+         }
+         if (!actionArgs.backtrack && (end <= cur.ch))return;
+-        if (token) {
++        if (match) {
++          var baseStr = match[2] || match[4]
++          var digits = match[3] || match[5]
+           var increment = actionArgs.increase ? 1 : -1;
+-          var number = parseInt(token) + (increment * actionArgs.repeat);
++          var base = {'0b': 2, '0': 8, '': 10, '0x': 16}[baseStr.toLowerCase()];
++          var number = parseInt(match[1] + digits, base) + (increment * actionArgs.repeat);
++          numberStr = number.toString(base);
++          var zeroPadding = baseStr ? new Array(digits.length - numberStr.length + 1 + match[1].length).join('0') : ''
++          if (numberStr.charAt(0) === '-') {
++            numberStr = '-' + baseStr + zeroPadding + numberStr.substr(1);
++          } else {
++            numberStr = baseStr + zeroPadding + numberStr;
++          }
+           var from = Pos(cur.line, start);
+           var to = Pos(cur.line, end);
+-          numberStr = number.toString();
+           cm.replaceRange(numberStr, from, to);
+         } else {
+           return;
+         }
+         cm.setCursor(Pos(cur.line, start + numberStr.length - 1));
+       },
+       repeatLastEdit: function(cm, actionArgs, vim) {
+         var lastEditInputState = vim.lastEditInputState;
+diff --git a/devtools/client/sourceeditor/codemirror/lib/codemirror.css b/devtools/client/sourceeditor/codemirror/lib/codemirror.css
+--- a/devtools/client/sourceeditor/codemirror/lib/codemirror.css
++++ b/devtools/client/sourceeditor/codemirror/lib/codemirror.css
+@@ -265,17 +265,17 @@ div.CodeMirror span.CodeMirror-nonmatchi
+   position: absolute;
+   left: 0; right: 0; top: 0; bottom: 0;
+   z-index: 0;
+ }
+ 
+ .CodeMirror-linewidget {
+   position: relative;
+   z-index: 2;
+-  overflow: auto;
++  padding: 0.1px; /* Force widget margins to stay inside of the container */
+ }
+ 
+ .CodeMirror-widget {}
+ 
+ .CodeMirror-rtl pre { direction: rtl; }
+ 
+ .CodeMirror-code {
+   outline: none;
+diff --git a/devtools/client/sourceeditor/codemirror/lib/codemirror.js b/devtools/client/sourceeditor/codemirror/lib/codemirror.js
+--- a/devtools/client/sourceeditor/codemirror/lib/codemirror.js
++++ b/devtools/client/sourceeditor/codemirror/lib/codemirror.js
+@@ -7601,16 +7601,17 @@ function defineOptions(CodeMirror) {
+   option("indentUnit", 2, loadMode, true)
+   option("indentWithTabs", false)
+   option("smartIndent", true)
+   option("tabSize", 4, function (cm) {
+     resetModeState(cm)
+     clearCaches(cm)
+     regChange(cm)
+   }, true)
++
+   option("lineSeparator", null, function (cm, val) {
+     cm.doc.lineSep = val
+     if (!val) { return }
+     var newBreaks = [], lineNo = cm.doc.first
+     cm.doc.iter(function (line) {
+       for (var pos = 0;;) {
+         var found = line.text.indexOf(val, pos)
+         if (found == -1) { break }
+@@ -9641,13 +9642,13 @@ CodeMirror.defineExtension = function (n
+ CodeMirror.defineDocExtension = function (name, func) {
+   Doc.prototype[name] = func
+ }
+ 
+ CodeMirror.fromTextArea = fromTextArea
+ 
+ addLegacyProps(CodeMirror)
+ 
+-CodeMirror.version = "5.32.0"
++CodeMirror.version = "5.33.0"
+ 
+ return CodeMirror;
+ 
+ })));
+\ No newline at end of file
+diff --git a/devtools/client/sourceeditor/codemirror/mode/clike/clike.js b/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
+--- a/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
++++ b/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
+@@ -484,16 +484,37 @@ CodeMirror.defineMode("clike", function(
+         state.tokenize = null;
+         break;
+       }
+       escaped = stream.next() == "\\" && !escaped;
+     }
+     return "string";
+   }
+ 
++  function tokenNestedComment(depth) {
++    return function (stream, state) {
++      var ch
++      while (ch = stream.next()) {
++        if (ch == "*" && stream.eat("/")) {
++          if (depth == 1) {
++            state.tokenize = null
++            break
++          } else {
++            state.tokenize = tokenNestedComment(depth - 1)
++            return state.tokenize(stream, state)
++          }
++        } else if (ch == "/" && stream.eat("*")) {
++          state.tokenize = tokenNestedComment(depth + 1)
++          return state.tokenize(stream, state)
++        }
++      }
++      return "comment"
++    }
++  }
++
+   def("text/x-scala", {
+     name: "clike",
+     keywords: words(
+ 
+       /* scala */
+       "abstract case catch class def do else extends final finally for forSome if " +
+       "implicit import lazy match new null object override package private protected return " +
+       "sealed super this throw trait try type val var while with yield _ " +
+@@ -539,16 +560,22 @@ CodeMirror.defineMode("clike", function(
+       "=": function(stream, state) {
+         var cx = state.context
+         if (cx.type == "}" && cx.align && stream.eat(">")) {
+           state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)
+           return "operator"
+         } else {
+           return false
+         }
++      },
++
++      "/": function(stream, state) {
++        if (!stream.eat("*")) return false
++        state.tokenize = tokenNestedComment(1)
++        return state.tokenize(stream, state)
+       }
+     },
+     modeProps: {closeBrackets: {triples: '"'}}
+   });
+ 
+   function tokenKotlinString(tripleString){
+     return function (stream, state) {
+       var escaped = false, next, end = false;
+@@ -573,17 +600,17 @@ CodeMirror.defineMode("clike", function(
+       "package as typealias class interface this super val " +
+       "var fun for is in This throw return " +
+       "break continue object if else while do try when !in !is as? " +
+ 
+       /*soft keywords*/
+       "file import where by get set abstract enum open inner override private public internal " +
+       "protected catch finally out final vararg reified dynamic companion constructor init " +
+       "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
+-      "external annotation crossinline const operator infix suspend"
++      "external annotation crossinline const operator infix suspend actual expect"
+     ),
+     types: words(
+       /* package java.lang */
+       "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+       "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+       "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+       "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
+     ),
+diff --git a/devtools/client/sourceeditor/codemirror/mode/css/css.js b/devtools/client/sourceeditor/codemirror/mode/css/css.js
+--- a/devtools/client/sourceeditor/codemirror/mode/css/css.js
++++ b/devtools/client/sourceeditor/codemirror/mode/css/css.js
+@@ -72,19 +72,19 @@ CodeMirror.defineMode("css", function(co
+         return ret("meta", "meta");
+       }
+     } else if (/[,+>*\/]/.test(ch)) {
+       return ret(null, "select-op");
+     } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
+       return ret("qualifier", "qualifier");
+     } else if (/[:;{}\[\]\(\)]/.test(ch)) {
+       return ret(null, ch);
+-    } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
+-               (ch == "d" && stream.match("omain(")) ||
+-               (ch == "r" && stream.match("egexp("))) {
++    } else if (((ch == "u" || ch == "U") && stream.match(/rl(-prefix)?\(/i)) ||
++               ((ch == "d" || ch == "D") && stream.match("omain(", true, true)) ||
++               ((ch == "r" || ch == "R") && stream.match("egexp(", true, true))) {
+       stream.backUp(1);
+       state.tokenize = tokenParenthesized;
+       return ret("property", "word");
+     } else if (/[\w\\\-]/.test(ch)) {
+       stream.eatWhile(/[\w\\\-]/);
+       return ret("property", "word");
+     } else {
+       return ret(null, null);
+@@ -157,26 +157,26 @@ CodeMirror.defineMode("css", function(co
+ 
+   var states = {};
+ 
+   states.top = function(type, stream, state) {
+     if (type == "{") {
+       return pushContext(state, stream, "block");
+     } else if (type == "}" && state.context.prev) {
+       return popContext(state);
+-    } else if (supportsAtComponent && /@component/.test(type)) {
++    } else if (supportsAtComponent && /@component/i.test(type)) {
+       return pushContext(state, stream, "atComponentBlock");
+-    } else if (/^@(-moz-)?document$/.test(type)) {
++    } else if (/^@(-moz-)?document$/i.test(type)) {
+       return pushContext(state, stream, "documentTypes");
+-    } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
++    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
+       return pushContext(state, stream, "atBlock");
+-    } else if (/^@(font-face|counter-style)/.test(type)) {
++    } else if (/^@(font-face|counter-style)/i.test(type)) {
+       state.stateArg = type;
+       return "restricted_atBlock_before";
+-    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
++    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
+       return "keyframes";
+     } else if (type && type.charAt(0) == "@") {
+       return pushContext(state, stream, "at");
+     } else if (type == "hash") {
+       override = "builtin";
+     } else if (type == "word") {
+       override = "tag";
+     } else if (type == "variable-definition") {
+@@ -788,17 +788,17 @@ CodeMirror.defineMode("css", function(co
+           state.tokenize = tokenCComment;
+           return tokenCComment(stream, state);
+         } else {
+           return ["operator", "operator"];
+         }
+       },
+       "@": function(stream) {
+         if (stream.eat("{")) return [null, "interpolation"];
+-        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
++        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
+         stream.eatWhile(/[\w\\\-]/);
+         if (stream.match(/^\s*:/, false))
+           return ["variable-2", "variable-definition"];
+         return ["variable-2", "variable"];
+       },
+       "&": function() {
+         return ["atom", "atom"];
+       }
+diff --git a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
+--- a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
++++ b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
+@@ -21,55 +21,28 @@ CodeMirror.defineMode("javascript", func
+ 
+   // Tokenizer
+ 
+   var keywords = function(){
+     function kw(type) {return {type: type, style: "keyword"};}
+     var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
+     var operator = kw("operator"), atom = {type: "atom", style: "atom"};
+ 
+-    var jsKeywords = {
++    return {
+       "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
+       "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
+       "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
+       "function": kw("function"), "catch": kw("catch"),
+       "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
+       "in": operator, "typeof": operator, "instanceof": operator,
+       "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
+       "this": kw("this"), "class": kw("class"), "super": kw("atom"),
+       "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
+       "await": C
+     };
+-
+-    // Extend the 'normal' keywords with the TypeScript language extensions
+-    if (isTS) {
+-      var type = {type: "variable", style: "type"};
+-      var tsKeywords = {
+-        // object-like things
+-        "interface": kw("class"),
+-        "implements": C,
+-        "namespace": C,
+-
+-        // scope modifiers
+-        "public": kw("modifier"),
+-        "private": kw("modifier"),
+-        "protected": kw("modifier"),
+-        "abstract": kw("modifier"),
+-        "readonly": kw("modifier"),
+-
+-        // types
+-        "string": type, "number": type, "boolean": type, "any": type
+-      };
+-
+-      for (var attr in tsKeywords) {
+-        jsKeywords[attr] = tsKeywords[attr];
+-      }
+-    }
+-
+-    return jsKeywords;
+   }();
+ 
+   var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
+   var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
+ 
+   function readRegexp(stream) {
+     var escaped = false, next, inSet = false;
+     while ((next = stream.next()) != null) {
+@@ -305,16 +278,20 @@ CodeMirror.defineMode("javascript", func
+       state.localVars = {name: varname, next: state.localVars};
+     } else {
+       if (inList(state.globalVars)) return;
+       if (parserConfig.globalVars)
+         state.globalVars = {name: varname, next: state.globalVars};
+     }
+   }
+ 
++  function isModifier(name) {
++    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
++  }
++
+   // Combinators
+ 
+   var defaultVars = {name: "this", next: {name: "arguments"}};
+   function pushcontext() {
+     cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
+     cx.state.localVars = defaultVars;
+   }
+   function popcontext() {
+@@ -361,64 +338,67 @@ CodeMirror.defineMode("javascript", func
+     if (type == ";") return cont();
+     if (type == "if") {
+       if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
+         cx.state.cc.pop()();
+       return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
+     }
+     if (type == "function") return cont(functiondef);
+     if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
++    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); }
+     if (type == "variable") {
+       if (isTS && value == "type") {
+         cx.marked = "keyword"
+         return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
+       } else if (isTS && value == "declare") {
+         cx.marked = "keyword"
+         return cont(statement)
+       } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) {
+         cx.marked = "keyword"
+         return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
++      } else if (isTS && value == "namespace") {
++        cx.marked = "keyword"
++        return cont(pushlex("form"), expression, block, poplex)
+       } else {
+         return cont(pushlex("stat"), maybelabel);
+       }
+     }
+     if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
+                                       block, poplex, poplex);
+     if (type == "case") return cont(expression, expect(":"));
+     if (type == "default") return cont(expect(":"));
+     if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
+                                      statement, poplex, popcontext);
+-    if (type == "class") return cont(pushlex("form"), className, poplex);
+     if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
+     if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
+     if (type == "async") return cont(statement)
+     if (value == "@") return cont(expression, statement)
+     return pass(pushlex("stat"), expression, expect(";"), poplex);
+   }
+-  function expression(type) {
+-    return expressionInner(type, false);
++  function expression(type, value) {
++    return expressionInner(type, value, false);
+   }
+-  function expressionNoComma(type) {
+-    return expressionInner(type, true);
++  function expressionNoComma(type, value) {
++    return expressionInner(type, value, true);
+   }
+   function parenExpr(type) {
+     if (type != "(") return pass()
+     return cont(pushlex(")"), expression, expect(")"), poplex)
+   }
+-  function expressionInner(type, noComma) {
++  function expressionInner(type, value, noComma) {
+     if (cx.state.fatArrowAt == cx.stream.start) {
+       var body = noComma ? arrowBodyNoComma : arrowBody;
+       if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
+       else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
+     }
+ 
+     var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
+     if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
+     if (type == "function") return cont(functiondef, maybeop);
+-    if (type == "class") return cont(pushlex("form"), classExpression, poplex);
++    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
+     if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
+     if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
+     if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
+     if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
+     if (type == "{") return contCommasep(objprop, "}", null, maybeop);
+     if (type == "quasi") return pass(quasi, maybeop);
+     if (type == "new") return cont(maybeTarget(noComma));
+     return cont();
+@@ -506,20 +486,21 @@ CodeMirror.defineMode("javascript", func
+       if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
+         cx.state.fatArrowAt = cx.stream.pos + m[0].length
+       return cont(afterprop);
+     } else if (type == "number" || type == "string") {
+       cx.marked = jsonldMode ? "property" : (cx.style + " property");
+       return cont(afterprop);
+     } else if (type == "jsonld-keyword") {
+       return cont(afterprop);
+-    } else if (type == "modifier") {
++    } else if (isTS && isModifier(value)) {
++      cx.marked = "keyword"
+       return cont(objprop)
+     } else if (type == "[") {
+-      return cont(expression, expect("]"), afterprop);
++      return cont(expression, maybetype, expect("]"), afterprop);
+     } else if (type == "spread") {
+       return cont(expressionNoComma, afterprop);
+     } else if (value == "*") {
+       cx.marked = "keyword";
+       return cont(objprop);
+     } else if (type == ":") {
+       return pass(afterprop)
+     }
+@@ -611,32 +592,32 @@ CodeMirror.defineMode("javascript", func
+   function typearg(type) {
+     if (type == "variable") return cont(typearg)
+     else if (type == ":") return cont(typeexpr)
+   }
+   function afterType(type, value) {
+     if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+     if (value == "|" || type == ".") return cont(typeexpr)
+     if (type == "[") return cont(expect("]"), afterType)
+-    if (value == "extends") return cont(typeexpr)
++    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
+   }
+   function maybeTypeArgs(_, value) {
+     if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+   }
+   function typeparam() {
+     return pass(typeexpr, maybeTypeDefault)
+   }
+   function maybeTypeDefault(_, value) {
+     if (value == "=") return cont(typeexpr)
+   }
+   function vardef() {
+     return pass(pattern, maybetype, maybeAssign, vardefCont);
+   }
+   function pattern(type, value) {
+-    if (type == "modifier") return cont(pattern)
++    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
+     if (type == "variable") { register(value); return cont(); }
+     if (type == "spread") return cont(pattern);
+     if (type == "[") return contCommasep(pattern, "]");
+     if (type == "{") return contCommasep(proppattern, "}");
+   }
+   function proppattern(type, value) {
+     if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
+       register(value);
+@@ -680,17 +661,18 @@ CodeMirror.defineMode("javascript", func
+   function functiondef(type, value) {
+     if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
+     if (type == "variable") {register(value); return cont(functiondef);}
+     if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
+     if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
+   }
+   function funarg(type, value) {
+     if (value == "@") cont(expression, funarg)
+-    if (type == "spread" || type == "modifier") return cont(funarg);
++    if (type == "spread") return cont(funarg);
++    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
+     return pass(pattern, maybetype, maybeAssign);
+   }
+   function classExpression(type, value) {
+     // Class expressions may have an optional name.
+     if (type == "variable") return className(type, value);
+     return classNameAfter(type, value);
+   }
+   function className(type, value) {
+@@ -698,29 +680,29 @@ CodeMirror.defineMode("javascript", func
+   }
+   function classNameAfter(type, value) {
+     if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
+     if (value == "extends" || value == "implements" || (isTS && type == ","))
+       return cont(isTS ? typeexpr : expression, classNameAfter);
+     if (type == "{") return cont(pushlex("}"), classBody, poplex);
+   }
+   function classBody(type, value) {
+-    if (type == "modifier" || type == "async" ||
++    if (type == "async" ||
+         (type == "variable" &&
+-         (value == "static" || value == "get" || value == "set") &&
++         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
+          cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
+       cx.marked = "keyword";
+       return cont(classBody);
+     }
+     if (type == "variable" || cx.style == "keyword") {
+       cx.marked = "property";
+       return cont(isTS ? classfield : functiondef, classBody);
+     }
+     if (type == "[")
+-      return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody)
++      return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
+     if (value == "*") {
+       cx.marked = "keyword";
+       return cont(classBody);
+     }
+     if (type == ";") return cont(classBody);
+     if (type == "}") return cont();
+     if (value == "@") return cont(expression, classBody)
+   }
+diff --git a/devtools/client/sourceeditor/codemirror/mode/jsx/jsx.js b/devtools/client/sourceeditor/codemirror/mode/jsx/jsx.js
+--- a/devtools/client/sourceeditor/codemirror/mode/jsx/jsx.js
++++ b/devtools/client/sourceeditor/codemirror/mode/jsx/jsx.js
+@@ -21,17 +21,17 @@
+   function copyContext(context) {
+     return new Context(CodeMirror.copyState(context.mode, context.state),
+                        context.mode,
+                        context.depth,
+                        context.prev && copyContext(context.prev))
+   }
+ 
+   CodeMirror.defineMode("jsx", function(config, modeConfig) {
+-    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false})
++    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false, allowMissingTagName: true})
+     var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || "javascript")
+ 
+     function flatXMLIndent(state) {
+       var tagName = state.tagName
+       state.tagName = null
+       var result = xmlMode.indent(state, "")
+       state.tagName = tagName
+       return result
+diff --git a/devtools/client/sourceeditor/codemirror/mode/xml/xml.js b/devtools/client/sourceeditor/codemirror/mode/xml/xml.js
+--- a/devtools/client/sourceeditor/codemirror/mode/xml/xml.js
++++ b/devtools/client/sourceeditor/codemirror/mode/xml/xml.js
+@@ -47,16 +47,17 @@ var htmlConfig = {
+ 
+ var xmlConfig = {
+   autoSelfClosers: {},
+   implicitlyClosed: {},
+   contextGrabbers: {},
+   doNotIndent: {},
+   allowUnquoted: false,
+   allowMissing: false,
++  allowMissingTagName: false,
+   caseFold: false
+ }
+ 
+ CodeMirror.defineMode("xml", function(editorConf, config_) {
+   var indentUnit = editorConf.indentUnit
+   var config = {}
+   var defaults = config_.htmlMode ? htmlConfig : xmlConfig
+   for (var prop in defaults) config[prop] = defaults[prop]
+@@ -221,16 +222,19 @@ CodeMirror.defineMode("xml", function(ed
+       return baseState;
+     }
+   }
+   function tagNameState(type, stream, state) {
+     if (type == "word") {
+       state.tagName = stream.current();
+       setStyle = "tag";
+       return attrState;
++    } else if (config.allowMissingTagName && type == "endTag") {
++      setStyle = "tag bracket";
++      return attrState(type, stream, state);
+     } else {
+       setStyle = "error";
+       return tagNameState;
+     }
+   }
+   function closeTagNameState(type, stream, state) {
+     if (type == "word") {
+       var tagName = stream.current();
+@@ -239,16 +243,19 @@ CodeMirror.defineMode("xml", function(ed
+         popContext(state);
+       if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
+         setStyle = "tag";
+         return closeState;
+       } else {
+         setStyle = "tag error";
+         return closeStateErr;
+       }
++    } else if (config.allowMissingTagName && type == "endTag") {
++      setStyle = "tag bracket";
++      return closeState(type, stream, state);
+     } else {
+       setStyle = "error";
+       return closeStateErr;
+     }
+   }
+ 
+   function closeState(type, _stream, state) {
+     if (type != "endTag") {
+diff --git a/devtools/client/sourceeditor/test/codemirror/mode_test.js b/devtools/client/sourceeditor/test/codemirror/mode_test.js
+--- a/devtools/client/sourceeditor/test/codemirror/mode_test.js
++++ b/devtools/client/sourceeditor/test/codemirror/mode_test.js
+@@ -62,17 +62,17 @@
+   test.mode = function(name, mode, tokens, modeName) {
+     var data = parseTokens(tokens);
+     return test((modeName || mode.name) + "_" + name, function() {
+       return compare(data.plain, data.tokens, mode);
+     });
+   };
+ 
+   function esc(str) {
+-    return str.replace('&', '&amp;').replace('<', '&lt;').replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
++    return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
+   }
+ 
+   function compare(text, expected, mode) {
+ 
+     var expectedOutput = [];
+     for (var i = 0; i < expected.length; ++i) {
+       var sty = expected[i].style;
+       if (sty && sty.indexOf(" ")) sty = sty.split(' ').sort().join(' ');
+diff --git a/devtools/client/sourceeditor/test/codemirror/sublime_test.js b/devtools/client/sourceeditor/test/codemirror/sublime_test.js
+--- a/devtools/client/sourceeditor/test/codemirror/sublime_test.js
++++ b/devtools/client/sourceeditor/test/codemirror/sublime_test.js
+@@ -147,17 +147,19 @@
+   stTest("selectScope", "foo(a) {\n  bar[1, 2];\n}",
+          "selectScope", hasSel(0, 0, 2, 1),
+          Pos(0, 4), "selectScope", hasSel(0, 4, 0, 5),
+          Pos(0, 5), "selectScope", hasSel(0, 4, 0, 5),
+          Pos(0, 6), "selectScope", hasSel(0, 0, 2, 1),
+          Pos(0, 8), "selectScope", hasSel(0, 8, 2, 0),
+          Pos(1, 2), "selectScope", hasSel(0, 8, 2, 0),
+          Pos(1, 6), "selectScope", hasSel(1, 6, 1, 10),
+-         Pos(1, 9), "selectScope", hasSel(1, 6, 1, 10));
++         Pos(1, 9), "selectScope", hasSel(1, 6, 1, 10),
++         "selectScope", hasSel(0, 8, 2, 0),
++         "selectScope", hasSel(0, 0, 2, 1));
+ 
+   stTest("goToBracket", "foo(a) {\n  bar[1, 2];\n}",
+          Pos(0, 0), "goToBracket", at(0, 0),
+          Pos(0, 4), "goToBracket", at(0, 5), "goToBracket", at(0, 4),
+          Pos(0, 8), "goToBracket", at(2, 0), "goToBracket", at(0, 8),
+          Pos(1, 2), "goToBracket", at(2, 0),
+          Pos(1, 7), "goToBracket", at(1, 10), "goToBracket", at(1, 6));
+ 
+@@ -214,41 +216,16 @@
+          setSel(0, 1, 0, 1,
+                 0, 2, 0, 4,
+                 0, 5, 0, 5),
+          "duplicateLine",
+          val("abcdef\nabcdcdef\nabcdcdef"), hasSel(2, 1, 2, 1,
+                                                    2, 4, 2, 6,
+                                                    2, 7, 2, 7));
+ 
+-  stTest("selectLinesUpward", "123\n345\n789\n012",
+-         setSel(0, 1, 0, 1,
+-                1, 1, 1, 3,
+-                2, 0, 2, 0,
+-                3, 0, 3, 0),
+-         "selectLinesUpward",
+-         hasSel(0, 1, 0, 1,
+-                0, 3, 0, 3,
+-                1, 0, 1, 0,
+-                1, 1, 1, 3,
+-                2, 0, 2, 0,
+-                3, 0, 3, 0));
+-
+-  stTest("selectLinesDownward", "123\n345\n789\n012",
+-         setSel(0, 1, 0, 1,
+-                1, 1, 1, 3,
+-                2, 0, 2, 0,
+-                3, 0, 3, 0),
+-         "selectLinesDownward",
+-         hasSel(0, 1, 0, 1,
+-                1, 1, 1, 3,
+-                2, 0, 2, 0,
+-                2, 3, 2, 3,
+-                3, 0, 3, 0));
+-
+   stTest("sortLines", "c\nb\na\nC\nB\nA",
+          "sortLines", val("A\nB\nC\na\nb\nc"),
+          "undo",
+          setSel(0, 0, 2, 0,
+                 3, 0, 5, 0),
+          "sortLines", val("b\nc\na\nB\nC\nA"),
+          hasSel(0, 0, 2, 0,
+                 3, 0, 5, 0),
+diff --git a/devtools/client/sourceeditor/test/codemirror/vim_test.js b/devtools/client/sourceeditor/test/codemirror/vim_test.js
+--- a/devtools/client/sourceeditor/test/codemirror/vim_test.js
++++ b/devtools/client/sourceeditor/test/codemirror/vim_test.js
+@@ -4230,9 +4230,255 @@ testVim('ex_map_key2key_from_colon', fun
+ }, { value: 'abc' });
+ 
+ // Test event handlers
+ testVim('beforeSelectionChange', function(cm, vim, helpers) {
+   cm.setCursor(0, 100);
+   eqCursorPos(cm.getCursor('head'), cm.getCursor('anchor'));
+ }, { value: 'abc' });
+ 
++testVim('increment_binary', function(cm, vim, helpers) {
++  cm.setCursor(0, 4);
++  helpers.doKeys('<C-a>');
++  eq('0b001', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0b010', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0b001', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0b000', cm.getValue());
++  cm.setCursor(0, 0);
++  helpers.doKeys('<C-a>');
++  eq('0b001', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0b010', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0b001', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0b000', cm.getValue());
++}, { value: '0b000' });
+ 
++testVim('increment_octal', function(cm, vim, helpers) {
++  cm.setCursor(0, 2);
++  helpers.doKeys('<C-a>');
++  eq('001', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('002', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('003', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('004', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('005', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('006', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('007', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('010', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('007', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('006', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('005', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('004', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('003', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('002', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('001', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('000', cm.getValue());
++  cm.setCursor(0, 0);
++  helpers.doKeys('<C-a>');
++  eq('001', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('002', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('001', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('000', cm.getValue());
++}, { value: '000' });
++
++testVim('increment_decimal', function(cm, vim, helpers) {
++  cm.setCursor(0, 2);
++  helpers.doKeys('<C-a>');
++  eq('101', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('102', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('103', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('104', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('105', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('106', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('107', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('108', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('109', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('110', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('109', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('108', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('107', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('106', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('105', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('104', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('103', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('102', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('101', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('100', cm.getValue());
++  cm.setCursor(0, 0);
++  helpers.doKeys('<C-a>');
++  eq('101', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('102', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('101', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('100', cm.getValue());
++}, { value: '100' });
++
++testVim('increment_decimal_single_zero', function(cm, vim, helpers) {
++  helpers.doKeys('<C-a>');
++  eq('1', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('2', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('3', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('4', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('5', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('6', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('7', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('8', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('9', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('10', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('9', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('8', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('7', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('6', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('5', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('4', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('3', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('2', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('1', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0', cm.getValue());
++  cm.setCursor(0, 0);
++  helpers.doKeys('<C-a>');
++  eq('1', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('2', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('1', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0', cm.getValue());
++}, { value: '0' });
++
++testVim('increment_hexadecimal', function(cm, vim, helpers) {
++  cm.setCursor(0, 2);
++  helpers.doKeys('<C-a>');
++  eq('0x1', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0x2', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0x3', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0x4', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0x5', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0x6', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0x7', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0x8', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0x9', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0xa', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0xb', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0xc', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0xd', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0xe', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0xf', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0x10', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x0f', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x0e', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x0d', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x0c', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x0b', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x0a', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x09', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x08', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x07', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x06', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x05', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x04', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x03', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x02', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x01', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x00', cm.getValue());
++  cm.setCursor(0, 0);
++  helpers.doKeys('<C-a>');
++  eq('0x01', cm.getValue());
++  helpers.doKeys('<C-a>');
++  eq('0x02', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x01', cm.getValue());
++  helpers.doKeys('<C-x>');
++  eq('0x00', cm.getValue());
++}, { value: '0x0' });

+ 31 - 0
frg/work-js/mozilla-release/patches/1429271-59a1.patch

@@ -0,0 +1,31 @@
+# HG changeset patch
+# User Oriol Brufau <oriol-bugzilla@hotmail.com>
+# Date 1515598394 -3600
+# Node ID b0febafcec421eb8c8fc2e067303882f5c72750c
+# Parent  3d13e0a8d5bcca99b87f02759380c84ca9c66d6d
+Bug 1429271 - Use 'is' to compare so that the obtained value is logged when the assert fails. r=Honza
+
+MozReview-Commit-ID: 6A87wJfrC8U
+
+diff --git a/devtools/client/jsonview/test/browser_jsonview_slash.js b/devtools/client/jsonview/test/browser_jsonview_slash.js
+--- a/devtools/client/jsonview/test/browser_jsonview_slash.js
++++ b/devtools/client/jsonview/test/browser_jsonview_slash.js
+@@ -1,16 +1,16 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ add_task(function* () {
+-  info("Test JSON with NUL started.");
++  info("Test JSON with slash started.");
+ 
+   const TEST_JSON_URL = "data:application/json,{\"a/b\":[1,2],\"a\":{\"b\":[3,4]}}";
+   yield addJsonViewTab(TEST_JSON_URL);
+ 
+   let countBefore = yield getElementCount(".jsonPanelBox .treeTable .treeRow");
+-  ok(countBefore == 7, "There must be seven rows");
++  is(countBefore, 7, "There must be seven rows");
+ });

+ 48 - 0
frg/work-js/mozilla-release/patches/1433929-1-60a1.patch

@@ -0,0 +1,48 @@
+# HG changeset patch
+# User Frederik Braun <fbraun@mozilla.com>
+# Date 1518710812 -3600
+# Node ID 2459452c3072b53c3f7db371d8b9b7d74b72f287
+# Parent  2672c2aff3f87edf6b7b5d6747e79c13a5c2a11d
+Bug 1433929 - Drop forms when sanitizing HTML fragments for chrome-privileged documents. r=bz
+
+MozReview-Commit-ID: 10q5eymtmU0
+
+diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
+--- a/dom/base/nsContentUtils.cpp
++++ b/dom/base/nsContentUtils.cpp
+@@ -5304,16 +5304,17 @@ nsContentUtils::ParseFragmentHTML(const 
+   NS_ENSURE_SUCCESS(rv, rv);
+ 
+   if (fragment) {
+     // Don't fire mutation events for nodes removed by the sanitizer.
+     nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
+ 
+     nsTreeSanitizer sanitizer(nsIParserUtils::SanitizerAllowStyle |
+                               nsIParserUtils::SanitizerAllowComments |
++                              nsIParserUtils::SanitizerDropForms |
+                               nsIParserUtils::SanitizerLogRemovals);
+     sanitizer.Sanitize(fragment);
+ 
+     ErrorResult error;
+     aTargetNode->AppendChild(*fragment, error);
+     rv = error.StealNSResult();
+   }
+ 
+@@ -5398,16 +5399,17 @@ nsContentUtils::ParseFragmentXML(const n
+   if (aSanitize != NeverSanitize && !aDocument->AllowUnsafeHTML()) {
+     // Don't fire mutation events for nodes removed by the sanitizer.
+     nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
+ 
+     RefPtr<DocumentFragment> fragment = static_cast<DocumentFragment*>(*aReturn);
+ 
+     nsTreeSanitizer sanitizer(nsIParserUtils::SanitizerAllowStyle |
+                               nsIParserUtils::SanitizerAllowComments |
++                              nsIParserUtils::SanitizerDropForms |
+                               nsIParserUtils::SanitizerLogRemovals);
+     sanitizer.Sanitize(fragment);
+   }
+ 
+   return rv;
+ }
+ 
+ /* static */

+ 890 - 0
frg/work-js/mozilla-release/patches/1433929-2-60a1.patch

@@ -0,0 +1,890 @@
+# HG changeset patch
+# User Frederik Braun <fbraun@mozilla.com>
+# Date 1518710818 -3600
+# Node ID 746c468466f7f53353a25d7a4ff6666fef8275e5
+# Parent  3dba4355f0cd72bfca9af377405eea0549412031
+Bug 1433929 - Use createElement style with DevTools forms. r=jryans
+
+MozReview-Commit-ID: K9RQyYUrdE8
+
+diff --git a/devtools/client/shared/test/browser_html_tooltip-03.js b/devtools/client/shared/test/browser_html_tooltip-03.js
+--- a/devtools/client/shared/test/browser_html_tooltip-03.js
++++ b/devtools/client/shared/test/browser_html_tooltip-03.js
+@@ -126,13 +126,16 @@ function blurNode(doc, selector) {
+  * @return {Promise} promise that will resolve the HTMLTooltip instance created when the
+  *         tooltip content will be ready.
+  */
+ function* createTooltip(doc, autofocus) {
+   let tooltip = new HTMLTooltip(doc, {autofocus, useXulWrapper});
+   let div = doc.createElementNS(HTML_NS, "div");
+   div.classList.add("tooltip-content");
+   div.style.height = "50px";
+-  div.innerHTML = '<input type="text"></input>';
++
++  let input = doc.createElementNS(HTML_NS, "input");
++  input.setAttribute("type", "text");
++  div.appendChild(input);
+ 
+   tooltip.setContent(div, {width: 150, height: 50});
+   return tooltip;
+ }
+diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/shared/widgets/FilterWidget.js
+--- a/devtools/client/shared/widgets/FilterWidget.js
++++ b/devtools/client/shared/widgets/FilterWidget.js
+@@ -150,62 +150,102 @@ function CSSFilterEditorWidget(el, value
+   this.setCssValue(value);
+   this.renderPresets();
+ }
+ 
+ exports.CSSFilterEditorWidget = CSSFilterEditorWidget;
+ 
+ CSSFilterEditorWidget.prototype = {
+   _initMarkup: function () {
+-    let filterListSelectPlaceholder =
+-      L10N.getStr("filterListSelectPlaceholder");
+-    let addNewFilterButton = L10N.getStr("addNewFilterButton");
+-    let presetsToggleButton = L10N.getStr("presetsToggleButton");
+-    let newPresetPlaceholder = L10N.getStr("newPresetPlaceholder");
+-    let savePresetButton = L10N.getStr("savePresetButton");
++    // The following structure is created:
++    //   <div class="filters-list">
++    //     <div id="filters"></div>
++    //     <div class="footer">
++    //       <select value="">
++    //         <option value="">${filterListSelectPlaceholder}</option>
++    //       </select>
++    //       <button id="add-filter" class="add">${addNewFilterButton}</button>
++    //       <button id="toggle-presets">${presetsToggleButton}</button>
++    //     </div>
++    //   </div>
++    //   <div class="presets-list">
++    //     <div id="presets"></div>
++    //     <div class="footer">
++    //       <input value="" class="devtools-textinput"
++    //              placeholder="${newPresetPlaceholder}"></input>
++    //       <button class="add">${savePresetButton}</button>
++    //     </div>
++    //   </div>
++    let content = this.doc.createDocumentFragment();
++
++    let filterListWrapper = this.doc.createElementNS(XHTML_NS, "div");
++    filterListWrapper.classList.add("filters-list");
++    content.appendChild(filterListWrapper);
++
++    this.filterList = this.doc.createElementNS(XHTML_NS, "div");
++    this.filterList.setAttribute("id", "filters");
++    filterListWrapper.appendChild(this.filterList);
++
++    let filterListFooter = this.doc.createElementNS(XHTML_NS, "div");
++    filterListFooter.classList.add("footer");
++    filterListWrapper.appendChild(filterListFooter);
++
++    this.filterSelect = this.doc.createElementNS(XHTML_NS, "select");
++    this.filterSelect.setAttribute("value", "");
++    filterListFooter.appendChild(this.filterSelect);
+ 
+-    // eslint-disable-next-line no-unsanitized/property
+-    this.el.innerHTML = `
+-      <div class="filters-list">
+-        <div id="filters"></div>
+-        <div class="footer">
+-          <select value="">
+-            <option value="">${filterListSelectPlaceholder}</option>
+-          </select>
+-          <button id="add-filter" class="add">${addNewFilterButton}</button>
+-          <button id="toggle-presets">${presetsToggleButton}</button>
+-        </div>
+-      </div>
++    let filterListPlaceholder = this.doc.createElementNS(XHTML_NS, "option");
++    filterListPlaceholder.setAttribute("value", "");
++    filterListPlaceholder.textContent = L10N.getStr("filterListSelectPlaceholder");
++    this.filterSelect.appendChild(filterListPlaceholder);
++
++    let addFilter = this.doc.createElementNS(XHTML_NS, "button");
++    addFilter.setAttribute("id", "add-filter");
++    addFilter.classList.add("add");
++    addFilter.textContent = L10N.getStr("addNewFilterButton");
++    filterListFooter.appendChild(addFilter);
++
++    this.togglePresets = this.doc.createElementNS(XHTML_NS, "button");
++    this.togglePresets.setAttribute("id", "toggle-presets");
++    this.togglePresets.textContent = L10N.getStr("presetsToggleButton");
++    filterListFooter.appendChild(this.togglePresets);
++
++    let presetListWrapper = this.doc.createElementNS(XHTML_NS, "div");
++    presetListWrapper.classList.add("presets-list");
++    content.appendChild(presetListWrapper);
+ 
+-      <div class="presets-list">
+-        <div id="presets"></div>
+-        <div class="footer">
+-          <input value="" class="devtools-textinput"
+-                 placeholder="${newPresetPlaceholder}"></input>
+-          <button class="add">${savePresetButton}</button>
+-        </div>
+-      </div>
+-    `;
+-    this.filtersList = this.el.querySelector("#filters");
+-    this.presetsList = this.el.querySelector("#presets");
+-    this.togglePresets = this.el.querySelector("#toggle-presets");
+-    this.filterSelect = this.el.querySelector("select");
+-    this.addPresetButton = this.el.querySelector(".presets-list .add");
+-    this.addPresetInput = this.el.querySelector(".presets-list .footer input");
++    this.presetList = this.doc.createElementNS(XHTML_NS, "div");
++    this.presetList.setAttribute("id", "presets");
++    presetListWrapper.appendChild(this.presetList);
++
++    let presetListFooter = this.doc.createElementNS(XHTML_NS, "div");
++    presetListFooter.classList.add("footer");
++    presetListWrapper.appendChild(presetListFooter);
+ 
+-    this.el.querySelector(".presets-list input").value = "";
++    this.addPresetInput = this.doc.createElementNS(XHTML_NS, "input");
++    this.addPresetInput.setAttribute("value", "");
++    this.addPresetInput.classList.add("devtools-textinput");
++    this.addPresetInput.setAttribute("placeholder", L10N.getStr("newPresetPlaceholder"));
++    presetListFooter.appendChild(this.addPresetInput);
++
++    this.addPresetButton = this.doc.createElementNS(XHTML_NS, "button");
++    this.addPresetButton.classList.add("add");
++    this.addPresetButton.textContent = L10N.getStr("savePresetButton");
++    presetListFooter.appendChild(this.addPresetButton);
++
++    this.el.appendChild(content);
+ 
+     this._populateFilterSelect();
+   },
+ 
+   _destroyMarkup: function () {
+     this._filterItemMarkup.remove();
+     this.el.remove();
+-    this.el = this.filtersList = this._filterItemMarkup = null;
+-    this.presetsList = this.togglePresets = this.filterSelect = null;
++    this.el = this.filterList = this._filterItemMarkup = null;
++    this.presetList = this.togglePresets = this.filterSelect = null;
+     this.addPresetButton = null;
+   },
+ 
+   destroy: function () {
+     this._removeEventListeners();
+     this._destroyMarkup();
+   },
+ 
+@@ -277,55 +317,55 @@ CSSFilterEditorWidget.prototype = {
+     base.appendChild(removeButton);
+ 
+     this._presetItemMarkup = base;
+   },
+ 
+   _addEventListeners: function () {
+     this.addButton = this.el.querySelector("#add-filter");
+     this.addButton.addEventListener("click", this._addButtonClick);
+-    this.filtersList.addEventListener("click", this._removeButtonClick);
+-    this.filtersList.addEventListener("mousedown", this._mouseDown);
+-    this.filtersList.addEventListener("keydown", this._keyDown);
++    this.filterList.addEventListener("click", this._removeButtonClick);
++    this.filterList.addEventListener("mousedown", this._mouseDown);
++    this.filterList.addEventListener("keydown", this._keyDown);
+     this.el.addEventListener("mousedown", this._resetFocus);
+ 
+-    this.presetsList.addEventListener("click", this._presetClick);
++    this.presetList.addEventListener("click", this._presetClick);
+     this.togglePresets.addEventListener("click", this._togglePresets);
+     this.addPresetButton.addEventListener("click", this._savePreset);
+ 
+     // These events are event delegators for
+     // drag-drop re-ordering and label-dragging
+     this.win.addEventListener("mousemove", this._mouseMove);
+     this.win.addEventListener("mouseup", this._mouseUp);
+ 
+     // Used to workaround float-precision problems
+-    this.filtersList.addEventListener("input", this._input);
++    this.filterList.addEventListener("input", this._input);
+   },
+ 
+   _removeEventListeners: function () {
+     this.addButton.removeEventListener("click", this._addButtonClick);
+-    this.filtersList.removeEventListener("click", this._removeButtonClick);
+-    this.filtersList.removeEventListener("mousedown", this._mouseDown);
+-    this.filtersList.removeEventListener("keydown", this._keyDown);
++    this.filterList.removeEventListener("click", this._removeButtonClick);
++    this.filterList.removeEventListener("mousedown", this._mouseDown);
++    this.filterList.removeEventListener("keydown", this._keyDown);
+     this.el.removeEventListener("mousedown", this._resetFocus);
+ 
+-    this.presetsList.removeEventListener("click", this._presetClick);
++    this.presetList.removeEventListener("click", this._presetClick);
+     this.togglePresets.removeEventListener("click", this._togglePresets);
+     this.addPresetButton.removeEventListener("click", this._savePreset);
+ 
+     // These events are used for drag drop re-ordering
+     this.win.removeEventListener("mousemove", this._mouseMove);
+     this.win.removeEventListener("mouseup", this._mouseUp);
+ 
+     // Used to workaround float-precision problems
+-    this.filtersList.removeEventListener("input", this._input);
++    this.filterList.removeEventListener("input", this._input);
+   },
+ 
+   _getFilterElementIndex: function (el) {
+-    return [...this.filtersList.children].indexOf(el);
++    return [...this.filterList.children].indexOf(el);
+   },
+ 
+   _keyDown: function (e) {
+     if (e.target.tagName.toLowerCase() !== "input" ||
+        (e.keyCode !== 40 && e.keyCode !== 38)) {
+       return;
+     }
+     let input = e.target;
+@@ -454,57 +494,57 @@ CSSFilterEditorWidget.prototype = {
+     if (this.isReorderingFilter) {
+       this._dragFilterElement(e);
+     } else if (this.isDraggingLabel) {
+       this._dragLabel(e);
+     }
+   },
+ 
+   _dragFilterElement: function (e) {
+-    const rect = this.filtersList.getBoundingClientRect();
++    const rect = this.filterList.getBoundingClientRect();
+     let top = e.pageY - LIST_PADDING;
+     let bottom = e.pageY + LIST_PADDING;
+     // don't allow dragging over top/bottom of list
+     if (top < rect.top || bottom > rect.bottom) {
+       return;
+     }
+ 
+-    const filterEl = this.filtersList.querySelector(".dragging");
++    const filterEl = this.filterList.querySelector(".dragging");
+ 
+     const delta = e.pageY - filterEl.startingY;
+     filterEl.style.top = delta + "px";
+ 
+     // change is the number of _steps_ taken from initial position
+     // i.e. how many elements we have passed
+     let change = delta / LIST_ITEM_HEIGHT;
+     if (change > 0) {
+       change = Math.floor(change);
+     } else if (change < 0) {
+       change = Math.ceil(change);
+     }
+ 
+-    const children = this.filtersList.children;
++    const children = this.filterList.children;
+     const index = [...children].indexOf(filterEl);
+     const destination = index + change;
+ 
+     // If we're moving out, or there's no change at all, stop and return
+     if (destination >= children.length || destination < 0 || change === 0) {
+       return;
+     }
+ 
+     // Re-order filter objects
+     swapArrayIndices(this.filters, index, destination);
+ 
+     // Re-order the dragging element in markup
+     const target = change > 0 ? children[destination + 1]
+                               : children[destination];
+     if (target) {
+-      this.filtersList.insertBefore(filterEl, target);
++      this.filterList.insertBefore(filterEl, target);
+     } else {
+-      this.filtersList.appendChild(filterEl);
++      this.filterList.appendChild(filterEl);
+     }
+ 
+     filterEl.removeAttribute("style");
+ 
+     const currentPosition = change * LIST_ITEM_HEIGHT;
+     filterEl.startingY = e.pageY + currentPosition - delta;
+   },
+ 
+@@ -545,17 +585,17 @@ CSSFilterEditorWidget.prototype = {
+     // Label-dragging is disabled on mouseup
+     this._dragging = null;
+     this.isDraggingLabel = false;
+ 
+     // Filter drag/drop needs more cleaning
+     if (!this.isReorderingFilter) {
+       return;
+     }
+-    let filterEl = this.filtersList.querySelector(".dragging");
++    let filterEl = this.filterList.querySelector(".dragging");
+ 
+     this.isReorderingFilter = false;
+     filterEl.classList.remove("dragging");
+     this.el.classList.remove("dragging");
+     filterEl.removeAttribute("style");
+ 
+     this.emit("updated", this.getCssValue());
+     this.render();
+@@ -626,23 +666,23 @@ CSSFilterEditorWidget.prototype = {
+ 
+   /**
+    * Clears the list and renders filters, binding required events.
+    * There are some delegated events bound in _addEventListeners method
+    */
+   render: function () {
+     if (!this.filters.length) {
+   // eslint-disable-next-line no-unsanitized/property
+-      this.filtersList.innerHTML = `<p> ${L10N.getStr("emptyFilterList")} <br />
++      this.filterList.innerHTML = `<p> ${L10N.getStr("emptyFilterList")} <br />
+                                  ${L10N.getStr("addUsingList")} </p>`;
+       this.emit("render");
+       return;
+     }
+ 
+-    this.filtersList.innerHTML = "";
++    this.filterList.innerHTML = "";
+ 
+     let base = this._filterItemMarkup;
+ 
+     for (let filter of this.filters) {
+       const def = this._definition(filter.name);
+ 
+       let el = base.cloneNode(true);
+ 
+@@ -682,61 +722,61 @@ CSSFilterEditorWidget.prototype = {
+ 
+         label.classList.add("devtools-draglabel");
+         label.title = L10N.getStr("labelDragTooltipText");
+       } else {
+         // string-type filters have no unit
+         unitPreview.remove();
+       }
+ 
+-      this.filtersList.appendChild(el);
++      this.filterList.appendChild(el);
+     }
+ 
+     let lastInput =
+-        this.filtersList.querySelector(".filter:last-of-type input");
++        this.filterList.querySelector(".filter:last-of-type input");
+     if (lastInput) {
+       lastInput.focus();
+       if (lastInput.type === "text") {
+         // move cursor to end of input
+         const end = lastInput.value.length;
+         lastInput.setSelectionRange(end, end);
+       }
+     }
+ 
+     this.emit("render");
+   },
+ 
+   renderPresets: function () {
+     this.getPresets().then(presets => {
+       // getPresets is async and the widget may be destroyed in between.
+-      if (!this.presetsList) {
++      if (!this.presetList) {
+         return;
+       }
+ 
+       if (!presets || !presets.length) {
+       // eslint-disable-next-line no-unsanitized/property
+-        this.presetsList.innerHTML = `<p>${L10N.getStr("emptyPresetList")}</p>`;
++        this.presetList.innerHTML = `<p>${L10N.getStr("emptyPresetList")}</p>`;
+         this.emit("render");
+         return;
+       }
+       let base = this._presetItemMarkup;
+ 
+-      this.presetsList.innerHTML = "";
++      this.presetList.innerHTML = "";
+ 
+       for (let [index, preset] of presets.entries()) {
+         let el = base.cloneNode(true);
+ 
+         let [label, span] = el.children;
+ 
+         el.dataset.id = index;
+ 
+         label.textContent = preset.name;
+         span.textContent = preset.value;
+ 
+-        this.presetsList.appendChild(el);
++        this.presetList.appendChild(el);
+       }
+ 
+       this.emit("render");
+     });
+   },
+ 
+   /**
+     * returns definition of a filter as defined in filterList
+diff --git a/devtools/client/sourceeditor/README b/devtools/client/sourceeditor/README
+--- a/devtools/client/sourceeditor/README
++++ b/devtools/client/sourceeditor/README
+@@ -49,42 +49,42 @@ below.
+ The following files in this directory and devtools/client/sourceeditor/test/codemirror/
+ are licensed according to the contents in the LICENSE file.
+ 
+ # Localization patches
+ 
+ diff --git a/devtools/client/sourceeditor/codemirror/addon/search/search.js b/devtools/client/sourceeditor/codemirror/addon/search/search.js
+ --- a/devtools/client/sourceeditor/codemirror/addon/search/search.js
+ +++ b/devtools/client/sourceeditor/codemirror/addon/search/search.js
+-@@ -92,32 +92,47 @@
++@@ -93,32 +93,47 @@
+      } else {
+        query = parseString(query)
+      }
+      if (typeof query == "string" ? query == "" : query.test(""))
+        query = /x^/;
+      return query;
+    }
+ 
+ -  var queryDialog =
+--    'Search: <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
++-    '<span class="CodeMirror-search-label">Search:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
+ +  var queryDialog;
+ 
+    function startSearch(cm, state, query) {
+      state.queryText = query;
+      state.query = parseQuery(query);
+      cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
+      state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
+      cm.addOverlay(state.overlay);
+      if (cm.showMatchesOnScrollbar) {
+        if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+        state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
+      }
+    }
+ 
+-   function doSearch(cm, rev, persistent) {
++   function doSearch(cm, rev, persistent, immediate) {
+ +    if (!queryDialog) {
+ +      let doc = cm.getWrapperElement().ownerDocument;
+ +      let inp = doc.createElement("input");
+ +
+ +      inp.type = "search";
+ +      inp.placeholder = cm.l10n("findCmd.promptMessage");
+ +      inp.style.marginInlineStart = "1em";
+ +      inp.style.marginInlineEnd = "1em";
+@@ -94,21 +94,142 @@ diff --git a/devtools/client/sourceedito
+ +      queryDialog = doc.createElement("div");
+ +      queryDialog.appendChild(inp);
+ +      queryDialog.style.display = "flex";
+ +    }
+ +
+      var state = getSearchState(cm);
+      if (state.query) return findNext(cm, rev);
+      var q = cm.getSelection() || state.lastQuery;
++     if (q instanceof RegExp && q.source == "x^") q = null
+      if (persistent && cm.openDialog) {
+        var hiding = null
+-       persistentDialog(cm, queryDialog, q, function(query, event) {
++       var searchNext = function(query, event) {
+          CodeMirror.e_stop(event);
+-         if (!query) return;
++@@ -181,56 +196,110 @@
++     var state = getSearchState(cm);
++     state.lastQuery = state.query;
++     if (!state.query) return;
++     state.query = state.queryText = null;
++     cm.removeOverlay(state.overlay);
++     if (state.annotate) { state.annotate.clear(); state.annotate = null; }
++   });}
++ 
++-  var replaceQueryDialog =
++-    ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
++-  var replacementQueryDialog = '<span class="CodeMirror-search-label">With:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
++-  var doReplaceConfirm = '<span class="CodeMirror-search-label">Replace?</span> <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>';
++-
++   function replaceAll(cm, query, text) {
++     cm.operation(function() {
++       for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
++         if (typeof query != "string") {
++           var match = cm.getRange(cursor.from(), cursor.to()).match(query);
++           cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
++         } else cursor.replace(text);
++       }
++     });
++   }
++ 
++   function replace(cm, all) {
++     if (cm.getOption("readOnly")) return;
++     var query = cm.getSelection() || getSearchState(cm).lastQuery;
++-    var dialogText = '<span class="CodeMirror-search-label">' + (all ? 'Replace all:' : 'Replace:') + '</span>';
++-    dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
+++
+++    let doc = cm.getWrapperElement().ownerDocument;
+++
+++    // `searchLabel` is used as part of `replaceQueryFragment` and as a separate
+++    // argument by itself, so it should be cloned.
+++    let searchLabel = doc.createElement("span");
+++    searchLabel.classList.add("CodeMirror-search-label");
+++    searchLabel.textContent = all ? "Replace all:" : "Replace:";
+++
+++    let replaceQueryFragment = doc.createDocumentFragment();
+++    replaceQueryFragment.appendChild(searchLabel.cloneNode(true));
+++
+++    let searchField = doc.createElement("input");
+++    searchField.setAttribute("type", "text");
+++    searchField.setAttribute("style", "width: 10em");
+++    searchField.classList.add("CodeMirror-search-field");
+++    replaceQueryFragment.appendChild(searchField);
+++
+++    let searchHint = doc.createElement("span");
+++    searchHint.setAttribute("style", "color: #888");
+++    searchHint.classList.add("CodeMirror-search-hint");
+++    searchHint.textContent = "(Use /re/ syntax for regexp search)";
+++    replaceQueryFragment.appendChild(searchHint);
+++
+++    dialog(cm, replaceQueryFragment, searchLabel, query, function(query) {
++       if (!query) return;
++       query = parseQuery(query);
++-      dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
+++
+++      let replacementQueryFragment = doc.createDocumentFragment();
+++
+++      let replaceWithLabel = searchLabel.cloneNode(false);
+++      replaceWithLabel.textContent = "With:";
+++      replacementQueryFragment.appendChild(replaceWithLabel);
+++
+++      let replaceField = doc.createElement("input");
+++      replaceField.setAttribute("type", "text");
+++      replaceField.setAttribute("style", "width: 10em");
+++      replaceField.classList.add("CodeMirror-search-field");
+++      replacementQueryFragment.appendChild(replaceField);
+++
+++      dialog(cm, replacementQueryFragment, "Replace with:", "", function(text) {
++         text = parseString(text)
++         if (all) {
++           replaceAll(cm, query, text)
++         } else {
++           clearSearch(cm);
++           var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
++           var advance = function() {
++             var start = cursor.from(), match;
++             if (!(match = cursor.findNext())) {
++               cursor = getSearchCursor(cm, query);
++               if (!(match = cursor.findNext()) ||
++                   (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
++             }
++             cm.setSelection(cursor.from(), cursor.to());
++-            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
++-            confirmDialog(cm, doReplaceConfirm, "Replace?",
+++            cm.scrollIntoView({ from: cursor.from(), to: cursor.to() });
+++
+++            let replaceConfirmFragment = doc.createDocumentFragment();
+++
+++            let replaceConfirmLabel = searchLabel.cloneNode(false);
+++            replaceConfirmLabel.textContent = "Replace?";
+++            replaceConfirmFragment.appendChild(replaceConfirmLabel);
+++
+++            let yesButton = doc.createElement("button");
+++            yesButton.textContent = "Yes";
+++            replaceConfirmFragment.appendChild(yesButton);
+++
+++            let noButton = doc.createElement("button");
+++            noButton.textContent = "No";
+++            replaceConfirmFragment.appendChild(noButton);
+++
+++            let allButton = doc.createElement("button");
+++            allButton.textContent = "All";
+++            replaceConfirmFragment.appendChild(allButton);
+++
+++            let stopButton = doc.createElement("button");
+++            stopButton.textContent = "Stop";
+++            replaceConfirmFragment.appendChild(stopButton);
+++
+++            confirmDialog(cm, replaceConfirmFragment, "Replace?",
++                           [function() {doReplace(match);}, advance,
++                            function() {replaceAll(cm, query, text)}]);
++           };
++           var doReplace = function(match) {
++             cursor.replace(typeof query == "string" ? text :
++                            text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
++             advance();
++           };
+ 
+ # Footnotes
+ 
+ [1] http://codemirror.net
+ [2] devtools/client/sourceeditor/codemirror
+ [3] devtools/client/sourceeditor/test/browser_codemirror.js
+ [4] devtools/client/jar.mn
+ [5] devtools/client/sourceeditor/editor.js
+diff --git a/devtools/client/sourceeditor/codemirror/addon/search/search.js b/devtools/client/sourceeditor/codemirror/addon/search/search.js
+--- a/devtools/client/sourceeditor/codemirror/addon/search/search.js
++++ b/devtools/client/sourceeditor/codemirror/addon/search/search.js
+@@ -196,56 +196,110 @@
+     var state = getSearchState(cm);
+     state.lastQuery = state.query;
+     if (!state.query) return;
+     state.query = state.queryText = null;
+     cm.removeOverlay(state.overlay);
+     if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+   });}
+ 
+-  var replaceQueryDialog =
+-    ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
+-  var replacementQueryDialog = '<span class="CodeMirror-search-label">With:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
+-  var doReplaceConfirm = '<span class="CodeMirror-search-label">Replace?</span> <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>';
+-
+   function replaceAll(cm, query, text) {
+     cm.operation(function() {
+       for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
+         if (typeof query != "string") {
+           var match = cm.getRange(cursor.from(), cursor.to()).match(query);
+           cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+         } else cursor.replace(text);
+       }
+     });
+   }
+ 
+   function replace(cm, all) {
+     if (cm.getOption("readOnly")) return;
+     var query = cm.getSelection() || getSearchState(cm).lastQuery;
+-    var dialogText = '<span class="CodeMirror-search-label">' + (all ? 'Replace all:' : 'Replace:') + '</span>';
+-    dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
++
++    let doc = cm.getWrapperElement().ownerDocument;
++
++    // `searchLabel` is used as part of `replaceQueryFragment` and as a separate
++    // argument by itself, so it should be cloned.
++    let searchLabel = doc.createElement("span");
++    searchLabel.classList.add("CodeMirror-search-label");
++    searchLabel.textContent = all ? "Replace all:" : "Replace:";
++
++    let replaceQueryFragment = doc.createDocumentFragment();
++    replaceQueryFragment.appendChild(searchLabel.cloneNode(true));
++
++    let searchField = doc.createElement("input");
++    searchField.setAttribute("type", "text");
++    searchField.setAttribute("style", "width: 10em");
++    searchField.classList.add("CodeMirror-search-field");
++    replaceQueryFragment.appendChild(searchField);
++
++    let searchHint = doc.createElement("span");
++    searchHint.setAttribute("style", "color: #888");
++    searchHint.classList.add("CodeMirror-search-hint");
++    searchHint.textContent = "(Use /re/ syntax for regexp search)";
++    replaceQueryFragment.appendChild(searchHint);
++
++    dialog(cm, replaceQueryFragment, searchLabel, query, function(query) {
+       if (!query) return;
+       query = parseQuery(query);
+-      dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
++
++      let replacementQueryFragment = doc.createDocumentFragment();
++
++      let replaceWithLabel = searchLabel.cloneNode(false);
++      replaceWithLabel.textContent = "With:";
++      replacementQueryFragment.appendChild(replaceWithLabel);
++
++      let replaceField = doc.createElement("input");
++      replaceField.setAttribute("type", "text");
++      replaceField.setAttribute("style", "width: 10em");
++      replaceField.classList.add("CodeMirror-search-field");
++      replacementQueryFragment.appendChild(replaceField);
++
++      dialog(cm, replacementQueryFragment, "Replace with:", "", function(text) {
+         text = parseString(text)
+         if (all) {
+           replaceAll(cm, query, text)
+         } else {
+           clearSearch(cm);
+           var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
+           var advance = function() {
+             var start = cursor.from(), match;
+             if (!(match = cursor.findNext())) {
+               cursor = getSearchCursor(cm, query);
+               if (!(match = cursor.findNext()) ||
+                   (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
+             }
+             cm.setSelection(cursor.from(), cursor.to());
+-            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
+-            confirmDialog(cm, doReplaceConfirm, "Replace?",
++            cm.scrollIntoView({ from: cursor.from(), to: cursor.to() });
++
++            let replaceConfirmFragment = doc.createDocumentFragment();
++
++            let replaceConfirmLabel = searchLabel.cloneNode(false);
++            replaceConfirmLabel.textContent = "Replace?";
++            replaceConfirmFragment.appendChild(replaceConfirmLabel);
++
++            let yesButton = doc.createElement("button");
++            yesButton.textContent = "Yes";
++            replaceConfirmFragment.appendChild(yesButton);
++
++            let noButton = doc.createElement("button");
++            noButton.textContent = "No";
++            replaceConfirmFragment.appendChild(noButton);
++
++            let allButton = doc.createElement("button");
++            allButton.textContent = "All";
++            replaceConfirmFragment.appendChild(allButton);
++
++            let stopButton = doc.createElement("button");
++            stopButton.textContent = "Stop";
++            replaceConfirmFragment.appendChild(stopButton);
++
++            confirmDialog(cm, replaceConfirmFragment, "Replace?",
+                           [function() {doReplace(match);}, advance,
+                            function() {replaceAll(cm, query, text)}]);
+           };
+           var doReplace = function(match) {
+             cursor.replace(typeof query == "string" ? text :
+                            text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+             advance();
+           };
+diff --git a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+--- a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
++++ b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+@@ -10402,56 +10402,110 @@ var CodeMirror =
+ 	    var state = getSearchState(cm);
+ 	    state.lastQuery = state.query;
+ 	    if (!state.query) return;
+ 	    state.query = state.queryText = null;
+ 	    cm.removeOverlay(state.overlay);
+ 	    if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+ 	  });}
+ 
+-	  var replaceQueryDialog =
+-	    ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
+-	  var replacementQueryDialog = '<span class="CodeMirror-search-label">With:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
+-	  var doReplaceConfirm = '<span class="CodeMirror-search-label">Replace?</span> <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>';
+-
+ 	  function replaceAll(cm, query, text) {
+ 	    cm.operation(function() {
+ 	      for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
+ 	        if (typeof query != "string") {
+ 	          var match = cm.getRange(cursor.from(), cursor.to()).match(query);
+ 	          cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+ 	        } else cursor.replace(text);
+ 	      }
+ 	    });
+ 	  }
+ 
+ 	  function replace(cm, all) {
+ 	    if (cm.getOption("readOnly")) return;
+ 	    var query = cm.getSelection() || getSearchState(cm).lastQuery;
+-	    var dialogText = '<span class="CodeMirror-search-label">' + (all ? 'Replace all:' : 'Replace:') + '</span>';
+-	    dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
++
++	    let doc = cm.getWrapperElement().ownerDocument;
++
++	    // `searchLabel` is used as part of `replaceQueryFragment` and as a separate
++	    // argument by itself, so it should be cloned.
++	    let searchLabel = doc.createElement("span");
++	    searchLabel.classList.add("CodeMirror-search-label");
++	    searchLabel.textContent = all ? "Replace all:" : "Replace:";
++
++	    let replaceQueryFragment = doc.createDocumentFragment();
++	    replaceQueryFragment.appendChild(searchLabel.cloneNode(true));
++
++	    let searchField = doc.createElement("input");
++	    searchField.setAttribute("type", "text");
++	    searchField.setAttribute("style", "width: 10em");
++	    searchField.classList.add("CodeMirror-search-field");
++	    replaceQueryFragment.appendChild(searchField);
++
++	    let searchHint = doc.createElement("span");
++	    searchHint.setAttribute("style", "color: #888");
++	    searchHint.classList.add("CodeMirror-search-hint");
++	    searchHint.textContent = "(Use /re/ syntax for regexp search)";
++	    replaceQueryFragment.appendChild(searchHint);
++
++	    dialog(cm, replaceQueryFragment, searchLabel, query, function(query) {
+ 	      if (!query) return;
+ 	      query = parseQuery(query);
+-	      dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
++
++	      let replacementQueryFragment = doc.createDocumentFragment();
++
++	      let replaceWithLabel = searchLabel.cloneNode(false);
++	      replaceWithLabel.textContent = "With:";
++	      replacementQueryFragment.appendChild(replaceWithLabel);
++
++	      let replaceField = doc.createElement("input");
++	      replaceField.setAttribute("type", "text");
++	      replaceField.setAttribute("style", "width: 10em");
++	      replaceField.classList.add("CodeMirror-search-field");
++	      replacementQueryFragment.appendChild(replaceField);
++
++	      dialog(cm, replacementQueryFragment, "Replace with:", "", function(text) {
+ 	        text = parseString(text)
+ 	        if (all) {
+ 	          replaceAll(cm, query, text)
+ 	        } else {
+ 	          clearSearch(cm);
+ 	          var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
+ 	          var advance = function() {
+ 	            var start = cursor.from(), match;
+ 	            if (!(match = cursor.findNext())) {
+ 	              cursor = getSearchCursor(cm, query);
+ 	              if (!(match = cursor.findNext()) ||
+ 	                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
+ 	            }
+ 	            cm.setSelection(cursor.from(), cursor.to());
+-	            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
+-	            confirmDialog(cm, doReplaceConfirm, "Replace?",
++	            cm.scrollIntoView({ from: cursor.from(), to: cursor.to() });
++
++	            let replaceConfirmFragment = doc.createDocumentFragment();
++
++	            let replaceConfirmLabel = searchLabel.cloneNode(false);
++	            replaceConfirmLabel.textContent = "Replace?";
++	            replaceConfirmFragment.appendChild(replaceConfirmLabel);
++
++	            let yesButton = doc.createElement("button");
++	            yesButton.textContent = "Yes";
++	            replaceConfirmFragment.appendChild(yesButton);
++
++	            let noButton = doc.createElement("button");
++	            noButton.textContent = "No";
++	            replaceConfirmFragment.appendChild(noButton);
++
++	            let allButton = doc.createElement("button");
++	            allButton.textContent = "All";
++	            replaceConfirmFragment.appendChild(allButton);
++
++	            let stopButton = doc.createElement("button");
++	            stopButton.textContent = "Stop";
++	            replaceConfirmFragment.appendChild(stopButton);
++
++	            confirmDialog(cm, replaceConfirmFragment, "Replace?",
+ 	                          [function() {doReplace(match);}, advance,
+ 	                           function() {replaceAll(cm, query, text)}]);
+ 	          };
+ 	          var doReplace = function(match) {
+ 	            cursor.replace(typeof query == "string" ? text :
+ 	                           text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+ 	            advance();
+ 	          };
+diff --git a/devtools/server/tests/mochitest/inspector-traversal-data.html b/devtools/server/tests/mochitest/inspector-traversal-data.html
+--- a/devtools/server/tests/mochitest/inspector-traversal-data.html
++++ b/devtools/server/tests/mochitest/inspector-traversal-data.html
+@@ -20,17 +20,28 @@
+   <script type="text/javascript">
+     "use strict";
+ 
+     window.onload = function () {
+       // Set up a basic shadow DOM
+       let host = document.querySelector("#shadow");
+       if (host.createShadowRoot) {
+         let root = host.createShadowRoot();
+-        root.innerHTML = "<h3>Shadow <em>DOM</em></h3><select multiple></select>";
++
++        let h3 = document.createElement("h3");
++        h3.append("Shadow ");
++
++        let em = document.createElement("em");
++        em.append("DOM");
++
++        let select = document.createElement("select");
++        select.setAttribute("multiple", "");
++        h3.appendChild(em);
++        root.appendChild(h3);
++        root.appendChild(select);
+       }
+ 
+       // Put a copy of the body in an iframe to test frame traversal.
+       let body = document.querySelector("body");
+       let data = "data:text/html,<html>" + body.outerHTML + "<html>";
+       let iframe = document.createElement("iframe");
+       iframe.setAttribute("id", "childFrame");
+       iframe.onload = function () {

+ 38 - 0
frg/work-js/mozilla-release/patches/1433929-3no4-60a1.patch

@@ -0,0 +1,38 @@
+# HG changeset patch
+# User Frederik Braun <fbraun@mozilla.com>
+# Date 1518710837 -3600
+# Node ID dd7085d49ceb83eb93360760c6760d6470e542ea
+# Parent  4a8fccdeb919b7239b6b405b5181852bca233565
+Bug 1433929 - Avoid innerHTML in accessible/tests/mochitest/elm/test_shadowroot_subframe.html. r=johannh
+
+MozReview-Commit-ID: 2CuAZkF78Hc
+
+diff --git a/accessible/tests/mochitest/elm/test_shadowroot_subframe.html.1433929-3.later b/accessible/tests/mochitest/elm/test_shadowroot_subframe.html.1433929-3.later
+new file mode 100644
+--- /dev/null
++++ b/accessible/tests/mochitest/elm/test_shadowroot_subframe.html.1433929-3.later
+@@ -0,0 +1,24 @@
++--- test_shadowroot_subframe.html
+++++ test_shadowroot_subframe.html
++@@ -32,12 +32,19 @@
++ 
++ </head>
++ <body>
++   <div role="group" id="component"></div>
++   <script>
++     var component = document.getElementById("component");
++     var shadow = component.attachShadow({mode: "open"});
++ 
++-    shadow.innerHTML = "<button>Hello</button>" +
++-      '<a href="#"> World</a>';
+++    var button = document.createElement("button");
+++    button.append("Hello");
+++
+++    var a = document.createElement("a");
+++    a.setAttribute("href", "#");
+++    a.append(" World");
+++
+++    shadow.appendChild(button);
+++    shadow.appendChild(a);
++   </script>
++ </body>

+ 72 - 0
frg/work-js/mozilla-release/patches/1433929-5-60a1.patch

@@ -0,0 +1,72 @@
+# HG changeset patch
+# User Johann Hofmann <jhofmann@mozilla.com>
+# Date 1518875993 -3600
+# Node ID c92a04d86490eb208e45511ea306c6272be82037
+# Parent  418f5f21ed1bcb7cdd9afe9c0c4160cee5571f69
+Bug 1433929 - Remove innerHTML usage from newtab/grid.js. r=Gijs
+
+MozReview-Commit-ID: 1dS1hEUJCzV
+
+diff --git a/browser/base/content/newtab/grid.js b/browser/base/content/newtab/grid.js
+--- a/browser/base/content/newtab/grid.js
++++ b/browser/base/content/newtab/grid.js
+@@ -174,28 +174,47 @@ var gGrid = {
+   /**
+    * Creates the DOM fragment that is re-used when creating sites.
+    */
+   _createSiteFragment: function Grid_createSiteFragment() {
+     let site = document.createElementNS(HTML_NAMESPACE, "div");
+     site.classList.add("newtab-site");
+     site.setAttribute("draggable", "true");
+ 
+-    // Create the site's inner HTML code.
+-    site.innerHTML =
+-      '<a class="newtab-link">' +
+-      '  <span class="newtab-thumbnail placeholder"/>' +
+-      '  <img src="" alt="" class="newtab-thumbnail thumbnail"/>' +
+-      '  <img src="" alt="" class="newtab-thumbnail enhanced-content"/>' +
+-      '  <span class="newtab-title"/>' +
+-      '</a>' +
+-      '<input type="button" title="' + newTabString("pin") + '"' +
+-      '       class="newtab-control newtab-control-pin"/>' +
+-      '<input type="button" title="' + newTabString("block") + '"' +
+-      '       class="newtab-control newtab-control-block"/>';
++    let link = document.createElement("a");
++    link.className = "newtab-link";
++    site.appendChild(link);
++
++    let thumbnailPlaceHolder = document.createElement("span");
++    thumbnailPlaceHolder.className = "newtab-thumbnail placeholder";
++    link.appendChild(thumbnailPlaceHolder);
++
++    let thumbnail = document.createElement("img");
++    thumbnail.className = "newtab-thumbnail thumbnail";
++    link.appendChild(thumbnail);
++
++    let enhancedContent = document.createElement("img");
++    enhancedContent.className = "newtab-thumbnail enhanced-content";
++    link.appendChild(enhancedContent);
++
++    let title = document.createElement("span");
++    title.className = "newtab-title";
++    link.appendChild(title);
++
++    let pinButton = document.createElement("input");
++    pinButton.type = "button";
++    pinButton.title = newTabString("pin");
++    pinButton.className = "newtab-control newtab-control-pin";
++    site.appendChild(pinButton);
++
++    let removeButton = document.createElement("input");
++    removeButton.type = "button";
++    removeButton.title = newTabString("block");
++    removeButton.className = "newtab-control newtab-control-block";
++    site.appendChild(removeButton);
+ 
+     this._siteFragment = document.createDocumentFragment();
+     this._siteFragment.appendChild(site);
+   },
+ 
+   /**
+    * Test a tile at a given position for being pinned or history
+    * @param position Position in sites array

+ 12 - 0
frg/work-js/mozilla-release/patches/1434225-60a1.patch

@@ -0,0 +1,12 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1517498142 28800
+# Node ID 9e41a686a5eda10bc630d2703fd8516e5804cf5a
+# Parent  f9a52d753b64a4d388e8174a8e08566951207725
+Bug 1434225 - Move codemirror build steps README to devtools/client/sourceeditor/. r=gl
+
+MozReview-Commit-ID: K2aKnNopC7Y
+
+diff --git a/devtools/client/sourceeditor/codemirror/README b/devtools/client/sourceeditor/README
+rename from devtools/client/sourceeditor/codemirror/README
+rename to devtools/client/sourceeditor/README

+ 667 - 0
frg/work-js/mozilla-release/patches/1434295-60a1.patch

@@ -0,0 +1,667 @@
+# HG changeset patch
+# User Gabriel Luong <gabriel.luong@gmail.com>
+# Date 1517498368 18000
+# Node ID e1a47a0d14219da641ec70cd7b6b6d772ef543a7
+# Parent  febbe89955dcd8ca8b46d6e59cf15e1ed73f2e2e
+Bug 1434295 - Update CodeMirror to 5.34.0. r=bgrins
+
+diff --git a/devtools/client/sourceeditor/codemirror/README b/devtools/client/sourceeditor/codemirror/README
+--- a/devtools/client/sourceeditor/codemirror/README
++++ b/devtools/client/sourceeditor/codemirror/README
+@@ -1,16 +1,16 @@
+ This is the CodeMirror editor packaged for the Mozilla Project. CodeMirror
+ is a JavaScript component that provides a code editor in the browser. When
+ a mode is available for the language you are coding in, it will color your
+ code, and optionally help with indentation.
+ 
+ # Upgrade
+ 
+-Currently used version is 5.33.0. To upgrade: download a new version of
++Currently used version is 5.34.0. To upgrade: download a new version of
+ CodeMirror from the project's page [1] and replace all JavaScript and
+ CSS files inside the codemirror directory [2].
+ 
+ Then to recreate codemirror.bundle.js:
+  > cd devtools/client/sourceeditor
+  > npm install
+  > webpack
+ 
+diff --git a/devtools/client/sourceeditor/codemirror/addon/edit/closebrackets.js b/devtools/client/sourceeditor/codemirror/addon/edit/closebrackets.js
+--- a/devtools/client/sourceeditor/codemirror/addon/edit/closebrackets.js
++++ b/devtools/client/sourceeditor/codemirror/addon/edit/closebrackets.js
+@@ -124,18 +124,18 @@
+       } else if ((identical || !opening) && next == ch) {
+         if (identical && stringStartsAfter(cm, cur))
+           curType = "both";
+         else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
+           curType = "skipThree";
+         else
+           curType = "skip";
+       } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
+-                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch &&
+-                 (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) {
++                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
++        if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
+         curType = "addFour";
+       } else if (identical) {
+         var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
+         if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
+         else return CodeMirror.Pass;
+       } else if (opening && (cm.getLine(cur.line).length == cur.ch ||
+                              isClosingBracket(next, pairs) ||
+                              /\s/.test(next))) {
+diff --git a/devtools/client/sourceeditor/codemirror/addon/edit/matchbrackets.js b/devtools/client/sourceeditor/codemirror/addon/edit/matchbrackets.js
+--- a/devtools/client/sourceeditor/codemirror/addon/edit/matchbrackets.js
++++ b/devtools/client/sourceeditor/codemirror/addon/edit/matchbrackets.js
+@@ -97,28 +97,33 @@
+           for (var i = 0; i < marks.length; i++) marks[i].clear();
+         });
+       };
+       if (autoclear) setTimeout(clear, 800);
+       else return clear;
+     }
+   }
+ 
+-  var currentlyHighlighted = null;
+   function doMatchBrackets(cm) {
+     cm.operation(function() {
+-      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
+-      currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
++      if (cm.state.matchBrackets.currentlyHighlighted) {
++        cm.state.matchBrackets.currentlyHighlighted();
++        cm.state.matchBrackets.currentlyHighlighted = null;
++      }
++      cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
+     });
+   }
+ 
+   CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
+     if (old && old != CodeMirror.Init) {
+       cm.off("cursorActivity", doMatchBrackets);
+-      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
++      if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
++        cm.state.matchBrackets.currentlyHighlighted();
++        cm.state.matchBrackets.currentlyHighlighted = null;
++      }
+     }
+     if (val) {
+       cm.state.matchBrackets = typeof val == "object" ? val : {};
+       cm.on("cursorActivity", doMatchBrackets);
+     }
+   });
+ 
+   CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
+diff --git a/devtools/client/sourceeditor/codemirror/addon/fold/xml-fold.js b/devtools/client/sourceeditor/codemirror/addon/fold/xml-fold.js
+--- a/devtools/client/sourceeditor/codemirror/addon/fold/xml-fold.js
++++ b/devtools/client/sourceeditor/codemirror/addon/fold/xml-fold.js
+@@ -133,17 +133,17 @@
+       }
+     }
+   }
+ 
+   CodeMirror.registerHelper("fold", "xml", function(cm, start) {
+     var iter = new Iter(cm, start.line, 0);
+     for (;;) {
+       var openTag = toNextTag(iter), end;
+-      if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return;
++      if (!openTag || !(end = toTagEnd(iter)) || iter.line != start.line) return;
+       if (!openTag[1] && end != "selfClose") {
+         var startPos = Pos(iter.line, iter.ch);
+         var endPos = findMatchingClose(iter, openTag[2]);
+         return endPos && {from: startPos, to: endPos.from};
+       }
+     }
+   });
+   CodeMirror.findMatchingTag = function(cm, pos, range) {
+diff --git a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+--- a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
++++ b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+@@ -6810,21 +6810,21 @@ var CodeMirror =
+ 	    { return }
+ 	  // Might be a text scaling operation, clear size caches.
+ 	  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
+ 	  d.scrollbarsClipped = false
+ 	  cm.setSize()
+ 	}
+ 
+ 	var keyNames = {
+-	  3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
++	  3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+ 	  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
+ 	  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
+ 	  46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
+-	  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
++	  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
+ 	  173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
+ 	  221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
+ 	  63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
+ 	}
+ 
+ 	// Number keys
+ 	for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
+ 	// Alphabetic keys
+@@ -6961,16 +6961,19 @@ var CodeMirror =
+ 	  return name
+ 	}
+ 
+ 	// Look up the name of a key as indicated by an event object.
+ 	function keyName(event, noShift) {
+ 	  if (presto && event.keyCode == 34 && event["char"]) { return false }
+ 	  var name = keyNames[event.keyCode]
+ 	  if (name == null || event.altGraphKey) { return false }
++	  // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
++	  // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
++	  if (event.keyCode == 3 && event.code) { name = event.code }
+ 	  return addModifierNames(name, event, noShift)
+ 	}
+ 
+ 	function getKeyMap(val) {
+ 	  return typeof val == "string" ? keyMap[val] : val
+ 	}
+ 
+ 	// Helper for deleting text near the selection(s), used to implement
+@@ -8250,17 +8253,17 @@ var CodeMirror =
+ 
+ 	function applyTextInput(cm, inserted, deleted, sel, origin) {
+ 	  var doc = cm.doc
+ 	  cm.display.shift = false
+ 	  if (!sel) { sel = doc.sel }
+ 
+ 	  var paste = cm.state.pasteIncoming || origin == "paste"
+ 	  var textLines = splitLinesAuto(inserted), multiPaste = null
+-	  // When pasing N lines into N selections, insert one line per selection
++	  // When pasting N lines into N selections, insert one line per selection
+ 	  if (paste && sel.ranges.length > 1) {
+ 	    if (lastCopied && lastCopied.text.join("\n") == inserted) {
+ 	      if (sel.ranges.length % lastCopied.text.length == 0) {
+ 	        multiPaste = []
+ 	        for (var i = 0; i < lastCopied.text.length; i++)
+ 	          { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
+ 	      }
+ 	    } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
+@@ -9884,17 +9887,17 @@ var CodeMirror =
+ 	CodeMirror.defineDocExtension = function (name, func) {
+ 	  Doc.prototype[name] = func
+ 	}
+ 
+ 	CodeMirror.fromTextArea = fromTextArea
+ 
+ 	addLegacyProps(CodeMirror)
+ 
+-	CodeMirror.version = "5.33.0"
++	CodeMirror.version = "5.34.0"
+ 
+ 	return CodeMirror;
+ 
+ 	})));
+ 
+ /***/ }),
+ /* 3 */
+ /***/ (function(module, exports, __webpack_require__) {
+@@ -10566,28 +10569,33 @@ var CodeMirror =
+ 	          for (var i = 0; i < marks.length; i++) marks[i].clear();
+ 	        });
+ 	      };
+ 	      if (autoclear) setTimeout(clear, 800);
+ 	      else return clear;
+ 	    }
+ 	  }
+ 
+-	  var currentlyHighlighted = null;
+ 	  function doMatchBrackets(cm) {
+ 	    cm.operation(function() {
+-	      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
+-	      currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
++	      if (cm.state.matchBrackets.currentlyHighlighted) {
++	        cm.state.matchBrackets.currentlyHighlighted();
++	        cm.state.matchBrackets.currentlyHighlighted = null;
++	      }
++	      cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
+ 	    });
+ 	  }
+ 
+ 	  CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
+ 	    if (old && old != CodeMirror.Init) {
+ 	      cm.off("cursorActivity", doMatchBrackets);
+-	      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
++	      if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
++	        cm.state.matchBrackets.currentlyHighlighted();
++	        cm.state.matchBrackets.currentlyHighlighted = null;
++	      }
+ 	    }
+ 	    if (val) {
+ 	      cm.state.matchBrackets = typeof val == "object" ? val : {};
+ 	      cm.on("cursorActivity", doMatchBrackets);
+ 	    }
+ 	  });
+ 
+ 	  CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
+@@ -10739,18 +10747,18 @@ var CodeMirror =
+ 	      } else if ((identical || !opening) && next == ch) {
+ 	        if (identical && stringStartsAfter(cm, cur))
+ 	          curType = "both";
+ 	        else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
+ 	          curType = "skipThree";
+ 	        else
+ 	          curType = "skip";
+ 	      } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
+-	                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch &&
+-	                 (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) {
++	                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
++	        if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
+ 	        curType = "addFour";
+ 	      } else if (identical) {
+ 	        var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
+ 	        if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
+ 	        else return CodeMirror.Pass;
+ 	      } else if (opening && (cm.getLine(cur.line).length == cur.ch ||
+ 	                             isClosingBracket(next, pairs) ||
+ 	                             /\s/.test(next))) {
+@@ -11370,25 +11378,24 @@ var CodeMirror =
+ 	      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
+ 	        cx.state.cc.pop()();
+ 	      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
+ 	    }
+ 	    if (type == "function") return cont(functiondef);
+ 	    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
+ 	    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); }
+ 	    if (type == "variable") {
+-	      if (isTS && value == "type") {
+-	        cx.marked = "keyword"
+-	        return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
+-	      } else if (isTS && value == "declare") {
++	      if (isTS && value == "declare") {
+ 	        cx.marked = "keyword"
+ 	        return cont(statement)
+-	      } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) {
++	      } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
+ 	        cx.marked = "keyword"
+-	        return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
++	        if (value == "enum") return cont(enumdef);
++	        else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
++	        else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+ 	      } else if (isTS && value == "namespace") {
+ 	        cx.marked = "keyword"
+ 	        return cont(pushlex("form"), expression, block, poplex)
+ 	      } else {
+ 	        return cont(pushlex("stat"), maybelabel);
+ 	      }
+ 	    }
+ 	    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
+@@ -11633,17 +11640,18 @@ var CodeMirror =
+ 	    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+ 	  }
+ 	  function typeparam() {
+ 	    return pass(typeexpr, maybeTypeDefault)
+ 	  }
+ 	  function maybeTypeDefault(_, value) {
+ 	    if (value == "=") return cont(typeexpr)
+ 	  }
+-	  function vardef() {
++	  function vardef(_, value) {
++	    if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
+ 	    return pass(pattern, maybetype, maybeAssign, vardefCont);
+ 	  }
+ 	  function pattern(type, value) {
+ 	    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
+ 	    if (type == "variable") { register(value); return cont(); }
+ 	    if (type == "spread") return cont(pattern);
+ 	    if (type == "[") return contCommasep(pattern, "]");
+ 	    if (type == "{") return contCommasep(proppattern, "}");
+@@ -11705,18 +11713,20 @@ var CodeMirror =
+ 	    if (type == "variable") return className(type, value);
+ 	    return classNameAfter(type, value);
+ 	  }
+ 	  function className(type, value) {
+ 	    if (type == "variable") {register(value); return cont(classNameAfter);}
+ 	  }
+ 	  function classNameAfter(type, value) {
+ 	    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
+-	    if (value == "extends" || value == "implements" || (isTS && type == ","))
++	    if (value == "extends" || value == "implements" || (isTS && type == ",")) {
++	      if (value == "implements") cx.marked = "keyword";
+ 	      return cont(isTS ? typeexpr : expression, classNameAfter);
++	    }
+ 	    if (type == "{") return cont(pushlex("}"), classBody, poplex);
+ 	  }
+ 	  function classBody(type, value) {
+ 	    if (type == "async" ||
+ 	        (type == "variable" &&
+ 	         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
+ 	         cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
+ 	      cx.marked = "keyword";
+@@ -11770,16 +11780,22 @@ var CodeMirror =
+ 	  }
+ 	  function maybeFrom(_type, value) {
+ 	    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
+ 	  }
+ 	  function arrayLiteral(type) {
+ 	    if (type == "]") return cont();
+ 	    return pass(commasep(expressionNoComma, "]"));
+ 	  }
++	  function enumdef() {
++	    return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
++	  }
++	  function enummember() {
++	    return pass(pattern, maybeAssign);
++	  }
+ 
+ 	  function isContinuedStatement(state, textAfter) {
+ 	    return state.lastType == "operator" || state.lastType == "," ||
+ 	      isOperatorChar.test(textAfter.charAt(0)) ||
+ 	      /[,.]/.test(textAfter.charAt(0));
+ 	  }
+ 
+ 	  function expressionAllowed(stream, state, backUp) {
+@@ -14638,17 +14654,17 @@ var CodeMirror =
+ 	      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+ 	      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+ 	      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+ 	      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
+ 	    ),
+ 	    intendSwitch: false,
+ 	    indentStatements: false,
+ 	    multiLineStrings: true,
+-	    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
++	    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+ 	    blockKeywords: words("catch class do else finally for if where try while enum"),
+ 	    defKeywords: words("class val var object interface fun"),
+ 	    atoms: words("true false null this"),
+ 	    hooks: {
+ 	      '"': function(stream, state) {
+ 	        state.tokenize = tokenKotlinString(stream.match('""'));
+ 	        return state.tokenize(stream, state);
+ 	      }
+@@ -21193,17 +21209,17 @@ var CodeMirror =
+ 	    }
+ 	  };
+ 
+ 	  cmds.toggleBookmark = function(cm) {
+ 	    var ranges = cm.listSelections();
+ 	    var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
+ 	    for (var i = 0; i < ranges.length; i++) {
+ 	      var from = ranges[i].from(), to = ranges[i].to();
+-	      var found = cm.findMarks(from, to);
++	      var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);
+ 	      for (var j = 0; j < found.length; j++) {
+ 	        if (found[j].sublimeBookmark) {
+ 	          found[j].clear();
+ 	          for (var k = 0; k < marks.length; k++)
+ 	            if (marks[k] == found[j])
+ 	              marks.splice(k--, 1);
+ 	          break;
+ 	        }
+@@ -21974,17 +21990,17 @@ var CodeMirror =
+ 	      }
+ 	    }
+ 	  }
+ 
+ 	  CodeMirror.registerHelper("fold", "xml", function(cm, start) {
+ 	    var iter = new Iter(cm, start.line, 0);
+ 	    for (;;) {
+ 	      var openTag = toNextTag(iter), end;
+-	      if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return;
++	      if (!openTag || !(end = toTagEnd(iter)) || iter.line != start.line) return;
+ 	      if (!openTag[1] && end != "selfClose") {
+ 	        var startPos = Pos(iter.line, iter.ch);
+ 	        var endPos = findMatchingClose(iter, openTag[2]);
+ 	        return endPos && {from: startPos, to: endPos.from};
+ 	      }
+ 	    }
+ 	  });
+ 	  CodeMirror.findMatchingTag = function(cm, pos, range) {
+diff --git a/devtools/client/sourceeditor/codemirror/keymap/sublime.js b/devtools/client/sourceeditor/codemirror/keymap/sublime.js
+--- a/devtools/client/sourceeditor/codemirror/keymap/sublime.js
++++ b/devtools/client/sourceeditor/codemirror/keymap/sublime.js
+@@ -377,17 +377,17 @@
+     }
+   };
+ 
+   cmds.toggleBookmark = function(cm) {
+     var ranges = cm.listSelections();
+     var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
+     for (var i = 0; i < ranges.length; i++) {
+       var from = ranges[i].from(), to = ranges[i].to();
+-      var found = cm.findMarks(from, to);
++      var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);
+       for (var j = 0; j < found.length; j++) {
+         if (found[j].sublimeBookmark) {
+           found[j].clear();
+           for (var k = 0; k < marks.length; k++)
+             if (marks[k] == found[j])
+               marks.splice(k--, 1);
+           break;
+         }
+diff --git a/devtools/client/sourceeditor/codemirror/lib/codemirror.js b/devtools/client/sourceeditor/codemirror/lib/codemirror.js
+--- a/devtools/client/sourceeditor/codemirror/lib/codemirror.js
++++ b/devtools/client/sourceeditor/codemirror/lib/codemirror.js
+@@ -6568,21 +6568,21 @@ function onResize(cm) {
+     { return }
+   // Might be a text scaling operation, clear size caches.
+   d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
+   d.scrollbarsClipped = false
+   cm.setSize()
+ }
+ 
+ var keyNames = {
+-  3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
++  3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+   19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
+   36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
+   46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
+-  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
++  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
+   173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
+   221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
+   63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
+ }
+ 
+ // Number keys
+ for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
+ // Alphabetic keys
+@@ -6719,16 +6719,19 @@ function addModifierNames(name, event, n
+   return name
+ }
+ 
+ // Look up the name of a key as indicated by an event object.
+ function keyName(event, noShift) {
+   if (presto && event.keyCode == 34 && event["char"]) { return false }
+   var name = keyNames[event.keyCode]
+   if (name == null || event.altGraphKey) { return false }
++  // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
++  // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
++  if (event.keyCode == 3 && event.code) { name = event.code }
+   return addModifierNames(name, event, noShift)
+ }
+ 
+ function getKeyMap(val) {
+   return typeof val == "string" ? keyMap[val] : val
+ }
+ 
+ // Helper for deleting text near the selection(s), used to implement
+@@ -8008,17 +8011,17 @@ function setLastCopied(newLastCopied) {
+ 
+ function applyTextInput(cm, inserted, deleted, sel, origin) {
+   var doc = cm.doc
+   cm.display.shift = false
+   if (!sel) { sel = doc.sel }
+ 
+   var paste = cm.state.pasteIncoming || origin == "paste"
+   var textLines = splitLinesAuto(inserted), multiPaste = null
+-  // When pasing N lines into N selections, insert one line per selection
++  // When pasting N lines into N selections, insert one line per selection
+   if (paste && sel.ranges.length > 1) {
+     if (lastCopied && lastCopied.text.join("\n") == inserted) {
+       if (sel.ranges.length % lastCopied.text.length == 0) {
+         multiPaste = []
+         for (var i = 0; i < lastCopied.text.length; i++)
+           { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
+       }
+     } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
+@@ -9642,13 +9645,13 @@ CodeMirror.defineExtension = function (n
+ CodeMirror.defineDocExtension = function (name, func) {
+   Doc.prototype[name] = func
+ }
+ 
+ CodeMirror.fromTextArea = fromTextArea
+ 
+ addLegacyProps(CodeMirror)
+ 
+-CodeMirror.version = "5.33.0"
++CodeMirror.version = "5.34.0"
+ 
+ return CodeMirror;
+ 
+ })));
+\ No newline at end of file
+diff --git a/devtools/client/sourceeditor/codemirror/mode/clike/clike.js b/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
+--- a/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
++++ b/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
+@@ -612,17 +612,17 @@ CodeMirror.defineMode("clike", function(
+       "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+       "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+       "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+       "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
+     ),
+     intendSwitch: false,
+     indentStatements: false,
+     multiLineStrings: true,
+-    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
++    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+     blockKeywords: words("catch class do else finally for if where try while enum"),
+     defKeywords: words("class val var object interface fun"),
+     atoms: words("true false null this"),
+     hooks: {
+       '"': function(stream, state) {
+         state.tokenize = tokenKotlinString(stream.match('""'));
+         return state.tokenize(stream, state);
+       }
+diff --git a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
+--- a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
++++ b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
+@@ -340,25 +340,24 @@ CodeMirror.defineMode("javascript", func
+       if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
+         cx.state.cc.pop()();
+       return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
+     }
+     if (type == "function") return cont(functiondef);
+     if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
+     if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); }
+     if (type == "variable") {
+-      if (isTS && value == "type") {
+-        cx.marked = "keyword"
+-        return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
+-      } else if (isTS && value == "declare") {
++      if (isTS && value == "declare") {
+         cx.marked = "keyword"
+         return cont(statement)
+-      } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) {
++      } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
+         cx.marked = "keyword"
+-        return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
++        if (value == "enum") return cont(enumdef);
++        else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
++        else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+       } else if (isTS && value == "namespace") {
+         cx.marked = "keyword"
+         return cont(pushlex("form"), expression, block, poplex)
+       } else {
+         return cont(pushlex("stat"), maybelabel);
+       }
+     }
+     if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
+@@ -603,17 +602,18 @@ CodeMirror.defineMode("javascript", func
+     if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+   }
+   function typeparam() {
+     return pass(typeexpr, maybeTypeDefault)
+   }
+   function maybeTypeDefault(_, value) {
+     if (value == "=") return cont(typeexpr)
+   }
+-  function vardef() {
++  function vardef(_, value) {
++    if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
+     return pass(pattern, maybetype, maybeAssign, vardefCont);
+   }
+   function pattern(type, value) {
+     if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
+     if (type == "variable") { register(value); return cont(); }
+     if (type == "spread") return cont(pattern);
+     if (type == "[") return contCommasep(pattern, "]");
+     if (type == "{") return contCommasep(proppattern, "}");
+@@ -675,18 +675,20 @@ CodeMirror.defineMode("javascript", func
+     if (type == "variable") return className(type, value);
+     return classNameAfter(type, value);
+   }
+   function className(type, value) {
+     if (type == "variable") {register(value); return cont(classNameAfter);}
+   }
+   function classNameAfter(type, value) {
+     if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
+-    if (value == "extends" || value == "implements" || (isTS && type == ","))
++    if (value == "extends" || value == "implements" || (isTS && type == ",")) {
++      if (value == "implements") cx.marked = "keyword";
+       return cont(isTS ? typeexpr : expression, classNameAfter);
++    }
+     if (type == "{") return cont(pushlex("}"), classBody, poplex);
+   }
+   function classBody(type, value) {
+     if (type == "async" ||
+         (type == "variable" &&
+          (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
+          cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
+       cx.marked = "keyword";
+@@ -740,16 +742,22 @@ CodeMirror.defineMode("javascript", func
+   }
+   function maybeFrom(_type, value) {
+     if (value == "from") { cx.marked = "keyword"; return cont(expression); }
+   }
+   function arrayLiteral(type) {
+     if (type == "]") return cont();
+     return pass(commasep(expressionNoComma, "]"));
+   }
++  function enumdef() {
++    return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
++  }
++  function enummember() {
++    return pass(pattern, maybeAssign);
++  }
+ 
+   function isContinuedStatement(state, textAfter) {
+     return state.lastType == "operator" || state.lastType == "," ||
+       isOperatorChar.test(textAfter.charAt(0)) ||
+       /[,.]/.test(textAfter.charAt(0));
+   }
+ 
+   function expressionAllowed(stream, state, backUp) {
+diff --git a/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js b/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
+--- a/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
++++ b/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
+@@ -378,16 +378,26 @@
+   TS("type guard",
+      "[keyword class] [def Appler] {",
+      "  [keyword static] [property assertApple]([def fruit]: [type Fruit]): [variable-2 fruit] [keyword is] [type Apple] {",
+      "    [keyword if] ([operator !]([variable-2 fruit] [keyword instanceof] [variable Apple]))",
+      "      [keyword throw] [keyword new] [variable Error]();",
+      "  }",
+      "}")
+ 
++  TS("type as variable",
++     "[variable type] [operator =] [variable x] [keyword as] [type Bar];");
++
++  TS("enum body",
++     "[keyword export] [keyword const] [keyword enum] [def CodeInspectionResultType] {",
++     "  [def ERROR] [operator =] [string 'problem_type_error'],",
++     "  [def WARNING] [operator =] [string 'problem_type_warning'],",
++     "  [def META],",
++     "}")
++
+   var jsonld_mode = CodeMirror.getMode(
+     {indentUnit: 2},
+     {name: "javascript", jsonld: true}
+   );
+   function LD(name) {
+     test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1));
+   }
+ 

+ 356 - 0
frg/work-js/mozilla-release/patches/1437147-60a1.patch

@@ -0,0 +1,356 @@
+# HG changeset patch
+# User Gijs Kruitbosch <gijskruitbosch@gmail.com>
+# Date 1518710599 0
+# Node ID da198449d15e25a8b3df5529d676d445caabd961
+# Parent  2efdf91d07393fde33ef50c3121f3d66d11a58db
+Bug 1437147 - report warnings when the sanitizer removes stuff for chrome documents, r=bz
+
+MozReview-Commit-ID: Lx1JjpYrLpD
+
+diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
+--- a/dom/base/nsContentUtils.cpp
++++ b/dom/base/nsContentUtils.cpp
+@@ -5303,17 +5303,18 @@ nsContentUtils::ParseFragmentHTML(const 
+                                        aPreventScriptExecution);
+   NS_ENSURE_SUCCESS(rv, rv);
+ 
+   if (fragment) {
+     // Don't fire mutation events for nodes removed by the sanitizer.
+     nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
+ 
+     nsTreeSanitizer sanitizer(nsIParserUtils::SanitizerAllowStyle |
+-                              nsIParserUtils::SanitizerAllowComments);
++                              nsIParserUtils::SanitizerAllowComments |
++                              nsIParserUtils::SanitizerLogRemovals);
+     sanitizer.Sanitize(fragment);
+ 
+     ErrorResult error;
+     aTargetNode->AppendChild(*fragment, error);
+     rv = error.StealNSResult();
+   }
+ 
+   return rv;
+@@ -5396,17 +5397,18 @@ nsContentUtils::ParseFragmentXML(const n
+   // returning.
+   if (aSanitize != NeverSanitize && !aDocument->AllowUnsafeHTML()) {
+     // Don't fire mutation events for nodes removed by the sanitizer.
+     nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
+ 
+     RefPtr<DocumentFragment> fragment = static_cast<DocumentFragment*>(*aReturn);
+ 
+     nsTreeSanitizer sanitizer(nsIParserUtils::SanitizerAllowStyle |
+-                              nsIParserUtils::SanitizerAllowComments);
++                              nsIParserUtils::SanitizerAllowComments |
++                              nsIParserUtils::SanitizerLogRemovals);
+     sanitizer.Sanitize(fragment);
+   }
+ 
+   return rv;
+ }
+ 
+ /* static */
+ nsresult
+diff --git a/dom/base/nsTreeSanitizer.cpp b/dom/base/nsTreeSanitizer.cpp
+--- a/dom/base/nsTreeSanitizer.cpp
++++ b/dom/base/nsTreeSanitizer.cpp
+@@ -18,16 +18,17 @@
+ #include "mozilla/css/Rule.h"
+ #include "mozilla/dom/CSSRuleList.h"
+ #include "mozilla/dom/SRIMetadata.h"
+ #include "nsCSSParser.h"
+ #include "nsCSSPropertyID.h"
+ #include "nsUnicharInputStream.h"
+ #include "nsIDOMCSSRule.h"
+ #include "nsAttrName.h"
++#include "nsIScriptError.h"
+ #include "nsIScriptSecurityManager.h"
+ #include "nsNetUtil.h"
+ #include "nsComponentManagerUtils.h"
+ #include "NullPrincipal.h"
+ #include "nsContentUtils.h"
+ #include "nsIParserUtils.h"
+ #include "nsIDocument.h"
+ #include "nsQueryObject.h"
+@@ -954,16 +955,17 @@ nsTreeSanitizer::nsTreeSanitizer(uint32_
+  , mAllowComments(aFlags & nsIParserUtils::SanitizerAllowComments)
+  , mDropNonCSSPresentation(aFlags &
+      nsIParserUtils::SanitizerDropNonCSSPresentation)
+  , mDropForms(aFlags & nsIParserUtils::SanitizerDropForms)
+  , mCidEmbedsOnly(aFlags &
+      nsIParserUtils::SanitizerCidEmbedsOnly)
+  , mDropMedia(aFlags & nsIParserUtils::SanitizerDropMedia)
+  , mFullDocument(false)
++ , mLogRemovals(aFlags & nsIParserUtils::SanitizerLogRemovals)
+ {
+   if (mCidEmbedsOnly) {
+     // Sanitizing styles for external references is not supported.
+     mAllowStyles = false;
+   }
+   if (!sElementsHTML) {
+     // Initialize lazily to avoid having to initialize at all if the user
+     // doesn't paste HTML or load feeds.
+@@ -1174,16 +1176,19 @@ nsTreeSanitizer::SanitizeStyleSheet(cons
+           didSanitize = true;
+         }
+         nsAutoString decl;
+         styleRule->GetCssText(decl);
+         aSanitized.Append(decl);
+       }
+     }
+   }
++  if (didSanitize && mLogRemovals) {
++    LogMessage("Removed some rules and/or properties from stylesheet.", aDocument);
++  }
+   return didSanitize;
+ }
+ 
+ void
+ nsTreeSanitizer::SanitizeAttributes(mozilla::dom::Element* aElement,
+                                     nsTHashtable<nsISupportsHashKey>* aAllowed,
+                                     nsIAtom** const* aURLs,
+                                     bool aAllowXLink,
+@@ -1225,16 +1230,20 @@ nsTreeSanitizer::SanitizeAttributes(mozi
+         if (decl) {
+           if (SanitizeStyleDeclaration(decl)) {
+             nsAutoString cleanValue;
+             decl->ToString(cleanValue);
+             aElement->SetAttr(kNameSpaceID_None,
+                               nsGkAtoms::style,
+                               cleanValue,
+                               false);
++            if (mLogRemovals) {
++              LogMessage("Removed -moz-binding styling from element style attribute.",
++                         aElement->OwnerDoc(), aElement);
++            }
+           }
+         }
+         continue;
+       }
+       if (aAllowDangerousSrc && nsGkAtoms::src == attrLocal) {
+         continue;
+       }
+       if (IsURL(aURLs, attrLocal)) {
+@@ -1302,16 +1311,20 @@ nsTreeSanitizer::SanitizeAttributes(mozi
+       }
+       if (nsGkAtoms::type == attrLocal || nsGkAtoms::title == attrLocal
+           || nsGkAtoms::show == attrLocal || nsGkAtoms::actuate == attrLocal) {
+         continue;
+       }
+       // else not allowed
+     }
+     aElement->UnsetAttr(kNameSpaceID_None, attrLocal, false);
++    if (mLogRemovals) {
++      LogMessage("Removed unsafe attribute.", aElement->OwnerDoc(),
++                 aElement, attrLocal);
++    }
+     // in case the attribute removal shuffled the attribute order, start the
+     // loop again.
+     --ac;
+     i = ac; // i will be decremented immediately thanks to the for loop
+   }
+ 
+   // If we've got HTML audio or video, add the controls attribute, because
+   // otherwise the content is unplayable with scripts removed.
+@@ -1370,16 +1383,20 @@ nsTreeSanitizer::SanitizeURL(mozilla::do
+         rv = secMan->CheckLoadURIWithPrincipal(sNullPrincipal, attrURI, flags);
+       }
+     } else {
+       rv = secMan->CheckLoadURIWithPrincipal(sNullPrincipal, attrURI, flags);
+     }
+   }
+   if (NS_FAILED(rv)) {
+     aElement->UnsetAttr(aNamespace, aLocalName, false);
++    if (mLogRemovals) {
++      LogMessage("Removed unsafe URI from element attribute.",
++                 aElement->OwnerDoc(), aElement, aLocalName);
++    }
+     return true;
+   }
+   return false;
+ }
+ 
+ void
+ nsTreeSanitizer::Sanitize(nsIContent* aFragment)
+ {
+@@ -1417,16 +1434,19 @@ nsTreeSanitizer::SanitizeChildren(nsINod
+   while (node) {
+     if (node->IsElement()) {
+       mozilla::dom::Element* elt = node->AsElement();
+       mozilla::dom::NodeInfo* nodeInfo = node->NodeInfo();
+       nsIAtom* localName = nodeInfo->NameAtom();
+       int32_t ns = nodeInfo->NamespaceID();
+ 
+       if (MustPrune(ns, localName, elt)) {
++        if (mLogRemovals) {
++          LogMessage("Removing unsafe node.", elt->OwnerDoc(), elt);
++        }
+         RemoveAllAttributes(elt);
+         nsIContent* descendant = node;
+         while ((descendant = descendant->GetNextNode(node))) {
+           if (descendant->IsElement()) {
+             RemoveAllAttributes(descendant->AsElement());
+           }
+         }
+         nsIContent* next = node->GetNextNonChildNode(aRoot);
+@@ -1449,16 +1469,20 @@ nsTreeSanitizer::SanitizeChildren(nsINod
+         nsCOMPtr<nsIURI> baseURI = node->GetBaseURI();
+         if (SanitizeStyleSheet(styleText,
+                                sanitizedStyle,
+                                aRoot->OwnerDoc(),
+                                baseURI)) {
+           nsContentUtils::SetNodeTextContent(node, sanitizedStyle, true);
+         } else {
+           // If the node had non-text child nodes, this operation zaps those.
++          //XXXgijs: if we're logging, we should theoretically report about
++          // this, but this way of removing those items doesn't allow for that
++          // to happen. Seems less likely to be a problem for actual chrome
++          // consumers though.
+           nsContentUtils::SetNodeTextContent(node, styleText, true);
+         }
+         if (ns == kNameSpaceID_XHTML) {
+           SanitizeAttributes(elt,
+                              sAttributesHTML,
+                              kURLAttributesHTML,
+                              false,
+                              mAllowStyles,
+@@ -1470,16 +1494,20 @@ nsTreeSanitizer::SanitizeChildren(nsINod
+                              true,
+                              mAllowStyles,
+                              false);
+         }
+         node = node->GetNextNonChildNode(aRoot);
+         continue;
+       }
+       if (MustFlatten(ns, localName)) {
++        if (mLogRemovals) {
++          LogMessage("Flattening unsafe node (descendants are preserved).",
++                     elt->OwnerDoc(), elt);
++        }
+         RemoveAllAttributes(elt);
+         nsCOMPtr<nsIContent> next = node->GetNextNode(aRoot);
+         nsCOMPtr<nsIContent> parent = node->GetParent();
+         nsCOMPtr<nsIContent> child; // Must keep the child alive during move
+         ErrorResult rv;
+         while ((child = node->GetFirstChild())) {
+           nsCOMPtr<nsINode> refNode = node;
+           parent->InsertBefore(*child, refNode, rv);
+@@ -1548,16 +1576,37 @@ void nsTreeSanitizer::RemoveAllAttribute
+       mozilla::dom::Element* elt = node->AsElement();
+       RemoveAllAttributes(elt);
+     }
+     node = node->GetNextNode(aElement);
+   }
+ }
+ 
+ void
++nsTreeSanitizer::LogMessage(const char* aMessage, nsIDocument* aDoc,
++                            Element* aElement, nsIAtom* aAttr)
++{
++  if (mLogRemovals) {
++    nsAutoString msg;
++    msg.Assign(NS_ConvertASCIItoUTF16(aMessage));
++    if (aElement) {
++      msg.Append(NS_LITERAL_STRING(" Element: ") + aElement->LocalName() +
++                 NS_LITERAL_STRING("."));
++    }
++    if (aAttr) {
++      msg.Append(NS_LITERAL_STRING(" Attribute: ") +
++                 nsDependentAtomString(aAttr) + NS_LITERAL_STRING("."));
++    }
++
++    nsContentUtils::ReportToConsoleNonLocalized(
++        msg, nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM"), aDoc);
++  }
++}
++
++void
+ nsTreeSanitizer::InitializeStatics()
+ {
+   NS_PRECONDITION(!sElementsHTML, "Initializing a second time.");
+ 
+   sElementsHTML = new nsTHashtable<nsISupportsHashKey>(ArrayLength(kElementsHTML));
+   for (uint32_t i = 0; kElementsHTML[i]; i++) {
+     sElementsHTML->PutEntry(*kElementsHTML[i]);
+   }
+diff --git a/dom/base/nsTreeSanitizer.h b/dom/base/nsTreeSanitizer.h
+--- a/dom/base/nsTreeSanitizer.h
++++ b/dom/base/nsTreeSanitizer.h
+@@ -80,16 +80,21 @@ class MOZ_STACK_CLASS nsTreeSanitizer {
+      */
+     bool mDropMedia;
+ 
+     /**
+      * Whether we are sanitizing a full document (as opposed to a fragment).
+      */
+     bool mFullDocument;
+ 
++    /**
++     * Whether we should notify to the console for anything that's stripped.
++     */
++    bool mLogRemovals;
++
+     void SanitizeChildren(nsINode* aRoot);
+ 
+     /**
+      * Queries if an element must be replaced with its children.
+      * @param aNamespace the namespace of the element the question is about
+      * @param aLocal the local name of the element the question is about
+      * @return true if the element must be replaced with its children and
+      *         false if the element is to be kept
+@@ -184,16 +189,30 @@ class MOZ_STACK_CLASS nsTreeSanitizer {
+ 
+     /**
+      * Removes all attributes from the descendants of an element but not from
+      * the element itself.
+      */
+     void RemoveAllAttributesFromDescendants(mozilla::dom::Element* aElement);
+ 
+     /**
++     * Log a Console Service message to indicate we removed something.
++     * If you pass an element and/or attribute, their information will
++     * be appended to the message.
++     *
++     * @param aMessage   the basic message to log.
++     * @param aDocument  the base document we're modifying
++     *                   (used for the error message)
++     * @param aElement   optional, the element being removed or modified.
++     * @param aAttribute optional, the attribute being removed or modified.
++     */
++    void LogMessage(const char* aMessage, nsIDocument* aDoc,
++                    Element* aElement = nullptr, nsIAtom* aAttr = nullptr);
++
++    /**
+      * The whitelist of HTML elements.
+      */
+     static nsTHashtable<nsISupportsHashKey>* sElementsHTML;
+ 
+     /**
+      * The whitelist of non-presentational HTML attributes.
+      */
+     static nsTHashtable<nsISupportsHashKey>* sAttributesHTML;
+diff --git a/parser/html/nsIParserUtils.idl b/parser/html/nsIParserUtils.idl
+--- a/parser/html/nsIParserUtils.idl
++++ b/parser/html/nsIParserUtils.idl
+@@ -54,16 +54,22 @@ interface nsIParserUtils : nsISupports
+ 
+   /**
+    * Flag for sanitizer: Drop <img>, <video>, <audio> and <source> and flatten
+    * out SVG.
+    */
+   const unsigned long SanitizerDropMedia = (1 << 5);
+ 
+   /**
++   * Flag for sanitizer: Log messages to the console for everything that gets
++   * sanitized
++   */
++  const unsigned long SanitizerLogRemovals = (1 << 6);
++
++  /**
+    * Parses a string into an HTML document, sanitizes the document and 
+    * returns the result serialized to a string.
+    *
+    * The sanitizer is designed to protect against XSS when sanitized content
+    * is inserted into a different-origin context without an iframe-equivalent
+    * sandboxing mechanism.
+    *
+    * By default, the sanitizer doesn't try to avoid leaking information that 

+ 2235 - 0
frg/work-js/mozilla-release/patches/1440321-1a-aboutdebugging-61a1.patch

@@ -0,0 +1,2235 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  c79e9936689156536aa3268e5b760de7d18f2743
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1a. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/aboutdebugging/modules/worker.js b/devtools/client/aboutdebugging/modules/worker.js
+--- a/devtools/client/aboutdebugging/modules/worker.js
++++ b/devtools/client/aboutdebugging/modules/worker.js
+@@ -1,16 +1,14 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+-const { Task } = require("devtools/shared/task");
+-
+ loader.lazyRequireGetter(this, "gDevTools",
+   "devtools/client/framework/devtools", true);
+ loader.lazyRequireGetter(this, "TargetFactory",
+   "devtools/client/framework/target", true);
+ loader.lazyRequireGetter(this, "Toolbox",
+   "devtools/client/framework/toolbox", true);
+ 
+ /**
+@@ -37,41 +35,41 @@ exports.debugWorker = function(client, w
+  *
+  * @param {DebuggerClient} client
+  * @return {Object}
+  *         - {Array} registrations
+  *           Array of ServiceWorkerRegistrationActor forms
+  *         - {Array} workers
+  *           Array of WorkerActor forms
+  */
+-exports.getWorkerForms = Task.async(function* (client) {
++exports.getWorkerForms = async function(client) {
+   let registrations = [];
+   let workers = [];
+ 
+   try {
+     // List service worker registrations
+     ({ registrations } =
+-      yield client.mainRoot.listServiceWorkerRegistrations());
++      await client.mainRoot.listServiceWorkerRegistrations());
+ 
+     // List workers from the Parent process
+-    ({ workers } = yield client.mainRoot.listWorkers());
++    ({ workers } = await client.mainRoot.listWorkers());
+ 
+     // And then from the Child processes
+-    let { processes } = yield client.mainRoot.listProcesses();
++    let { processes } = await client.mainRoot.listProcesses();
+     for (let process of processes) {
+       // Ignore parent process
+       if (process.parent) {
+         continue;
+       }
+-      let { form } = yield client.getProcess(process.id);
++      let { form } = await client.getProcess(process.id);
+       let processActor = form.actor;
+-      let response = yield client.request({
++      let response = await client.request({
+         to: processActor,
+         type: "listWorkers"
+       });
+       workers = workers.concat(response.workers);
+     }
+   } catch (e) {
+     // Something went wrong, maybe our client is disconnected?
+   }
+ 
+   return { registrations, workers };
+-});
++};
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_debug_bootstrapped.js b/devtools/client/aboutdebugging/test/browser_addons_debug_bootstrapped.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_debug_bootstrapped.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_debug_bootstrapped.js
+@@ -10,33 +10,33 @@ PromiseTestUtils.whitelistRejectionsGlob
+ // Avoid test timeouts that can occur while waiting for the "addon-console-works" message.
+ requestLongerTimeout(2);
+ 
+ const ADDON_ID = "test-devtools@mozilla.org";
+ const ADDON_NAME = "test-devtools";
+ 
+ const { BrowserToolboxProcess } = ChromeUtils.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});
+ 
+-add_task(function* () {
+-  yield new Promise(resolve => {
++add_task(async function() {
++  await new Promise(resolve => {
+     let options = {"set": [
+       // Force enabling of addons debugging
+       ["devtools.chrome.enabled", true],
+       ["devtools.debugger.remote-enabled", true],
+       // Disable security prompt
+       ["devtools.debugger.prompt-connection", false],
+       // Enable Browser toolbox test script execution via env variable
+       ["devtools.browser-toolbox.allow-unsafe-script", true],
+     ]};
+     SpecialPowers.pushPrefEnv(options, resolve);
+   });
+ 
+-  let { tab, document } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
+-  yield installAddon({
++  let { tab, document } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
++  await installAddon({
+     document,
+     path: "addons/unpacked/install.rdf",
+     name: ADDON_NAME,
+   });
+ 
+   // Retrieve the DEBUG button for the addon
+   let names = getInstalledAddonNames(document);
+   let name = names.filter(element => element.textContent === ADDON_NAME)[0];
+@@ -72,17 +72,17 @@ add_task(function* () {
+   registerCleanupFunction(() => {
+     env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
+   });
+ 
+   let onToolboxClose = BrowserToolboxProcess.once("close");
+ 
+   debugBtn.click();
+ 
+-  yield onCustomMessage;
++  await onCustomMessage;
+   ok(true, "Received the notification message from the bootstrap.js function");
+ 
+-  yield onToolboxClose;
++  await onToolboxClose;
+   ok(true, "Addon toolbox closed");
+ 
+-  yield uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
+-  yield closeAboutDebugging(tab);
++  await uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_debug_info.js b/devtools/client/aboutdebugging/test/browser_addons_debug_info.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_debug_info.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_debug_info.js
+@@ -5,43 +5,43 @@ const UUID_REGEX = /^([0-9a-f]{8}-[0-9a-
+ function testFilePath(container, expectedFilePath) {
+   // Verify that the path to the install location is shown next to its label.
+   let filePath = container.querySelector(".file-path");
+   ok(filePath, "file path is in DOM");
+   ok(filePath.textContent.endsWith(expectedFilePath), "file path is set correctly");
+   is(filePath.previousElementSibling.textContent, "Location", "file path has label");
+ }
+ 
+-add_task(function* testLegacyAddon() {
++add_task(async function testLegacyAddon() {
+   let addonId = "test-devtools@mozilla.org";
+   let addonName = "test-devtools";
+-  let { tab, document } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
++  let { tab, document } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
+ 
+-  yield installAddon({
++  await installAddon({
+     document,
+     path: "addons/unpacked/install.rdf",
+     name: addonName,
+   });
+ 
+   let container = document.querySelector(`[data-addon-id="${addonId}"]`);
+   testFilePath(container, "browser/devtools/client/aboutdebugging/test/addons/unpacked/");
+ 
+-  yield uninstallAddon({document, id: addonId, name: addonName});
++  await uninstallAddon({document, id: addonId, name: addonName});
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+-add_task(function* testWebExtension() {
++add_task(async function testWebExtension() {
+   let addonId = "test-devtools-webextension-nobg@mozilla.org";
+   let addonName = "test-devtools-webextension-nobg";
+-  let { tab, document } = yield openAboutDebugging("addons");
++  let { tab, document } = await openAboutDebugging("addons");
+ 
+-  yield waitForInitialAddonList(document);
+-  yield installAddon({
++  await waitForInitialAddonList(document);
++  await installAddon({
+     document,
+     path: "addons/test-devtools-webextension-nobg/manifest.json",
+     name: addonName,
+     isWebExtension: true
+   });
+ 
+   let container = document.querySelector(`[data-addon-id="${addonId}"]`);
+   testFilePath(container, "/test/addons/test-devtools-webextension-nobg/");
+@@ -50,27 +50,27 @@ add_task(function* testWebExtension() {
+   ok(extensionID.textContent === "test-devtools-webextension-nobg@mozilla.org");
+ 
+   let internalUUID = container.querySelector(".internal-uuid span");
+   ok(internalUUID.textContent.match(UUID_REGEX), "internalUUID is correct");
+ 
+   let manifestURL = container.querySelector(".manifest-url");
+   ok(manifestURL.href.startsWith("moz-extension://"), "href for manifestURL exists");
+ 
+-  yield uninstallAddon({document, id: addonId, name: addonName});
++  await uninstallAddon({document, id: addonId, name: addonName});
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+-add_task(function* testTemporaryWebExtension() {
++add_task(async function testTemporaryWebExtension() {
+   let addonName = "test-devtools-webextension-noid";
+-  let { tab, document } = yield openAboutDebugging("addons");
++  let { tab, document } = await openAboutDebugging("addons");
+ 
+-  yield waitForInitialAddonList(document);
+-  yield installAddon({
++  await waitForInitialAddonList(document);
++  await installAddon({
+     document,
+     path: "addons/test-devtools-webextension-noid/manifest.json",
+     name: addonName,
+     isWebExtension: true
+   });
+ 
+   let addons = document.querySelectorAll("#temporary-extensions .addon-target-container");
+   // Assuming that our temporary add-on is now at the top.
+@@ -78,43 +78,43 @@ add_task(function* testTemporaryWebExten
+   let addonId = container.dataset.addonId;
+ 
+   let extensionID = container.querySelector(".extension-id span");
+   ok(extensionID.textContent.endsWith("@temporary-addon"));
+ 
+   let temporaryID = container.querySelector(".temporary-id-url");
+   ok(temporaryID, "Temporary ID message does appear");
+ 
+-  yield uninstallAddon({document, id: addonId, name: addonName});
++  await uninstallAddon({document, id: addonId, name: addonName});
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+-add_task(function* testUnknownManifestProperty() {
++add_task(async function testUnknownManifestProperty() {
+   let addonId = "test-devtools-webextension-unknown-prop@mozilla.org";
+   let addonName = "test-devtools-webextension-unknown-prop";
+-  let { tab, document } = yield openAboutDebugging("addons");
++  let { tab, document } = await openAboutDebugging("addons");
+ 
+-  yield waitForInitialAddonList(document);
+-  yield installAddon({
++  await waitForInitialAddonList(document);
++  await installAddon({
+     document,
+     path: "addons/test-devtools-webextension-unknown-prop/manifest.json",
+     name: addonName,
+     isWebExtension: true
+   });
+ 
+   info("Wait until the addon appears in about:debugging");
+-  let container = yield waitUntilAddonContainer(addonName, document);
++  let container = await waitUntilAddonContainer(addonName, document);
+ 
+   info("Wait until the installation message appears for the new addon");
+-  yield waitUntilElement(".addon-target-messages", container);
++  await waitUntilElement(".addon-target-messages", container);
+ 
+   let messages = container.querySelectorAll(".addon-target-message");
+   ok(messages.length === 1, "there is one message");
+   ok(messages[0].textContent.match(/Error processing browser_actions/),
+      "the message is helpful");
+   ok(messages[0].classList.contains("addon-target-warning-message"),
+      "the message is a warning");
+ 
+-  yield uninstallAddon({document, id: addonId, name: addonName});
++  await uninstallAddon({document, id: addonId, name: addonName});
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension.js b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension.js
+@@ -19,20 +19,20 @@ const {
+   BrowserToolboxProcess
+ } = ChromeUtils.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});
+ 
+ /**
+  * This test file ensures that the webextension addon developer toolbox:
+  * - when the debug button is clicked on a webextension, the opened toolbox
+  *   has a working webconsole with the background page as default target;
+  */
+-add_task(function* testWebExtensionsToolboxWebConsole() {
++add_task(async function testWebExtensionsToolboxWebConsole() {
+   let {
+     tab, document, debugBtn,
+-  } = yield setupTestAboutDebuggingWebExtension(ADDON_NAME, ADDON_MANIFEST_PATH);
++  } = await setupTestAboutDebuggingWebExtension(ADDON_NAME, ADDON_MANIFEST_PATH);
+ 
+   // Be careful, this JS function is going to be executed in the addon toolbox,
+   // which lives in another process. So do not try to use any scope variable!
+   let env = Cc["@mozilla.org/process/environment;1"]
+               .getService(Ci.nsIEnvironment);
+   let testScript = function() {
+     /* eslint-disable no-undef */
+     function findMessages(hud, text, selector = ".message") {
+@@ -68,14 +68,14 @@ add_task(function* testWebExtensionsTool
+   registerCleanupFunction(() => {
+     env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
+   });
+ 
+   let onToolboxClose = BrowserToolboxProcess.once("close");
+ 
+   debugBtn.click();
+ 
+-  yield onToolboxClose;
++  await onToolboxClose;
+   ok(true, "Addon toolbox closed");
+ 
+-  yield uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
+-  yield closeAboutDebugging(tab);
++  await uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_inspector.js b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_inspector.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_inspector.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_inspector.js
+@@ -18,20 +18,20 @@ const {
+   BrowserToolboxProcess
+ } = ChromeUtils.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});
+ 
+ /**
+  * This test file ensures that the webextension addon developer toolbox:
+  * - the webextension developer toolbox has a working Inspector panel, with the
+  *   background page as default target;
+  */
+-add_task(function* testWebExtensionsToolboxInspector() {
++add_task(async function testWebExtensionsToolboxInspector() {
+   let {
+     tab, document, debugBtn,
+-  } = yield setupTestAboutDebuggingWebExtension(ADDON_NAME, ADDON_PATH);
++  } = await setupTestAboutDebuggingWebExtension(ADDON_NAME, ADDON_PATH);
+ 
+   // Be careful, this JS function is going to be executed in the addon toolbox,
+   // which lives in another process. So do not try to use any scope variable!
+   let env = Cc["@mozilla.org/process/environment;1"]
+         .getService(Ci.nsIEnvironment);
+   let testScript = function() {
+     /* eslint-disable no-undef */
+     toolbox.selectTool("inspector")
+@@ -73,15 +73,15 @@ add_task(function* testWebExtensionsTool
+   };
+   env.set("MOZ_TOOLBOX_TEST_SCRIPT", "new " + testScript);
+   registerCleanupFunction(() => {
+     env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
+   });
+ 
+   let onToolboxClose = BrowserToolboxProcess.once("close");
+   debugBtn.click();
+-  yield onToolboxClose;
++  await onToolboxClose;
+ 
+   ok(true, "Addon toolbox closed");
+ 
+-  yield uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
+-  yield closeAboutDebugging(tab);
++  await uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_nobg.js b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_nobg.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_nobg.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_nobg.js
+@@ -20,20 +20,20 @@ const {
+ 
+ /**
+  * This test file ensures that the webextension addon developer toolbox:
+  * - the webextension developer toolbox is connected to a fallback page when the
+  *   background page is not available (and in the fallback page document body contains
+  *   the expected message, which warns the user that the current page is not a real
+  *   webextension context);
+  */
+-add_task(function* testWebExtensionsToolboxNoBackgroundPage() {
++add_task(async function testWebExtensionsToolboxNoBackgroundPage() {
+   let {
+     tab, document, debugBtn,
+-  } = yield setupTestAboutDebuggingWebExtension(ADDON_NOBG_NAME, ADDON_NOBG_PATH);
++  } = await setupTestAboutDebuggingWebExtension(ADDON_NOBG_NAME, ADDON_NOBG_PATH);
+ 
+   // Be careful, this JS function is going to be executed in the addon toolbox,
+   // which lives in another process. So do not try to use any scope variable!
+   let env = Cc["@mozilla.org/process/environment;1"]
+         .getService(Ci.nsIEnvironment);
+   let testScript = function() {
+     /* eslint-disable no-undef */
+     toolbox.selectTool("inspector")
+@@ -75,15 +75,15 @@ add_task(function* testWebExtensionsTool
+   };
+   env.set("MOZ_TOOLBOX_TEST_SCRIPT", "new " + testScript);
+   registerCleanupFunction(() => {
+     env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
+   });
+ 
+   let onToolboxClose = BrowserToolboxProcess.once("close");
+   debugBtn.click();
+-  yield onToolboxClose;
++  await onToolboxClose;
+ 
+   ok(true, "Addon toolbox closed");
+ 
+-  yield uninstallAddon({document, id: ADDON_NOBG_ID, name: ADDON_NOBG_NAME});
+-  yield closeAboutDebugging(tab);
++  await uninstallAddon({document, id: ADDON_NOBG_ID, name: ADDON_NOBG_NAME});
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
+@@ -36,17 +36,17 @@ const {
+ /**
+  * Returns the widget id for an extension with the passed id.
+  */
+ function makeWidgetId(id) {
+   id = id.toLowerCase();
+   return id.replace(/[^a-z0-9_-]/g, "_");
+ }
+ 
+-add_task(function* testWebExtensionsToolboxSwitchToPopup() {
++add_task(async function testWebExtensionsToolboxSwitchToPopup() {
+   let onReadyForOpenPopup;
+   let onPopupCustomMessage;
+ 
+   Management.on("startup", function listener(event, extension) {
+     if (extension.name != ADDON_NAME) {
+       return;
+     }
+ 
+@@ -73,17 +73,17 @@ add_task(function* testWebExtensionsTool
+ 
+     // Wait for a notification sent by a script evaluated the test addon via
+     // the web console.
+     onPopupCustomMessage = waitForExtensionTestMessage("popupPageFunctionCalled");
+   });
+ 
+   let {
+     tab, document, debugBtn,
+-  } = yield setupTestAboutDebuggingWebExtension(ADDON_NAME, ADDON_MANIFEST_PATH);
++  } = await setupTestAboutDebuggingWebExtension(ADDON_NAME, ADDON_MANIFEST_PATH);
+ 
+   // Be careful, this JS function is going to be executed in the addon toolbox,
+   // which lives in another process. So do not try to use any scope variable!
+   let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+ 
+   let testScript = function() {
+     /* eslint-disable no-undef */
+ 
+@@ -160,30 +160,30 @@ add_task(function* testWebExtensionsTool
+   registerCleanupFunction(() => {
+     env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
+   });
+ 
+   let onToolboxClose = BrowserToolboxProcess.once("close");
+ 
+   debugBtn.click();
+ 
+-  yield onReadyForOpenPopup;
++  await onReadyForOpenPopup;
+ 
+   let browserActionId = makeWidgetId(ADDON_ID) + "-browser-action";
+   let browserActionEl = window.document.getElementById(browserActionId);
+ 
+   ok(browserActionEl, "Got the browserAction button from the browser UI");
+   browserActionEl.click();
+   info("Clicked on the browserAction button");
+ 
+-  let args = yield onPopupCustomMessage;
++  let args = await onPopupCustomMessage;
+   ok(true, "Received console message from the popup page function as expected");
+   is(args[0], "popupPageFunctionCalled", "Got the expected console message");
+   is(args[1] && args[1].name, ADDON_NAME,
+      "Got the expected manifest from WebExtension API");
+ 
+-  yield onToolboxClose;
++  await onToolboxClose;
+ 
+   ok(true, "Addon toolbox closed");
+ 
+-  yield uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
+-  yield closeAboutDebugging(tab);
++  await uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_debugging_initial_state.js b/devtools/client/aboutdebugging/test/browser_addons_debugging_initial_state.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_debugging_initial_state.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_debugging_initial_state.js
+@@ -25,49 +25,49 @@ const TEST_DATA = [
+     expected: false,
+   }, {
+     chromeEnabled: true,
+     debuggerRemoteEnable: true,
+     expected: true,
+   }
+ ];
+ 
+-add_task(function* () {
++add_task(async function() {
+   for (let testData of TEST_DATA) {
+-    yield testCheckboxState(testData);
++    await testCheckboxState(testData);
+   }
+ });
+ 
+-function* testCheckboxState(testData) {
++async function testCheckboxState(testData) {
+   info("Set preferences as defined by the current test data.");
+-  yield new Promise(resolve => {
++  await new Promise(resolve => {
+     let options = {"set": [
+       ["devtools.chrome.enabled", testData.chromeEnabled],
+       ["devtools.debugger.remote-enabled", testData.debuggerRemoteEnable],
+     ]};
+     SpecialPowers.pushPrefEnv(options, resolve);
+   });
+ 
+-  let { tab, document } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
++  let { tab, document } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
+ 
+   info("Install a test addon.");
+-  yield installAddon({
++  await installAddon({
+     document,
+     path: "addons/unpacked/install.rdf",
+     name: ADDON_NAME,
+   });
+ 
+   info("Test checkbox checked state.");
+   let addonDebugCheckbox = document.querySelector("#enable-addon-debugging");
+   is(addonDebugCheckbox.checked, testData.expected,
+     "Addons debugging checkbox should be in expected state.");
+ 
+   info("Test debug buttons disabled state.");
+   let debugButtons = [...document.querySelectorAll("#addons .debug-button")];
+   ok(debugButtons.every(b => b.disabled != testData.expected),
+     "Debug buttons should be in the expected state");
+ 
+   info("Uninstall test addon installed earlier.");
+-  yield uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
++  await uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ }
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_install.js b/devtools/client/aboutdebugging/test/browser_addons_install.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_install.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_install.js
+@@ -29,100 +29,100 @@ function mockFilePicker(window, file) {
+ function promiseWriteWebManifestForExtension(manifest, dir) {
+   let files = {
+     "manifest.json": JSON.stringify(manifest),
+   };
+   return AddonTestUtils.promiseWriteFilesToExtension(
+     dir.path, manifest.applications.gecko.id, files, true);
+ }
+ 
+-add_task(function* testLegacyInstallSuccess() {
+-  let { tab, document } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
++add_task(async function testLegacyInstallSuccess() {
++  let { tab, document } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
+ 
+   // Install this add-on, and verify that it appears in the about:debugging UI
+-  yield installAddon({
++  await installAddon({
+     document,
+     path: "addons/unpacked/install.rdf",
+     name: ADDON_NAME,
+   });
+ 
+   // Install the add-on, and verify that it disappears in the about:debugging UI
+-  yield uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
++  await uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+-add_task(function* testWebextensionInstallError() {
+-  let { tab, document, window } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
++add_task(async function testWebextensionInstallError() {
++  let { tab, document, window } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
+ 
+   // Trigger the file picker by clicking on the button
+   mockFilePicker(window, getSupportsFile("addons/bad/manifest.json").file);
+   document.getElementById("load-addon-from-file").click();
+ 
+   info("wait for the install error to appear");
+   let top = document.querySelector(".addons-top");
+-  yield waitUntilElement(".addons-install-error", top);
++  await waitUntilElement(".addons-install-error", top);
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+-add_task(function* testWebextensionInstallErrorRetry() {
+-  let { tab, document, window } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
++add_task(async function testWebextensionInstallErrorRetry() {
++  let { tab, document, window } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
+ 
+   let tempdir = AddonTestUtils.tempDir.clone();
+   let addonId = "invalid-addon-install-retry@mozilla.org";
+   let addonName = "invalid-addon-install-retry";
+   let manifest = {
+     name: addonName,
+     description: "test invalid-addon-install-retry",
+     // eslint-disable-next-line camelcase
+     manifest_version: 2,
+     version: "1.0",
+     applications: { gecko: { id: addonId } },
+     // These should all be wrapped in arrays.
+     // eslint-disable-next-line camelcase
+     content_scripts: { matches: "http://*/", js: "foo.js" },
+   };
+ 
+-  yield promiseWriteWebManifestForExtension(manifest, tempdir);
++  await promiseWriteWebManifestForExtension(manifest, tempdir);
+ 
+   // Mock the file picker to select a test addon.
+   let manifestFile = tempdir.clone();
+   manifestFile.append(addonId, "manifest.json");
+   mockFilePicker(window, manifestFile);
+ 
+   // Trigger the file picker by clicking on the button.
+   document.getElementById("load-addon-from-file").click();
+ 
+   info("wait for the install error to appear");
+   let top = document.querySelector(".addons-top");
+-  yield waitUntilElement(".addons-install-error", top);
++  await waitUntilElement(".addons-install-error", top);
+ 
+   let retryButton = document.querySelector("button.addons-install-retry");
+   is(retryButton.textContent, "Retry", "Retry button has a good label");
+ 
+   // Fix the manifest so the add-on will install.
+   // eslint-disable-next-line camelcase
+   manifest.content_scripts = [{
+     matches: ["http://*/"],
+     js: ["foo.js"],
+   }];
+-  yield promiseWriteWebManifestForExtension(manifest, tempdir);
++  await promiseWriteWebManifestForExtension(manifest, tempdir);
+ 
+   let addonEl = document.querySelector(`[data-addon-id="${addonId}"]`);
+   // Verify this add-on isn't installed yet.
+   ok(!addonEl, "Addon is not installed yet");
+ 
+   // Retry the install.
+   retryButton.click();
+ 
+   info("Wait for the add-on to be shown");
+-  yield waitUntilElement(`[data-addon-id="${addonId}"]`, document);
++  await waitUntilElement(`[data-addon-id="${addonId}"]`, document);
+   info("Addon is installed");
+ 
+   // Install the add-on, and verify that it disappears in the about:debugging UI
+-  yield uninstallAddon({document, id: addonId, name: addonName});
++  await uninstallAddon({document, id: addonId, name: addonName});
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_reload.js b/devtools/client/aboutdebugging/test/browser_addons_reload.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_reload.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_reload.js
+@@ -55,21 +55,21 @@ class TempWebExt {
+     fos.close();
+   }
+ 
+   remove() {
+     return this.tmpDir.remove(true);
+   }
+ }
+ 
+-add_task(function* reloadButtonReloadsAddon() {
+-  const { tab, document, window } = yield openAboutDebugging("addons");
++add_task(async function reloadButtonReloadsAddon() {
++  const { tab, document, window } = await openAboutDebugging("addons");
+   const { AboutDebugging } = window;
+-  yield waitForInitialAddonList(document);
+-  yield installAddon({
++  await waitForInitialAddonList(document);
++  await installAddon({
+     document,
+     path: "addons/unpacked/install.rdf",
+     name: ADDON_NAME,
+   });
+ 
+   const reloadButton = getReloadButton(document, ADDON_NAME);
+   is(reloadButton.title, "", "Reload button should not have a tooltip");
+   const onInstalled = promiseAddonEvent("onInstalled");
+@@ -79,86 +79,86 @@ add_task(function* reloadButtonReloadsAd
+       Services.obs.removeObserver(listener, ADDON_NAME);
+       info("Add-on was re-installed: " + ADDON_NAME);
+       done();
+     }, ADDON_NAME);
+   });
+ 
+   let reloaded = once(AboutDebugging, "addon-reload");
+   reloadButton.click();
+-  yield reloaded;
++  await reloaded;
+ 
+-  const [reloadedAddon] = yield onInstalled;
++  const [reloadedAddon] = await onInstalled;
+   is(reloadedAddon.name, ADDON_NAME,
+      "Add-on was reloaded: " + reloadedAddon.name);
+ 
+-  yield onBootstrapInstallCalled;
+-  yield tearDownAddon(reloadedAddon);
+-  yield closeAboutDebugging(tab);
++  await onBootstrapInstallCalled;
++  await tearDownAddon(reloadedAddon);
++  await closeAboutDebugging(tab);
+ });
+ 
+-add_task(function* reloadButtonRefreshesMetadata() {
+-  const { tab, document, window } = yield openAboutDebugging("addons");
++add_task(async function reloadButtonRefreshesMetadata() {
++  const { tab, document, window } = await openAboutDebugging("addons");
+   const { AboutDebugging } = window;
+-  yield waitForInitialAddonList(document);
++  await waitForInitialAddonList(document);
+ 
+   const manifestBase = {
+     "manifest_version": 2,
+     "name": "Temporary web extension",
+     "version": "1.0",
+     "applications": {
+       "gecko": {
+         "id": ADDON_ID
+       }
+     }
+   };
+ 
+   const tempExt = new TempWebExt(ADDON_ID);
+   tempExt.writeManifest(manifestBase);
+ 
+   const onInstalled = promiseAddonEvent("onInstalled");
+-  yield AddonManager.installTemporaryAddon(tempExt.sourceDir);
++  await AddonManager.installTemporaryAddon(tempExt.sourceDir);
+ 
+   info("Wait until addon onInstalled event is received");
+-  yield onInstalled;
++  await onInstalled;
+ 
+   info("Wait until addon appears in about:debugging#addons");
+-  yield waitUntilAddonContainer("Temporary web extension", document);
++  await waitUntilAddonContainer("Temporary web extension", document);
+ 
+   const newName = "Temporary web extension (updated)";
+   tempExt.writeManifest(Object.assign({}, manifestBase, {name: newName}));
+ 
+   // Wait for the add-on list to be updated with the reloaded name.
+   const onReInstall = promiseAddonEvent("onInstalled");
+   const reloadButton = getReloadButton(document, manifestBase.name);
+   let reloaded = once(AboutDebugging, "addon-reload");
+   reloadButton.click();
+-  yield reloaded;
++  await reloaded;
+ 
+   info("Wait until addon onInstalled event is received again");
+-  const [reloadedAddon] = yield onReInstall;
++  const [reloadedAddon] = await onReInstall;
+ 
+   info("Wait until addon name is updated in about:debugging#addons");
+-  yield waitUntilAddonContainer(newName, document);
++  await waitUntilAddonContainer(newName, document);
+ 
+-  yield tearDownAddon(reloadedAddon);
++  await tearDownAddon(reloadedAddon);
+   tempExt.remove();
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+-add_task(function* onlyTempInstalledAddonsCanBeReloaded() {
+-  const { tab, document } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
+-  yield installAddonWithManager(getSupportsFile("addons/bug1273184.xpi").file);
++add_task(async function onlyTempInstalledAddonsCanBeReloaded() {
++  const { tab, document } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
++  await installAddonWithManager(getSupportsFile("addons/bug1273184.xpi").file);
+ 
+   info("Wait until addon appears in about:debugging#addons");
+-  yield waitUntilAddonContainer(PACKAGED_ADDON_NAME, document);
++  await waitUntilAddonContainer(PACKAGED_ADDON_NAME, document);
+ 
+   info("Retrieved the installed addon from the addon manager");
+-  const addon = yield getAddonByID(PACKAGED_ADDON_ID);
++  const addon = await getAddonByID(PACKAGED_ADDON_ID);
+   is(addon.name, PACKAGED_ADDON_NAME, "Addon name is correct");
+ 
+   const reloadButton = getReloadButton(document, addon.name);
+   ok(!reloadButton, "There should not be a reload button");
+ 
+-  yield tearDownAddon(addon);
+-  yield closeAboutDebugging(tab);
++  await tearDownAddon(addon);
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_remove.js b/devtools/client/aboutdebugging/test/browser_addons_remove.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_remove.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_remove.js
+@@ -7,75 +7,75 @@ const PACKAGED_ADDON_NAME = "bug 1273184
+ function getTargetEl(document, id) {
+   return document.querySelector(`[data-addon-id="${id}"]`);
+ }
+ 
+ function getRemoveButton(document, id) {
+   return document.querySelector(`[data-addon-id="${id}"] .uninstall-button`);
+ }
+ 
+-add_task(function* removeLegacyExtension() {
++add_task(async function removeLegacyExtension() {
+   const addonID = "test-devtools@mozilla.org";
+   const addonName = "test-devtools";
+ 
+-  const { tab, document } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
++  const { tab, document } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
+ 
+   // Install this add-on, and verify that it appears in the about:debugging UI
+-  yield installAddon({
++  await installAddon({
+     document,
+     path: "addons/unpacked/install.rdf",
+     name: addonName,
+   });
+ 
+   ok(getTargetEl(document, addonID), "add-on is shown");
+ 
+   info("Click on the remove button and wait until the addon container is removed");
+   getRemoveButton(document, addonID).click();
+-  yield waitUntil(() => !getTargetEl(document, addonID), 100);
++  await waitUntil(() => !getTargetEl(document, addonID), 100);
+ 
+   info("add-on is not shown");
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+-add_task(function* removeWebextension() {
++add_task(async function removeWebextension() {
+   const addonID = "test-devtools-webextension@mozilla.org";
+   const addonName = "test-devtools-webextension";
+ 
+-  const { tab, document } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
++  const { tab, document } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
+ 
+   // Install this add-on, and verify that it appears in the about:debugging UI
+-  yield installAddon({
++  await installAddon({
+     document,
+     path: "addons/test-devtools-webextension/manifest.json",
+     name: addonName,
+     isWebExtension: true,
+   });
+ 
+   ok(getTargetEl(document, addonID), "add-on is shown");
+ 
+   info("Click on the remove button and wait until the addon container is removed");
+   getRemoveButton(document, addonID).click();
+-  yield waitUntil(() => !getTargetEl(document, addonID), 100);
++  await waitUntil(() => !getTargetEl(document, addonID), 100);
+ 
+   info("add-on is not shown");
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+-add_task(function* onlyTempInstalledAddonsCanBeRemoved() {
+-  const { tab, document } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
++add_task(async function onlyTempInstalledAddonsCanBeRemoved() {
++  const { tab, document } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
+ 
+-  yield installAddonWithManager(getSupportsFile("addons/bug1273184.xpi").file);
+-  const addon = yield getAddonByID("bug1273184@tests");
++  await installAddonWithManager(getSupportsFile("addons/bug1273184.xpi").file);
++  const addon = await getAddonByID("bug1273184@tests");
+ 
+   info("Wait until addon appears in about:debugging#addons");
+-  yield waitUntilAddonContainer(PACKAGED_ADDON_NAME, document);
++  await waitUntilAddonContainer(PACKAGED_ADDON_NAME, document);
+ 
+   const removeButton = getRemoveButton(document, addon.id);
+   ok(!removeButton, "remove button is not shown");
+ 
+-  yield tearDownAddon(addon);
+-  yield closeAboutDebugging(tab);
++  await tearDownAddon(addon);
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_toggle_debug.js b/devtools/client/aboutdebugging/test/browser_addons_toggle_debug.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_toggle_debug.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_toggle_debug.js
+@@ -4,31 +4,31 @@
+ 
+ // Test that individual Debug buttons are disabled when "Addons debugging"
+ // is disabled.
+ // Test that the buttons are updated dynamically if the preference changes.
+ 
+ const ADDON_ID = "test-devtools@mozilla.org";
+ const ADDON_NAME = "test-devtools";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Turn off addon debugging.");
+-  yield new Promise(resolve => {
++  await new Promise(resolve => {
+     let options = {"set": [
+       ["devtools.chrome.enabled", false],
+       ["devtools.debugger.remote-enabled", false],
+     ]};
+     SpecialPowers.pushPrefEnv(options, resolve);
+   });
+ 
+-  let { tab, document } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
++  let { tab, document } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
+ 
+   info("Install a test addon.");
+-  yield installAddon({
++  await installAddon({
+     document,
+     path: "addons/unpacked/install.rdf",
+     name: ADDON_NAME,
+   });
+ 
+   let addonDebugCheckbox = document.querySelector("#enable-addon-debugging");
+   ok(!addonDebugCheckbox.checked, "Addons debugging should be disabled.");
+ 
+@@ -46,19 +46,19 @@ add_task(function* () {
+   info("Click again on 'Enable addons debugging' checkbox.");
+   addonDebugCheckbox.click();
+ 
+   info("Wait until all debug buttons are enabled.");
+   waitUntil(() => areDebugButtonsDisabled(document), 100);
+   info("All debug buttons are disabled again.");
+ 
+   info("Uninstall addon installed earlier.");
+-  yield uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
++  await uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+ function getDebugButtons(document) {
+   return [...document.querySelectorAll("#addons .debug-button")];
+ }
+ 
+ function areDebugButtonsEnabled(document) {
+   return getDebugButtons(document).every(b => !b.disabled);
+diff --git a/devtools/client/aboutdebugging/test/browser_page_not_found.js b/devtools/client/aboutdebugging/test/browser_page_not_found.js
+--- a/devtools/client/aboutdebugging/test/browser_page_not_found.js
++++ b/devtools/client/aboutdebugging/test/browser_page_not_found.js
+@@ -1,41 +1,41 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ // Test that navigating to a about:debugging#invalid-hash should show up an
+ // error page.
+ // Every url navigating including #invalid-hash should be kept in history and
+ // navigate back as expected.
+-add_task(function* () {
+-  let { tab, document } = yield openAboutDebugging("invalid-hash");
++add_task(async function() {
++  let { tab, document } = await openAboutDebugging("invalid-hash");
+   let element = document.querySelector(".header-name");
+   is(element.textContent, "Page not found", "Show error page");
+ 
+   info("Opening addons-panel panel");
+   document.querySelector("[aria-controls='addons-panel']").click();
+-  yield waitUntilElement("#addons-panel", document);
++  await waitUntilElement("#addons-panel", document);
+ 
+-  yield waitForInitialAddonList(document);
++  await waitForInitialAddonList(document);
+   element = document.querySelector(".header-name");
+   is(element.textContent, "Add-ons", "Show Addons");
+ 
+   info("Opening about:debugging#invalid-hash");
+   window.openUILinkIn("about:debugging#invalid-hash", "current");
+-  yield waitUntilElement(".error-page", document);
++  await waitUntilElement(".error-page", document);
+ 
+   element = document.querySelector(".header-name");
+   is(element.textContent, "Page not found", "Show error page");
+ 
+   gBrowser.goBack();
+-  yield waitUntilElement("#addons-panel", document);
+-  yield waitForInitialAddonList(document);
++  await waitUntilElement("#addons-panel", document);
++  await waitForInitialAddonList(document);
+   element = document.querySelector(".header-name");
+   is(element.textContent, "Add-ons", "Show Addons");
+ 
+   gBrowser.goBack();
+-  yield waitUntilElement(".error-page", document);
++  await waitUntilElement(".error-page", document);
+   element = document.querySelector(".header-name");
+   is(element.textContent, "Page not found", "Show error page");
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers.js b/devtools/client/aboutdebugging/test/browser_service_workers.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers.js
+@@ -3,41 +3,41 @@
+ 
+ "use strict";
+ 
+ // Service workers can't be loaded from chrome://,
+ // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
+ const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
+ const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
+ 
+-add_task(function* () {
+-  yield enableServiceWorkerDebugging();
++add_task(async function() {
++  await enableServiceWorkerDebugging();
+ 
+-  let { tab, document } = yield openAboutDebugging("workers");
++  let { tab, document } = await openAboutDebugging("workers");
+ 
+-  let swTab = yield addTab(TAB_URL);
++  let swTab = await addTab(TAB_URL);
+ 
+   let serviceWorkersElement = getServiceWorkerList(document);
+ 
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     // Check that the service worker appears in the UI
+     let names = [...document.querySelectorAll("#service-workers .target-name")];
+     names = names.map(element => element.textContent);
+     return names.includes(SERVICE_WORKER);
+   });
+   info("The service worker url appears in the list");
+ 
+   try {
+-    yield unregisterServiceWorker(swTab, serviceWorkersElement);
++    await unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+ 
+   // Check that the service worker disappeared from the UI
+   let names = [...document.querySelectorAll("#service-workers .target-name")];
+   names = names.map(element => element.textContent);
+   ok(!names.includes(SERVICE_WORKER),
+     "The service worker url is no longer in the list: " + names);
+ 
+-  yield removeTab(swTab);
+-  yield closeAboutDebugging(tab);
++  await removeTab(swTab);
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js b/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js
+@@ -3,46 +3,46 @@
+ 
+ "use strict";
+ 
+ // Service workers can't be loaded from chrome://,
+ // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
+ const EMPTY_SW_TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
+ const FETCH_SW_TAB_URL = URL_ROOT + "service-workers/fetch-sw.html";
+ 
+-function* testBody(url, expecting) {
+-  yield enableServiceWorkerDebugging();
+-  let { tab, document } = yield openAboutDebugging("workers");
++async function testBody(url, expecting) {
++  await enableServiceWorkerDebugging();
++  let { tab, document } = await openAboutDebugging("workers");
+ 
+-  let swTab = yield addTab(url);
++  let swTab = await addTab(url);
+ 
+   let serviceWorkersElement = getServiceWorkerList(document);
+ 
+   info("Wait for fetch flag.");
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     let fetchFlags =
+       [...document.querySelectorAll("#service-workers .service-worker-fetch-flag")];
+     fetchFlags = fetchFlags.map(element => element.textContent);
+     return fetchFlags.includes(expecting);
+   }, 100);
+ 
+   info("Found correct fetch flag.");
+ 
+   try {
+-    yield unregisterServiceWorker(swTab, serviceWorkersElement);
++    await unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+ 
+   // Check that the service worker disappeared from the UI
+   let names = [...document.querySelectorAll("#service-workers .target-name")];
+   names = names.map(element => element.textContent);
+   ok(names.length == 0, "All service workers were removed from the list.");
+ 
+-  yield removeTab(swTab);
+-  yield closeAboutDebugging(tab);
++  await removeTab(swTab);
++  await closeAboutDebugging(tab);
+ }
+ 
+-add_task(function* () {
+-  yield testBody(FETCH_SW_TAB_URL, "Listening for fetch events.");
+-  yield testBody(EMPTY_SW_TAB_URL, "Not listening for fetch events.");
++add_task(async function() {
++  await testBody(FETCH_SW_TAB_URL, "Listening for fetch events.");
++  await testBody(EMPTY_SW_TAB_URL, "Not listening for fetch events.");
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js b/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js
+@@ -5,69 +5,69 @@
+ 
+ // Service worker debugging is unavailable when multi-e10s is enabled.
+ // Check that the appropriate warning panel is displayed when there are more than 1
+ // content process available.
+ 
+ const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
+ const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
+ 
+-add_task(function* () {
+-  yield enableServiceWorkerDebugging();
++add_task(async function() {
++  await enableServiceWorkerDebugging();
+   info("Force two content processes");
+-  yield pushPref("dom.ipc.processCount", 2);
++  await pushPref("dom.ipc.processCount", 2);
+ 
+-  let { tab, document } = yield openAboutDebugging("workers");
++  let { tab, document } = await openAboutDebugging("workers");
+ 
+   let warningSection = document.querySelector(".service-worker-multi-process");
+   let img = warningSection.querySelector(".warning");
+   ok(img, "warning message is rendered");
+ 
+   let serviceWorkersElement = getServiceWorkerList(document);
+ 
+-  let swTab = yield addTab(TAB_URL, { background: true });
++  let swTab = await addTab(TAB_URL, { background: true });
+ 
+   info("Wait for service worker to appear in the list");
+   // Check that the service worker appears in the UI
+   let serviceWorkerContainer =
+-    yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
++    await waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   info("Check that service worker buttons are disabled.");
+   let debugButton = getDebugButton(serviceWorkerContainer);
+   ok(debugButton.disabled, "Start/Debug button is disabled");
+ 
+   info("Update the preference to 1");
+   let onWarningCleared = waitUntil(() => {
+     let hasWarning = document.querySelector(".service-worker-multi-process");
+     return !hasWarning && !debugButton.disabled;
+   }, 100);
+-  yield pushPref("dom.ipc.processCount", 1);
+-  yield onWarningCleared;
++  await pushPref("dom.ipc.processCount", 1);
++  await onWarningCleared;
+   ok(!debugButton.disabled, "Debug button is enabled.");
+ 
+   info("Update the preference back to 2");
+   let onWarningRestored = waitUntil(() => {
+     let hasWarning = document.querySelector(".service-worker-multi-process");
+     return hasWarning && getDebugButton(serviceWorkerContainer).disabled;
+   }, 100);
+-  yield pushPref("dom.ipc.processCount", 2);
+-  yield onWarningRestored;
++  await pushPref("dom.ipc.processCount", 2);
++  await onWarningRestored;
+ 
+   // Update the reference to the debugButton, as the previous DOM element might have been
+   // deleted.
+   debugButton = getDebugButton(serviceWorkerContainer);
+   ok(debugButton.disabled, "Debug button is disabled again.");
+ 
+   info("Unregister service worker");
+   try {
+-    yield unregisterServiceWorker(swTab, serviceWorkersElement);
++    await unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+ 
+-  yield removeTab(swTab);
+-  yield closeAboutDebugging(tab);
++  await removeTab(swTab);
++  await closeAboutDebugging(tab);
+ });
+ 
+ function getDebugButton(serviceWorkerContainer) {
+   return serviceWorkerContainer.querySelector(".debug-button");
+ }
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_not_compatible.js b/devtools/client/aboutdebugging/test/browser_service_workers_not_compatible.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_not_compatible.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_not_compatible.js
+@@ -6,55 +6,55 @@
+ // Test that Service Worker section should show warning message in
+ // about:debugging if any of following conditions is met:
+ // 1. service worker is disabled
+ // 2. the about:debugging pannel is openned in private browsing mode
+ // 3. the about:debugging pannel is openned in private content window
+ 
+ var imgClass = ".service-worker-disabled .warning";
+ 
+-add_task(function* () {
+-  yield new Promise(done => {
++add_task(async function() {
++  await new Promise(done => {
+     info("disable service workers");
+     let options = {"set": [
+       ["dom.serviceWorkers.enabled", false],
+     ]};
+     SpecialPowers.pushPrefEnv(options, done);
+   });
+ 
+-  let { tab, document } = yield openAboutDebugging("workers");
++  let { tab, document } = await openAboutDebugging("workers");
+   // Check that the warning img appears in the UI
+   let img = document.querySelector(imgClass);
+   ok(img, "warning message is rendered");
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+-add_task(function* () {
+-  yield new Promise(done => {
++add_task(async function() {
++  await new Promise(done => {
+     info("set private browsing mode as default");
+     let options = {"set": [
+       ["browser.privatebrowsing.autostart", true],
+     ]};
+     SpecialPowers.pushPrefEnv(options, done);
+   });
+ 
+-  let { tab, document } = yield openAboutDebugging("workers");
++  let { tab, document } = await openAboutDebugging("workers");
+   // Check that the warning img appears in the UI
+   let img = document.querySelector(imgClass);
+   ok(img, "warning message is rendered");
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Opening a new private window");
+   let win = OpenBrowserWindow({private: true});
+-  yield waitForDelayedStartupFinished(win);
++  await waitForDelayedStartupFinished(win);
+ 
+-  let { tab, document } = yield openAboutDebugging("workers", win);
++  let { tab, document } = await openAboutDebugging("workers", win);
+   // Check that the warning img appears in the UI
+   let img = document.querySelector(imgClass);
+   ok(img, "warning message is rendered");
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+   win.close();
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_push.js b/devtools/client/aboutdebugging/test/browser_service_workers_push.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_push.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_push.js
+@@ -9,79 +9,79 @@
+ // intended in about:debugging.
+ // It should trigger a "push" notification in the worker.
+ 
+ // Service workers can't be loaded from chrome://, but http:// is ok with
+ // dom.serviceWorkers.testing.enabled turned on.
+ const SERVICE_WORKER = URL_ROOT + "service-workers/push-sw.js";
+ const TAB_URL = URL_ROOT + "service-workers/push-sw.html";
+ 
+-add_task(function* () {
+-  yield enableServiceWorkerDebugging();
+-  let { tab, document } = yield openAboutDebugging("workers");
++add_task(async function() {
++  await enableServiceWorkerDebugging();
++  let { tab, document } = await openAboutDebugging("workers");
+ 
+   // Listen for mutations in the service-workers list.
+   let serviceWorkersElement = getServiceWorkerList(document);
+ 
+   // Open a tab that registers a push service worker.
+-  let swTab = yield addTab(TAB_URL);
++  let swTab = await addTab(TAB_URL);
+ 
+   info("Make the test page notify us when the service worker sends a message.");
+ 
+-  yield ContentTask.spawn(swTab.linkedBrowser, {}, function() {
++  await ContentTask.spawn(swTab.linkedBrowser, {}, function() {
+     let win = content.wrappedJSObject;
+     win.navigator.serviceWorker.addEventListener("message", function(event) {
+       sendAsyncMessage(event.data);
+     });
+   });
+ 
+   // Expect the service worker to claim the test window when activating.
+   let onClaimed = onTabMessage(swTab, "sw-claimed");
+ 
+   info("Wait until the service worker appears in the UI");
+-  yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
++  await waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   info("Ensure that the registration resolved before trying to interact with " +
+     "the service worker.");
+-  yield waitForServiceWorkerRegistered(swTab);
++  await waitForServiceWorkerRegistered(swTab);
+   ok(true, "Service worker registration resolved");
+ 
+-  yield waitForServiceWorkerActivation(SERVICE_WORKER, document);
++  await waitForServiceWorkerActivation(SERVICE_WORKER, document);
+ 
+   // Retrieve the Push button for the worker.
+   let names = [...document.querySelectorAll("#service-workers .target-name")];
+   let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
+   ok(name, "Found the service worker in the list");
+ 
+   let targetElement = name.parentNode.parentNode;
+ 
+   let pushBtn = targetElement.querySelector(".push-button");
+   ok(pushBtn, "Found its push button");
+ 
+   info("Wait for the service worker to claim the test window before proceeding.");
+-  yield onClaimed;
++  await onClaimed;
+ 
+   info("Click on the Push button and wait for the service worker to receive " +
+     "a push notification");
+   let onPushNotification = onTabMessage(swTab, "sw-pushed");
+ 
+   pushBtn.click();
+-  yield onPushNotification;
++  await onPushNotification;
+   ok(true, "Service worker received a push notification");
+ 
+   // Finally, unregister the service worker itself.
+   try {
+-    yield unregisterServiceWorker(swTab, serviceWorkersElement);
++    await unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+ 
+-  yield removeTab(swTab);
+-  yield closeAboutDebugging(tab);
++  await removeTab(swTab);
++  await closeAboutDebugging(tab);
+ });
+ 
+ /**
+  * Helper to listen once on a message sent using postMessage from the provided tab.
+  */
+ function onTabMessage(tab, message) {
+   let mm = tab.linkedBrowser.messageManager;
+   return new Promise(done => {
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js b/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js
+@@ -11,21 +11,21 @@
+ const SERVICE_WORKER = URL_ROOT + "service-workers/push-sw.js";
+ const TAB_URL = URL_ROOT + "service-workers/push-sw.html";
+ 
+ const FAKE_ENDPOINT = "https://fake/endpoint";
+ 
+ const PushService = Cc["@mozilla.org/push/Service;1"]
+   .getService(Ci.nsIPushService).wrappedJSObject;
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Turn on workers via mochitest http.");
+-  yield enableServiceWorkerDebugging();
++  await enableServiceWorkerDebugging();
+   // Enable the push service.
+-  yield pushPref("dom.push.connection.enabled", true);
++  await pushPref("dom.push.connection.enabled", true);
+ 
+   info("Mock the push service");
+   PushService.service = {
+     _registrations: new Map(),
+     _notify(scope) {
+       Services.obs.notifyObservers(
+         null,
+         PushService.subscriptionModifiedTopic,
+@@ -47,60 +47,60 @@ add_task(function* () {
+       let deleted = this._registrations.delete(pageRecord.scope);
+       if (deleted) {
+         this._notify(pageRecord.scope);
+       }
+       return Promise.resolve(deleted);
+     },
+   };
+ 
+-  let { tab, document } = yield openAboutDebugging("workers");
++  let { tab, document } = await openAboutDebugging("workers");
+ 
+   // Listen for mutations in the service-workers list.
+   let serviceWorkersElement = document.getElementById("service-workers");
+ 
+   // Open a tab that registers a push service worker.
+-  let swTab = yield addTab(TAB_URL);
++  let swTab = await addTab(TAB_URL);
+ 
+   info("Wait until the service worker appears in about:debugging");
+-  yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
++  await waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+-  yield waitForServiceWorkerActivation(SERVICE_WORKER, document);
++  await waitForServiceWorkerActivation(SERVICE_WORKER, document);
+ 
+   // Wait for the service worker details to update.
+   let names = [...document.querySelectorAll("#service-workers .target-name")];
+   let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
+   ok(name, "Found the service worker in the list");
+ 
+   let targetContainer = name.closest(".target-container");
+ 
+   // Retrieve the push subscription endpoint URL, and verify it looks good.
+   info("Wait for the push URL");
+-  let pushURL = yield waitUntilElement(".service-worker-push-url", targetContainer);
++  let pushURL = await waitUntilElement(".service-worker-push-url", targetContainer);
+ 
+   info("Found the push service URL in the service worker details");
+   is(pushURL.textContent, FAKE_ENDPOINT, "The push service URL looks correct");
+ 
+   // Unsubscribe from the push service.
+   ContentTask.spawn(swTab.linkedBrowser, {}, function() {
+     let win = content.wrappedJSObject;
+     return win.sub.unsubscribe();
+   });
+ 
+   // Wait for the service worker details to update again
+   info("Wait until the push URL is removed from the UI");
+-  yield waitUntil(() => !targetContainer.querySelector(".service-worker-push-url"), 100);
++  await waitUntil(() => !targetContainer.querySelector(".service-worker-push-url"), 100);
+   info("The push service URL should be removed");
+ 
+   // Finally, unregister the service worker itself.
+   try {
+-    yield unregisterServiceWorker(swTab, serviceWorkersElement);
++    await unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+ 
+   info("Unmock the push service");
+   PushService.service = null;
+ 
+-  yield removeTab(swTab);
+-  yield closeAboutDebugging(tab);
++  await removeTab(swTab);
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_start.js b/devtools/client/aboutdebugging/test/browser_service_workers_start.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_start.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_start.js
+@@ -9,71 +9,71 @@
+ 
+ // Service workers can't be loaded from chrome://, but http:// is ok with
+ // dom.serviceWorkers.testing.enabled turned on.
+ const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
+ const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
+ 
+ const SW_TIMEOUT = 1000;
+ 
+-add_task(function* () {
+-  yield enableServiceWorkerDebugging();
+-  yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+-  yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
++add_task(async function() {
++  await enableServiceWorkerDebugging();
++  await pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
++  await pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
+ 
+-  let { tab, document } = yield openAboutDebugging("workers");
++  let { tab, document } = await openAboutDebugging("workers");
+ 
+   // Listen for mutations in the service-workers list.
+   let serviceWorkersElement = getServiceWorkerList(document);
+ 
+   // Open a tab that registers an empty service worker.
+-  let swTab = yield addTab(TAB_URL);
++  let swTab = await addTab(TAB_URL);
+ 
+   // Wait for the service-workers list to update.
+   info("Wait until the service worker appears in about:debugging");
+-  yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
++  await waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   info("Ensure that the registration resolved before trying to interact with " +
+     "the service worker.");
+-  yield waitForServiceWorkerRegistered(swTab);
++  await waitForServiceWorkerRegistered(swTab);
+   ok(true, "Service worker registration resolved");
+ 
+-  yield waitForServiceWorkerActivation(SERVICE_WORKER, document);
++  await waitForServiceWorkerActivation(SERVICE_WORKER, document);
+ 
+   // Retrieve the Target element corresponding to the service worker.
+   let names = [...document.querySelectorAll("#service-workers .target-name")];
+   let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
+   ok(name, "Found the service worker in the list");
+   let targetElement = name.parentNode.parentNode;
+ 
+   // The service worker may already be killed with the low 1s timeout.
+   // At this stage, either the SW is started and the Debug button is visible or was
+   // already stopped and the start button is visible. Wait until the service worker is
+   // stopped.
+   info("Wait until the start button is visible");
+-  yield waitUntilElement(".start-button", targetElement);
++  await waitUntilElement(".start-button", targetElement);
+ 
+   // We should now have a Start button but no Debug button.
+   let startBtn = targetElement.querySelector(".start-button");
+   ok(startBtn, "Found its start button");
+   ok(!targetElement.querySelector(".debug-button"), "No debug button");
+ 
+   // Click on the Start button and wait for the service worker to be back.
+   startBtn.click();
+ 
+   info("Wait until the service worker starts and the debug button appears");
+-  yield waitUntilElement(".debug-button", targetElement);
++  await waitUntilElement(".debug-button", targetElement);
+   info("Found the debug button");
+ 
+   // Check that we have a Debug button but not a Start button again.
+   ok(!targetElement.querySelector(".start-button"), "No start button");
+ 
+   // Finally, unregister the service worker itself.
+   try {
+-    yield unregisterServiceWorker(swTab, serviceWorkersElement);
++    await unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+ 
+-  yield removeTab(swTab);
+-  yield closeAboutDebugging(tab);
++  await removeTab(swTab);
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_status.js b/devtools/client/aboutdebugging/test/browser_service_workers_status.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_status.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_status.js
+@@ -6,47 +6,47 @@
+ // Service workers can't be loaded from chrome://,
+ // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
+ const SERVICE_WORKER = URL_ROOT + "service-workers/delay-sw.js";
+ const TAB_URL = URL_ROOT + "service-workers/delay-sw.html";
+ const SW_TIMEOUT = 2000;
+ 
+ requestLongerTimeout(2);
+ 
+-add_task(function* () {
+-  yield enableServiceWorkerDebugging();
+-  yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+-  yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
++add_task(async function() {
++  await enableServiceWorkerDebugging();
++  await pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
++  await pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
+ 
+-  let { tab, document } = yield openAboutDebugging("workers");
++  let { tab, document } = await openAboutDebugging("workers");
+ 
+   // Listen for mutations in the service-workers list.
+   let serviceWorkersElement = getServiceWorkerList(document);
+ 
+-  let swTab = yield addTab(TAB_URL);
++  let swTab = await addTab(TAB_URL);
+ 
+   info("Wait until the service worker appears in about:debugging");
+-  let container = yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
++  let container = await waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   // We should ideally check that the service worker registration goes through the
+   // "registering" and "running" steps, but it is difficult to workaround race conditions
+   // for a test running on a wide variety of platforms. Due to intermittent failures, we
+   // simply check that the registration transitions to "stopped".
+   let status = container.querySelector(".target-status");
+-  yield waitUntil(() => status.textContent == "Stopped", 100);
++  await waitUntil(() => status.textContent == "Stopped", 100);
+   is(status.textContent, "Stopped", "Service worker is currently stopped");
+ 
+   try {
+-    yield unregisterServiceWorker(swTab, serviceWorkersElement);
++    await unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker unregistered");
+   } catch (e) {
+     ok(false, "Service worker not unregistered; " + e);
+   }
+ 
+   // Check that the service worker disappeared from the UI
+   let names = [...document.querySelectorAll("#service-workers .target-name")];
+   names = names.map(element => element.textContent);
+   ok(!names.includes(SERVICE_WORKER),
+     "The service worker url is no longer in the list: " + names);
+ 
+-  yield removeTab(swTab);
+-  yield closeAboutDebugging(tab);
++  await removeTab(swTab);
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js b/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
+@@ -6,32 +6,32 @@
+ 
+ // Service workers can't be loaded from chrome://,
+ // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
+ const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
+ const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
+ 
+ const SW_TIMEOUT = 1000;
+ 
+-add_task(function* () {
+-  yield enableServiceWorkerDebugging();
+-  yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+-  yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
++add_task(async function() {
++  await enableServiceWorkerDebugging();
++  await pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
++  await pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
+ 
+-  let { tab, document } = yield openAboutDebugging("workers");
++  let { tab, document } = await openAboutDebugging("workers");
+ 
+   let serviceWorkersElement = getServiceWorkerList(document);
+ 
+-  let swTab = yield addTab(TAB_URL);
++  let swTab = await addTab(TAB_URL);
+ 
+   info("Wait until the service worker appears in about:debugging");
+-  yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
++  await waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   // Ensure that the registration resolved before trying to connect to the sw
+-  yield waitForServiceWorkerRegistered(swTab);
++  await waitForServiceWorkerRegistered(swTab);
+   ok(true, "Service worker registration resolved");
+ 
+   // Retrieve the DEBUG button for the worker
+   let names = [...document.querySelectorAll("#service-workers .target-name")];
+   let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
+   ok(name, "Found the service worker in the list");
+   let targetElement = name.parentNode.parentNode;
+   let debugBtn = targetElement.querySelector(".debug-button");
+@@ -40,45 +40,45 @@ add_task(function* () {
+   // Click on it and wait for the toolbox to be ready
+   let onToolboxReady = new Promise(done => {
+     gDevTools.once("toolbox-ready", function(e, toolbox) {
+       done(toolbox);
+     });
+   });
+   debugBtn.click();
+ 
+-  let toolbox = yield onToolboxReady;
++  let toolbox = await onToolboxReady;
+ 
+   // Wait for more than the regular timeout,
+   // so that if the worker freezing doesn't work,
+   // it will be destroyed and removed from the list
+-  yield new Promise(done => {
++  await new Promise(done => {
+     setTimeout(done, SW_TIMEOUT * 2);
+   });
+ 
+   assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
+   ok(targetElement.querySelector(".debug-button"),
+     "The debug button is still there");
+ 
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   toolbox = null;
+ 
+   // Now ensure that the worker is correctly destroyed
+   // after we destroy the toolbox.
+   // The DEBUG button should disappear once the worker is destroyed.
+   info("Wait until the debug button disappears");
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     return !targetElement.querySelector(".debug-button");
+   });
+ 
+   // Finally, unregister the service worker itself.
+   try {
+-    yield unregisterServiceWorker(swTab, serviceWorkersElement);
++    await unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+ 
+   assertHasTarget(false, document, "service-workers", SERVICE_WORKER);
+ 
+-  yield removeTab(swTab);
+-  yield closeAboutDebugging(tab);
++  await removeTab(swTab);
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js b/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js
+@@ -9,32 +9,32 @@
+ // the displayed list of service workers.
+ 
+ // Service workers can't be loaded from chrome://, but http:// is ok with
+ // dom.serviceWorkers.testing.enabled turned on.
+ const SCOPE = URL_ROOT + "service-workers/";
+ const SERVICE_WORKER = SCOPE + "empty-sw.js";
+ const TAB_URL = SCOPE + "empty-sw.html";
+ 
+-add_task(function* () {
+-  yield enableServiceWorkerDebugging();
++add_task(async function() {
++  await enableServiceWorkerDebugging();
+ 
+-  let { tab, document } = yield openAboutDebugging("workers");
++  let { tab, document } = await openAboutDebugging("workers");
+ 
+   // Open a tab that registers an empty service worker.
+-  let swTab = yield addTab(TAB_URL);
++  let swTab = await addTab(TAB_URL);
+ 
+   info("Wait until the service worker appears in about:debugging");
+-  yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
++  await waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+-  yield waitForServiceWorkerActivation(SERVICE_WORKER, document);
++  await waitForServiceWorkerActivation(SERVICE_WORKER, document);
+ 
+   info("Ensure that the registration resolved before trying to interact with " +
+     "the service worker.");
+-  yield waitForServiceWorkerRegistered(swTab);
++  await waitForServiceWorkerRegistered(swTab);
+   ok(true, "Service worker registration resolved");
+ 
+   let targets = document.querySelectorAll("#service-workers .target");
+   is(targets.length, 1, "One service worker is now displayed.");
+ 
+   let target = targets[0];
+   let name = target.querySelector(".target-name");
+   is(name.textContent, SERVICE_WORKER, "Found the service worker in the list");
+@@ -46,15 +46,15 @@ add_task(function* () {
+ 
+   info("Unregister the service worker via the unregister link.");
+   let unregisterLink = target.querySelector(".unregister-link");
+   ok(unregisterLink, "Found the unregister link");
+ 
+   unregisterLink.click();
+ 
+   info("Wait until the service worker disappears");
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     return !document.querySelector("#service-workers .target");
+   });
+ 
+-  yield removeTab(swTab);
+-  yield closeAboutDebugging(tab);
++  await removeTab(swTab);
++  await closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_tabs.js b/devtools/client/aboutdebugging/test/browser_tabs.js
+--- a/devtools/client/aboutdebugging/test/browser_tabs.js
++++ b/devtools/client/aboutdebugging/test/browser_tabs.js
+@@ -1,77 +1,77 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TAB_URL = "data:text/html,<title>foo</title>";
+ 
+-add_task(function* () {
+-  let { tab, document } = yield openAboutDebugging("tabs");
++add_task(async function() {
++  let { tab, document } = await openAboutDebugging("tabs");
+ 
+   // Wait for initial tabs list which may be empty
+   let tabsElement = getTabList(document);
+-  yield waitUntilElement(".target-name", tabsElement);
++  await waitUntilElement(".target-name", tabsElement);
+ 
+   // Refresh tabsElement to get the .target-list element
+   tabsElement = getTabList(document);
+ 
+   let names = [...tabsElement.querySelectorAll(".target-name")];
+   let initialTabCount = names.length;
+ 
+   info("Open a new background tab");
+-  let newTab = yield addTab(TAB_URL, { background: true });
++  let newTab = await addTab(TAB_URL, { background: true });
+ 
+   info("Wait for the tab to appear in the list with the correct name");
+-  let container = yield waitUntilTabContainer("foo", document);
++  let container = await waitUntilTabContainer("foo", document);
+ 
+   info("Wait until the title to update");
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     return container.querySelector(".target-name").title === TAB_URL;
+   }, 100);
+ 
+   let icon = container.querySelector(".target-icon");
+   ok(icon && icon.src, "Tab icon found and src attribute is not empty");
+ 
+   info("Check if the tab icon is a valid image");
+-  yield new Promise(r => {
++  await new Promise(r => {
+     let image = new Image();
+     image.onload = () => {
+       ok(true, "Favicon is not a broken image");
+       r();
+     };
+     image.onerror = () => {
+       ok(false, "Favicon is a broken image");
+       r();
+     };
+     image.src = icon.src;
+   });
+ 
+   // Finally, close the tab
+-  yield removeTab(newTab);
++  await removeTab(newTab);
+ 
+   info("Wait until the tab container is removed");
+-  yield waitUntil(() => !getTabContainer("foo", document), 100);
++  await waitUntil(() => !getTabContainer("foo", document), 100);
+ 
+   // Check that the tab disappeared from the UI
+   names = [...tabsElement.querySelectorAll("#tabs .target-name")];
+   is(names.length, initialTabCount, "The tab disappeared from the UI");
+ 
+-  yield closeAboutDebugging(tab);
++  await closeAboutDebugging(tab);
+ });
+ 
+ function getTabContainer(name, document) {
+   let nameElements = [...document.querySelectorAll("#tabs .target-name")];
+   let nameElement = nameElements.filter(element => element.textContent === name)[0];
+   if (nameElement) {
+     return nameElement.closest(".target-container");
+   }
+ 
+   return null;
+ }
+ 
+-function* waitUntilTabContainer(name, document) {
+-  yield waitUntil(() => {
++async function waitUntilTabContainer(name, document) {
++  await waitUntil(() => {
+     return getTabContainer(name, document);
+   });
+   return getTabContainer(name, document);
+ }
+diff --git a/devtools/client/aboutdebugging/test/head.js b/devtools/client/aboutdebugging/test/head.js
+--- a/devtools/client/aboutdebugging/test/head.js
++++ b/devtools/client/aboutdebugging/test/head.js
+@@ -15,30 +15,30 @@ Services.scriptloader.loadSubScript(
+ const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
+ const { Management } = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
+ 
+ flags.testing = true;
+ registerCleanupFunction(() => {
+   flags.testing = false;
+ });
+ 
+-function* openAboutDebugging(page, win) {
++async function openAboutDebugging(page, win) {
+   info("opening about:debugging");
+   let url = "about:debugging";
+   if (page) {
+     url += "#" + page;
+   }
+ 
+-  let tab = yield addTab(url, { window: win });
++  let tab = await addTab(url, { window: win });
+   let browser = tab.linkedBrowser;
+   let document = browser.contentDocument;
+   let window = browser.contentWindow;
+ 
+   info("Wait until the main about debugging container is available");
+-  yield waitUntilElement(".app", document);
++  await waitUntilElement(".app", document);
+ 
+   return { tab, document, window };
+ }
+ 
+ function closeAboutDebugging(tab) {
+   info("Closing about:debugging");
+   return removeTab(tab);
+ }
+@@ -130,52 +130,52 @@ function getServiceWorkerContainer(name,
+  * Returns a promise that resolves the service worker container element.
+  *
+  * @param  {String} name
+  *         expected service worker name
+  * @param  {DOMDocument} document
+  *         #service-workers section container document
+  * @return {Promise} promise that resolves the service worker container element.
+  */
+-function* waitUntilServiceWorkerContainer(name, document) {
+-  yield waitUntil(() => {
++async function waitUntilServiceWorkerContainer(name, document) {
++  await waitUntil(() => {
+     return getServiceWorkerContainer(name, document);
+   }, 100);
+   return getServiceWorkerContainer(name, document);
+ }
+ 
+ /**
+  * Wait until a selector matches an element in a given parent node.
+  * Returns a promise that resolves the matched element.
+  *
+  * @param {String} selector
+  *        CSS selector to match.
+  * @param {DOMNode} parent
+  *        Parent that should contain the element.
+  * @return {Promise} promise that resolves the matched DOMNode.
+  */
+-function* waitUntilElement(selector, parent) {
+-  yield waitUntil(() => {
++async function waitUntilElement(selector, parent) {
++  await waitUntil(() => {
+     return parent.querySelector(selector);
+   }, 100);
+   return parent.querySelector(selector);
+ }
+ 
+ /**
+  * Depending on whether there are tabs opened, return either a
+  * target list element or its container.
+  * @param  {DOMDocument}  document   #tabs section container document
+  * @return {DOMNode}                 target list or container element
+  */
+ function getTabList(document) {
+   return document.querySelector("#tabs .target-list") ||
+     document.querySelector("#tabs.targets");
+ }
+ 
+-function* installAddon({document, path, name, isWebExtension}) {
++async function installAddon({document, path, name, isWebExtension}) {
+   // Mock the file picker to select a test addon
+   let MockFilePicker = SpecialPowers.MockFilePicker;
+   MockFilePicker.init(window);
+   let file = getSupportsFile(path);
+   MockFilePicker.setFiles([file.file]);
+ 
+   let onAddonInstalled;
+ 
+@@ -198,26 +198,26 @@ function* installAddon({document, path, 
+ 
+         done();
+       }, "test-devtools");
+     });
+   }
+   // Trigger the file picker by clicking on the button
+   document.getElementById("load-addon-from-file").click();
+ 
+-  yield onAddonInstalled;
++  await onAddonInstalled;
+   ok(true, "Addon installed and running its bootstrap.js file");
+ 
+   info("Wait for the addon to appear in the UI");
+-  yield waitUntilAddonContainer(name, document);
++  await waitUntilAddonContainer(name, document);
+ }
+ 
+-function* uninstallAddon({document, id, name}) {
++async function uninstallAddon({document, id, name}) {
+   // Now uninstall this addon
+-  yield new Promise(done => {
++  await new Promise(done => {
+     AddonManager.getAddonByID(id, addon => {
+       let listener = {
+         onUninstalled: function(uninstalledAddon) {
+           if (uninstalledAddon != addon) {
+             return;
+           }
+           AddonManager.removeAddonListener(listener);
+ 
+@@ -225,17 +225,17 @@ function* uninstallAddon({document, id, 
+         }
+       };
+       AddonManager.addAddonListener(listener);
+       addon.uninstall();
+     });
+   });
+ 
+   info("Wait until the addon is removed from about:debugging");
+-  yield waitUntil(() => !getAddonContainer(name, document), 100);
++  await waitUntil(() => !getAddonContainer(name, document), 100);
+ }
+ 
+ function getAddonCount(document) {
+   const addonListContainer = getAddonList(document);
+   let addonElements = addonListContainer.querySelectorAll(".target");
+   return addonElements.length;
+ }
+ 
+@@ -255,18 +255,18 @@ function getAddonContainer(name, documen
+   let nameElement = nameElements.filter(element => element.textContent === name)[0];
+   if (nameElement) {
+     return nameElement.closest(".addon-target-container");
+   }
+ 
+   return null;
+ }
+ 
+-function* waitUntilAddonContainer(name, document) {
+-  yield waitUntil(() => {
++async function waitUntilAddonContainer(name, document) {
++  await waitUntil(() => {
+     return getAddonContainer(name, document);
+   });
+   return getAddonContainer(name, document);
+ }
+ 
+ /**
+  * Checks if an about:debugging TargetList element contains a Target element
+  * corresponding to the specified name.
+@@ -284,49 +284,49 @@ function assertHasTarget(expected, docum
+ 
+ /**
+  * Returns a promise that will resolve after the service worker in the page
+  * has successfully registered itself.
+  * @param {Tab} tab
+  * @return {Promise} Resolves when the service worker is registered.
+  */
+ function waitForServiceWorkerRegistered(tab) {
+-  return ContentTask.spawn(tab.linkedBrowser, {}, function* () {
++  return ContentTask.spawn(tab.linkedBrowser, {}, async function() {
+     // Retrieve the `sw` promise created in the html page.
+     let { sw } = content.wrappedJSObject;
+-    yield sw;
++    await sw;
+   });
+ }
+ 
+ /**
+  * Asks the service worker within the test page to unregister, and returns a
+  * promise that will resolve when it has successfully unregistered itself and the
+  * about:debugging UI has fully processed this update.
+  *
+  * @param {Tab} tab
+  * @param {Node} serviceWorkersElement
+  * @return {Promise} Resolves when the service worker is unregistered.
+  */
+-function* unregisterServiceWorker(tab, serviceWorkersElement) {
++async function unregisterServiceWorker(tab, serviceWorkersElement) {
+   // Get the initial count of service worker registrations.
+   let registrations = serviceWorkersElement.querySelectorAll(".target-container");
+   let registrationCount = registrations.length;
+ 
+   // Wait until the registration count is decreased by one.
+   let isRemoved = waitUntil(() => {
+     registrations = serviceWorkersElement.querySelectorAll(".target-container");
+     return registrations.length === registrationCount - 1;
+   }, 100);
+ 
+   // Unregister the service worker from the content page
+-  yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
++  await ContentTask.spawn(tab.linkedBrowser, {}, async function() {
+     // Retrieve the `sw` promise created in the html page
+     let { sw } = content.wrappedJSObject;
+-    let registration = yield sw;
+-    yield registration.unregister();
++    let registration = await sw;
++    await registration.unregister();
+   });
+ 
+   return isRemoved;
+ }
+ 
+ /**
+  * Waits for the creation of a new window, usually used with create private
+  * browsing window.
+@@ -342,34 +342,34 @@ function waitForDelayedStartupFinished(w
+       }
+     }, "browser-delayed-startup-finished");
+   });
+ }
+ 
+ /**
+  * open the about:debugging page and install an addon
+  */
+-function* setupTestAboutDebuggingWebExtension(name, path) {
+-  yield new Promise(resolve => {
++async function setupTestAboutDebuggingWebExtension(name, path) {
++  await new Promise(resolve => {
+     let options = {"set": [
+       // Force enabling of addons debugging
+       ["devtools.chrome.enabled", true],
+       ["devtools.debugger.remote-enabled", true],
+       // Disable security prompt
+       ["devtools.debugger.prompt-connection", false],
+       // Enable Browser toolbox test script execution via env variable
+       ["devtools.browser-toolbox.allow-unsafe-script", true],
+     ]};
+     SpecialPowers.pushPrefEnv(options, resolve);
+   });
+ 
+-  let { tab, document } = yield openAboutDebugging("addons");
+-  yield waitForInitialAddonList(document);
++  let { tab, document } = await openAboutDebugging("addons");
++  await waitForInitialAddonList(document);
+ 
+-  yield installAddon({
++  await installAddon({
+     document,
+     path,
+     name,
+     isWebExtension: true,
+   });
+ 
+   // Retrieve the DEBUG button for the addon
+   let names = getInstalledAddonNames(document);
+@@ -381,43 +381,43 @@ function* setupTestAboutDebuggingWebExte
+ 
+   return { tab, document, debugBtn };
+ }
+ 
+ /**
+  * Wait for aboutdebugging to be notified about the activation of the service worker
+  * corresponding to the provided service worker url.
+  */
+-function* waitForServiceWorkerActivation(swUrl, document) {
++async function waitForServiceWorkerActivation(swUrl, document) {
+   let serviceWorkersElement = getServiceWorkerList(document);
+   let names = serviceWorkersElement.querySelectorAll(".target-name");
+   let name = [...names].filter(element => element.textContent === swUrl)[0];
+ 
+   let targetElement = name.parentNode.parentNode;
+   let targetStatus = targetElement.querySelector(".target-status");
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     return targetStatus.textContent !== "Registering";
+   }, 100);
+ }
+ 
+ /**
+  * Set all preferences needed to enable service worker debugging and testing.
+  */
+-function* enableServiceWorkerDebugging() {
++async function enableServiceWorkerDebugging() {
+   let options = { "set": [
+     // Enable service workers.
+     ["dom.serviceWorkers.enabled", true],
+     // Accept workers from mochitest's http.
+     ["dom.serviceWorkers.testing.enabled", true],
+     // Force single content process.
+     ["dom.ipc.processCount", 1],
+   ]};
+ 
+   // Wait for dom.ipc.processCount to be updated before releasing processes.
+-  yield new Promise(done => {
++  await new Promise(done => {
+     SpecialPowers.pushPrefEnv(options, done);
+   });
+ 
+   Services.ppmm.releaseCachedProcesses();
+ }
+ 
+ /**
+  * Returns a promise that resolves when the given add-on event is fired. The
+@@ -461,15 +461,15 @@ function getAddonByID(addonId) {
+   return new Promise(resolve => {
+     AddonManager.getAddonByID(addonId, addon => resolve(addon));
+   });
+ }
+ 
+ /**
+  * Uninstall an add-on.
+  */
+-function* tearDownAddon(addon) {
++async function tearDownAddon(addon) {
+   const onUninstalled = promiseAddonEvent("onUninstalled");
+   addon.uninstall();
+-  const [uninstalledAddon] = yield onUninstalled;
++  const [uninstalledAddon] = await onUninstalled;
+   is(uninstalledAddon.id, addon.id,
+      `Add-on was uninstalled: ${uninstalledAddon.id}`);
+ }

+ 2815 - 0
frg/work-js/mozilla-release/patches/1440321-1b-canvasdebugger-61a1.patch

@@ -0,0 +1,2815 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  86ff63ccc640e358f4e3be6f3678c4ac6fffe933
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1b. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/canvasdebugger/canvasdebugger.js b/devtools/client/canvasdebugger/canvasdebugger.js
+--- a/devtools/client/canvasdebugger/canvasdebugger.js
++++ b/devtools/client/canvasdebugger/canvasdebugger.js
+@@ -20,17 +20,16 @@ const { WidgetMethods, setNamedTimeout, 
+         setConditionalTimeout } = require("devtools/client/shared/widgets/view-helpers");
+ 
+ // Use privileged promise in panel documents to prevent having them to freeze
+ // during toolbox destruction. See bug 1402779.
+ const Promise = require("Promise");
+ 
+ const CANVAS_ACTOR_RECORDING_ATTEMPT = flags.testing ? 500 : 5000;
+ 
+-const { Task } = require("devtools/shared/task");
+ 
+ ChromeUtils.defineModuleGetter(this, "FileUtils",
+   "resource://gre/modules/FileUtils.jsm");
+ 
+ ChromeUtils.defineModuleGetter(this, "NetUtil",
+   "resource://gre/modules/NetUtil.jsm");
+ 
+ XPCOMUtils.defineLazyGetter(this, "NetworkHelper", function () {
+diff --git a/devtools/client/canvasdebugger/snapshotslist.js b/devtools/client/canvasdebugger/snapshotslist.js
+--- a/devtools/client/canvasdebugger/snapshotslist.js
++++ b/devtools/client/canvasdebugger/snapshotslist.js
+@@ -174,62 +174,62 @@ var SnapshotsListView = extend(WidgetMet
+     $("#reload-notice").hidden = true;
+     $("#empty-notice").hidden = true;
+     $("#waiting-notice").hidden = false;
+ 
+     $("#debugging-pane-contents").hidden = true;
+     $("#screenshot-container").hidden = true;
+     $("#snapshot-filmstrip").hidden = true;
+ 
+-    Task.spawn(function* () {
++    (async function () {
+       // Wait for a few milliseconds between presenting the function calls,
+       // screenshot and thumbnails, to allow each component being
+       // sequentially drawn. This gives the illusion of snappiness.
+ 
+-      yield DevToolsUtils.waitForTime(SNAPSHOT_DATA_DISPLAY_DELAY);
++      await DevToolsUtils.waitForTime(SNAPSHOT_DATA_DISPLAY_DELAY);
+       CallsListView.showCalls(calls);
+       $("#debugging-pane-contents").hidden = false;
+       $("#waiting-notice").hidden = true;
+ 
+-      yield DevToolsUtils.waitForTime(SNAPSHOT_DATA_DISPLAY_DELAY);
++      await DevToolsUtils.waitForTime(SNAPSHOT_DATA_DISPLAY_DELAY);
+       CallsListView.showThumbnails(thumbnails);
+       $("#snapshot-filmstrip").hidden = false;
+ 
+-      yield DevToolsUtils.waitForTime(SNAPSHOT_DATA_DISPLAY_DELAY);
++      await DevToolsUtils.waitForTime(SNAPSHOT_DATA_DISPLAY_DELAY);
+       CallsListView.showScreenshot(screenshot);
+       $("#screenshot-container").hidden = false;
+ 
+       window.emit(EVENTS.SNAPSHOT_RECORDING_SELECTED);
+-    });
++    })();
+   },
+ 
+   /**
+    * The click listener for the "clear" button in this container.
+    */
+   _onClearButtonClick: function () {
+-    Task.spawn(function* () {
++    (async function () {
+       SnapshotsListView.empty();
+       CallsListView.empty();
+ 
+       $("#reload-notice").hidden = true;
+       $("#empty-notice").hidden = true;
+       $("#waiting-notice").hidden = true;
+ 
+-      if (yield gFront.isInitialized()) {
++      if (await gFront.isInitialized()) {
+         $("#empty-notice").hidden = false;
+       } else {
+         $("#reload-notice").hidden = false;
+       }
+ 
+       $("#debugging-pane-contents").hidden = true;
+       $("#screenshot-container").hidden = true;
+       $("#snapshot-filmstrip").hidden = true;
+ 
+       window.emit(EVENTS.SNAPSHOTS_LIST_CLEARED);
+-    });
++    })();
+   },
+ 
+   /**
+    * The click listener for the "record" button in this container.
+    */
+   _onRecordButtonClick: function () {
+     this._disableRecordButton();
+ 
+@@ -265,82 +265,82 @@ var SnapshotsListView = extend(WidgetMet
+    */
+   _disableRecordButton: function () {
+     $("#record-snapshot").setAttribute("disabled", true);
+   },
+ 
+   /**
+    * Begins recording an animation.
+    */
+-  _recordAnimation: Task.async(function* () {
++  async _recordAnimation() {
+     if (this._recording) {
+       return;
+     }
+     this._recording = true;
+     $("#record-snapshot").setAttribute("checked", "true");
+ 
+     setNamedTimeout("canvas-actor-recording", CANVAS_ACTOR_RECORDING_ATTEMPT, this._stopRecordingAnimation);
+ 
+-    yield DevToolsUtils.waitForTime(SNAPSHOT_START_RECORDING_DELAY);
++    await DevToolsUtils.waitForTime(SNAPSHOT_START_RECORDING_DELAY);
+     window.emit(EVENTS.SNAPSHOT_RECORDING_STARTED);
+ 
+     gFront.recordAnimationFrame().then(snapshot => {
+       if (snapshot) {
+         this._onRecordSuccess(snapshot);
+       } else {
+         this._onRecordFailure();
+       }
+     });
+ 
+     // Wait another delay before reenabling the button to stop the recording
+     // if a recording is not found.
+-    yield DevToolsUtils.waitForTime(SNAPSHOT_START_RECORDING_DELAY);
++    await DevToolsUtils.waitForTime(SNAPSHOT_START_RECORDING_DELAY);
+     this._enableRecordButton();
+-  }),
++  },
+ 
+   /**
+    * Stops recording animation. Called when a click on the stopwatch occurs during a recording,
+    * or if a recording times out.
+    */
+-  _stopRecordingAnimation: Task.async(function* () {
++  async _stopRecordingAnimation() {
+     clearNamedTimeout("canvas-actor-recording");
+-    let actorCanStop = yield gTarget.actorHasMethod("canvas", "stopRecordingAnimationFrame");
++    let actorCanStop = await gTarget.actorHasMethod("canvas", "stopRecordingAnimationFrame");
+ 
+     if (actorCanStop) {
+-      yield gFront.stopRecordingAnimationFrame();
++      await gFront.stopRecordingAnimationFrame();
+     }
+     // If actor does not have the method to stop recording (Fx39+),
+     // manually call the record failure method. This will call a connection failure
+     // on disconnect as a result of `gFront.recordAnimationFrame()` never resolving,
+     // but this is better than it hanging when there is no requestAnimationFrame anyway.
+     else {
+       this._onRecordFailure();
+     }
+ 
+     this._recording = false;
+     $("#record-snapshot").removeAttribute("checked");
+     this._enableRecordButton();
+-  }),
++  },
+ 
+   /**
+    * Resolves from the front's recordAnimationFrame to setup the interface with the screenshots.
+    */
+-  _onRecordSuccess: Task.async(function* (snapshotActor) {
++  async _onRecordSuccess(snapshotActor) {
+     // Clear bail-out case if frame found in CANVAS_ACTOR_RECORDING_ATTEMPT milliseconds
+     clearNamedTimeout("canvas-actor-recording");
+     let snapshotItem = this.getItemAtIndex(this.itemCount - 1);
+-    let snapshotOverview = yield snapshotActor.getOverview();
++    let snapshotOverview = await snapshotActor.getOverview();
+     this.customizeSnapshot(snapshotItem, snapshotActor, snapshotOverview);
+ 
+     this._recording = false;
+     $("#record-snapshot").removeAttribute("checked");
+ 
+     window.emit(EVENTS.SNAPSHOT_RECORDING_COMPLETED);
+     window.emit(EVENTS.SNAPSHOT_RECORDING_FINISHED);
+-  }),
++  },
+ 
+   /**
+    * Called as a reject from the front's recordAnimationFrame.
+    */
+   _onRecordFailure: function () {
+     clearNamedTimeout("canvas-actor-recording");
+     showNotification(gToolbox, "canvas-debugger-timeout", L10N.getStr("recordingTimeoutFailure"));
+     window.emit(EVENTS.SNAPSHOT_RECORDING_CANCELLED);
+@@ -403,62 +403,62 @@ var SnapshotsListView = extend(WidgetMet
+     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+     fp.init(window, L10N.getStr("snapshotsList.saveDialogTitle"), Ci.nsIFilePicker.modeSave);
+     fp.appendFilter(L10N.getStr("snapshotsList.saveDialogJSONFilter"), "*.json");
+     fp.appendFilter(L10N.getStr("snapshotsList.saveDialogAllFilter"), "*.*");
+     fp.defaultString = "snapshot.json";
+ 
+     // Start serializing all the function call actors for the specified snapshot,
+     // while the nsIFilePicker dialog is being opened. Snappy.
+-    let serialized = Task.spawn(function* () {
++    let serialized = (async function () {
+       let data = {
+         fileType: CALLS_LIST_SERIALIZER_IDENTIFIER,
+         version: CALLS_LIST_SERIALIZER_VERSION,
+         calls: [],
+         thumbnails: [],
+         screenshot: null
+       };
+       let functionCalls = snapshotItem.attachment.calls;
+       let thumbnails = snapshotItem.attachment.thumbnails;
+       let screenshot = snapshotItem.attachment.screenshot;
+ 
+       // Prepare all the function calls for serialization.
+-      yield DevToolsUtils.yieldingEach(functionCalls, (call, i) => {
++      await DevToolsUtils.yieldingEach(functionCalls, (call, i) => {
+         let { type, name, file, line, timestamp, argsPreview, callerPreview } = call;
+         return call.getDetails().then(({ stack }) => {
+           data.calls[i] = {
+             type: type,
+             name: name,
+             file: file,
+             line: line,
+             stack: stack,
+             timestamp: timestamp,
+             argsPreview: argsPreview,
+             callerPreview: callerPreview
+           };
+         });
+       });
+ 
+       // Prepare all the thumbnails for serialization.
+-      yield DevToolsUtils.yieldingEach(thumbnails, (thumbnail, i) => {
++      await DevToolsUtils.yieldingEach(thumbnails, (thumbnail, i) => {
+         let { index, width, height, flipped, pixels } = thumbnail;
+         data.thumbnails.push({ index, width, height, flipped, pixels });
+       });
+ 
+       // Prepare the screenshot for serialization.
+       let { index, width, height, flipped, pixels } = screenshot;
+       data.screenshot = { index, width, height, flipped, pixels };
+ 
+       let string = JSON.stringify(data);
+       let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+         createInstance(Ci.nsIScriptableUnicodeConverter);
+ 
+       converter.charset = "UTF-8";
+       return converter.convertToInputStream(string);
+-    });
++    })();
+ 
+     // Open the nsIFilePicker and wait for the function call actors to finish
+     // being serialized, in order to save the generated JSON data to disk.
+     fp.open({ done: result => {
+       if (result == Ci.nsIFilePicker.returnCancel) {
+         return;
+       }
+       let footer = $(".snapshot-item-footer", snapshotItem.target);
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-01.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-01.js
+@@ -1,17 +1,17 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the canvas debugger leaks on initialization and sudden destruction.
+  * You can also use this initialization format as a template for other tests.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCallWatcherBackend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCallWatcherBackend(SIMPLE_CANVAS_URL);
+ 
+   ok(target, "Should have a target available.");
+   ok(front, "Should have a protocol front available.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-02.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-02.js
+@@ -1,36 +1,36 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if functions calls are recorded and stored for a canvas context,
+  * and that their stack is successfully retrieved.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCallWatcherBackend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCallWatcherBackend(SIMPLE_CANVAS_URL);
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({
++  await front.setup({
+     tracedGlobals: ["CanvasRenderingContext2D", "WebGLRenderingContext"],
+     startRecording: true,
+     performReload: true,
+     storeCalls: true
+   });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+   // Allow the content to execute some functions.
+-  yield waitForTick();
++  await waitForTick();
+ 
+-  let functionCalls = yield front.pauseRecording();
++  let functionCalls = await front.pauseRecording();
+   ok(functionCalls,
+     "An array of function call actors was sent after reloading.");
+   ok(functionCalls.length > 0,
+     "There's at least one function call actor available.");
+ 
+   is(functionCalls[0].type, CallWatcherFront.METHOD_FUNCTION,
+     "The called function is correctly identified as a method.");
+   is(functionCalls[0].name, "clearRect",
+@@ -40,17 +40,17 @@ function* ifTestingSupported() {
+   is(functionCalls[0].line, 25,
+     "The called function's line is correct.");
+ 
+   is(functionCalls[0].callerPreview, "Object",
+     "The called function's caller preview is correct.");
+   is(functionCalls[0].argsPreview, "0, 0, 128, 128",
+     "The called function's args preview is correct.");
+ 
+-  let details = yield functionCalls[1].getDetails();
++  let details = await functionCalls[1].getDetails();
+   ok(details,
+     "The first called function has some details available.");
+ 
+   is(details.stack.length, 3,
+     "The called function's stack depth is correct.");
+ 
+   is(details.stack[0].name, "fillStyle",
+     "The called function's stack is correct (1.1).");
+@@ -68,11 +68,11 @@ function* ifTestingSupported() {
+ 
+   is(details.stack[2].name, "drawScene",
+     "The called function's stack is correct (3.1).");
+   is(details.stack[2].file, SIMPLE_CANVAS_URL,
+     "The called function's stack is correct (3.2).");
+   is(details.stack[2].line, 33,
+     "The called function's stack is correct (3.3).");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-03.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-03.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-03.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-03.js
+@@ -1,32 +1,32 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if functions inside a single animation frame are recorded and stored
+  * for a canvas context.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  let snapshotActor = yield front.recordAnimationFrame();
++  let snapshotActor = await front.recordAnimationFrame();
+   ok(snapshotActor,
+     "A snapshot actor was sent after recording.");
+ 
+-  let animationOverview = yield snapshotActor.getOverview();
++  let animationOverview = await snapshotActor.getOverview();
+   ok(snapshotActor,
+     "An animation overview could be retrieved after recording.");
+ 
+   let functionCalls = animationOverview.calls;
+   ok(functionCalls,
+     "An array of function call actors was sent after recording.");
+   is(functionCalls.length, 8,
+     "The number of function call actors is correct.");
+@@ -65,11 +65,11 @@ function* ifTestingSupported() {
+     "The last called function's file is correct.");
+   is(functionCalls[7].line, 30,
+     "The last called function's line is correct.");
+   ok(functionCalls[7].argsPreview.includes("Function"),
+     "The last called function's args preview is correct.");
+   is(functionCalls[7].callerPreview, "Object",
+     "The last called function's caller preview is correct.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-04.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-04.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-04.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-04.js
+@@ -1,32 +1,32 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if draw calls inside a single animation frame generate and retrieve
+  * the correct thumbnails.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  let snapshotActor = yield front.recordAnimationFrame();
++  let snapshotActor = await front.recordAnimationFrame();
+   ok(snapshotActor,
+     "A snapshot actor was sent after recording.");
+ 
+-  let animationOverview = yield snapshotActor.getOverview();
++  let animationOverview = await snapshotActor.getOverview();
+   ok(animationOverview,
+     "An animation overview could be retrieved after recording.");
+ 
+   let thumbnails = animationOverview.thumbnails;
+   ok(thumbnails,
+     "An array of thumbnails was sent after recording.");
+   is(thumbnails.length, 4,
+     "The number of thumbnails is correct.");
+@@ -70,16 +70,16 @@ function* ifTestingSupported() {
+     "The fourth thumbnail's width is correct.");
+   is(thumbnails[3].height, 50,
+     "The fourth thumbnail's height is correct.");
+   is(thumbnails[3].flipped, false,
+     "The fourth thumbnail's flipped flag is correct.");
+   is([].find.call(Uint32(thumbnails[3].pixels), e => e > 0), 4290822336,
+     "The fourth thumbnail's pixels seem to not be completely transparent.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+ 
+ function Uint32(src) {
+   let charView = new Uint8Array(src);
+   return new Uint32Array(charView.buffer);
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-05.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-05.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-05.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-05.js
+@@ -1,32 +1,32 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if draw calls inside a single animation frame generate and retrieve
+  * the correct "end result" screenshot.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  let snapshotActor = yield front.recordAnimationFrame();
++  let snapshotActor = await front.recordAnimationFrame();
+   ok(snapshotActor,
+     "A snapshot actor was sent after recording.");
+ 
+-  let animationOverview = yield snapshotActor.getOverview();
++  let animationOverview = await snapshotActor.getOverview();
+   ok(snapshotActor,
+     "An animation overview could be retrieved after recording.");
+ 
+   let screenshot = animationOverview.screenshot;
+   ok(screenshot,
+     "A screenshot was sent after recording.");
+ 
+   is(screenshot.index, 6,
+@@ -35,16 +35,16 @@ function* ifTestingSupported() {
+     "The screenshot's width is correct.");
+   is(screenshot.height, 128,
+     "The screenshot's height is correct.");
+   is(screenshot.flipped, false,
+     "The screenshot's flipped flag is correct.");
+   is([].find.call(Uint32(screenshot.pixels), e => e > 0), 4290822336,
+     "The screenshot's pixels seem to not be completely transparent.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+ 
+ function Uint32(src) {
+   let charView = new Uint8Array(src);
+   return new Uint32Array(charView.buffer);
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-06.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-06.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-06.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-06.js
+@@ -1,48 +1,48 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if screenshots for arbitrary draw calls are generated properly.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_TRANSPARENT_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCanvasDebuggerBackend(SIMPLE_CANVAS_TRANSPARENT_URL);
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  let snapshotActor = yield front.recordAnimationFrame();
+-  let animationOverview = yield snapshotActor.getOverview();
++  let snapshotActor = await front.recordAnimationFrame();
++  let animationOverview = await snapshotActor.getOverview();
+ 
+   let functionCalls = animationOverview.calls;
+   ok(functionCalls,
+     "An array of function call actors was sent after recording.");
+   is(functionCalls.length, 8,
+     "The number of function call actors is correct.");
+ 
+   is(functionCalls[0].name, "clearRect",
+     "The first called function's name is correct.");
+   is(functionCalls[2].name, "fillRect",
+     "The second called function's name is correct.");
+   is(functionCalls[4].name, "fillRect",
+     "The third called function's name is correct.");
+   is(functionCalls[6].name, "fillRect",
+     "The fourth called function's name is correct.");
+ 
+-  let firstDrawCallScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[0]);
+-  let secondDrawCallScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[2]);
+-  let thirdDrawCallScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[4]);
+-  let fourthDrawCallScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[6]);
++  let firstDrawCallScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[0]);
++  let secondDrawCallScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[2]);
++  let thirdDrawCallScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[4]);
++  let fourthDrawCallScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[6]);
+ 
+   ok(firstDrawCallScreenshot,
+     "The first draw call has a screenshot attached.");
+   is(firstDrawCallScreenshot.index, 0,
+     "The first draw call has the correct screenshot index.");
+   is(firstDrawCallScreenshot.width, 128,
+     "The first draw call has the correct screenshot width.");
+   is(firstDrawCallScreenshot.height, 128,
+@@ -85,16 +85,16 @@ function* ifTestingSupported() {
+ 
+   isnot(firstDrawCallScreenshot.pixels, secondDrawCallScreenshot.pixels,
+     "The screenshots taken on consecutive draw calls are different (1).");
+   isnot(secondDrawCallScreenshot.pixels, thirdDrawCallScreenshot.pixels,
+     "The screenshots taken on consecutive draw calls are different (2).");
+   isnot(thirdDrawCallScreenshot.pixels, fourthDrawCallScreenshot.pixels,
+     "The screenshots taken on consecutive draw calls are different (3).");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+ 
+ function Uint32(src) {
+   let charView = new Uint8Array(src);
+   return new Uint32Array(charView.buffer);
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-07.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-07.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-07.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-07.js
+@@ -1,88 +1,88 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if screenshots for non-draw calls can still be retrieved properly,
+  * by deferring the the most recent previous draw-call.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  let snapshotActor = yield front.recordAnimationFrame();
+-  let animationOverview = yield snapshotActor.getOverview();
++  let snapshotActor = await front.recordAnimationFrame();
++  let animationOverview = await snapshotActor.getOverview();
+ 
+   let functionCalls = animationOverview.calls;
+   ok(functionCalls,
+     "An array of function call actors was sent after recording.");
+   is(functionCalls.length, 8,
+     "The number of function call actors is correct.");
+ 
+-  let firstNonDrawCall = yield functionCalls[1].getDetails();
+-  let secondNonDrawCall = yield functionCalls[3].getDetails();
+-  let lastNonDrawCall = yield functionCalls[7].getDetails();
++  let firstNonDrawCall = await functionCalls[1].getDetails();
++  let secondNonDrawCall = await functionCalls[3].getDetails();
++  let lastNonDrawCall = await functionCalls[7].getDetails();
+ 
+   is(firstNonDrawCall.name, "fillStyle",
+     "The first non-draw function's name is correct.");
+   is(secondNonDrawCall.name, "fillStyle",
+     "The second non-draw function's name is correct.");
+   is(lastNonDrawCall.name, "requestAnimationFrame",
+     "The last non-draw function's name is correct.");
+ 
+-  let firstScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[1]);
+-  let secondScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[3]);
+-  let lastScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[7]);
++  let firstScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[1]);
++  let secondScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[3]);
++  let lastScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[7]);
+ 
+   ok(firstScreenshot,
+     "A screenshot was successfully retrieved for the first non-draw function.");
+   ok(secondScreenshot,
+     "A screenshot was successfully retrieved for the second non-draw function.");
+   ok(lastScreenshot,
+     "A screenshot was successfully retrieved for the last non-draw function.");
+ 
+-  let firstActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[0]);
++  let firstActualScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[0]);
+   ok(sameArray(firstScreenshot.pixels, firstActualScreenshot.pixels),
+     "The screenshot for the first non-draw function is correct.");
+   is(firstScreenshot.width, 128,
+     "The screenshot for the first non-draw function has the correct width.");
+   is(firstScreenshot.height, 128,
+     "The screenshot for the first non-draw function has the correct height.");
+ 
+-  let secondActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[2]);
++  let secondActualScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[2]);
+   ok(sameArray(secondScreenshot.pixels, secondActualScreenshot.pixels),
+     "The screenshot for the second non-draw function is correct.");
+   is(secondScreenshot.width, 128,
+     "The screenshot for the second non-draw function has the correct width.");
+   is(secondScreenshot.height, 128,
+     "The screenshot for the second non-draw function has the correct height.");
+ 
+-  let lastActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[6]);
++  let lastActualScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[6]);
+   ok(sameArray(lastScreenshot.pixels, lastActualScreenshot.pixels),
+     "The screenshot for the last non-draw function is correct.");
+   is(lastScreenshot.width, 128,
+     "The screenshot for the last non-draw function has the correct width.");
+   is(lastScreenshot.height, 128,
+     "The screenshot for the last non-draw function has the correct height.");
+ 
+   ok(!sameArray(firstScreenshot.pixels, secondScreenshot.pixels),
+     "The screenshots taken on consecutive draw calls are different (1).");
+   ok(!sameArray(secondScreenshot.pixels, lastScreenshot.pixels),
+     "The screenshots taken on consecutive draw calls are different (2).");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+ 
+ function sameArray(a, b) {
+   if (a.length != b.length) {
+     return false;
+   }
+   for (let i = 0; i < a.length; i++) {
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-08.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-08.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-08.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-08.js
+@@ -1,36 +1,36 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that integers used in arguments are not cast to their constant, enum value
+  * forms if the method's signature does not expect an enum. Bug 999687.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_BITMASKS_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCanvasDebuggerBackend(SIMPLE_BITMASKS_URL);
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  let snapshotActor = yield front.recordAnimationFrame();
+-  let animationOverview = yield snapshotActor.getOverview();
++  let snapshotActor = await front.recordAnimationFrame();
++  let animationOverview = await snapshotActor.getOverview();
+   let functionCalls = animationOverview.calls;
+ 
+   is(functionCalls[0].name, "clearRect",
+     "The first called function's name is correct.");
+   is(functionCalls[0].argsPreview, "0, 0, 4, 4",
+     "The first called function's args preview is not cast to enums.");
+ 
+   is(functionCalls[2].name, "fillRect",
+     "The fillRect called function's name is correct.");
+   is(functionCalls[2].argsPreview, "0, 0, 1, 1",
+     "The fillRect called function's args preview is not casted to enums.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-09.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-09.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-09.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-09.js
+@@ -1,36 +1,36 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that integers used in arguments are not cast to their constant, enum value
+  * forms if the method's signature does not expect an enum. Bug 999687.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCanvasDebuggerBackend(WEBGL_ENUM_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCanvasDebuggerBackend(WEBGL_ENUM_URL);
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  let snapshotActor = yield front.recordAnimationFrame();
+-  let animationOverview = yield snapshotActor.getOverview();
++  let snapshotActor = await front.recordAnimationFrame();
++  let animationOverview = await snapshotActor.getOverview();
+   let functionCalls = animationOverview.calls;
+ 
+   is(functionCalls[0].name, "clear",
+     "The function's name is correct.");
+   is(functionCalls[0].argsPreview, "DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT",
+     "The bits passed into `gl.clear` have been cast to their enum values.");
+ 
+   is(functionCalls[1].name, "bindTexture",
+     "The function's name is correct.");
+   is(functionCalls[1].argsPreview, "TEXTURE_2D, null",
+     "The bits passed into `gl.bindTexture` have been cast to their enum values.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-10.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-10.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-10.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-10.js
+@@ -1,69 +1,69 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the correct framebuffer, renderbuffer and textures are re-bound
+  * after generating screenshots using the actor.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCanvasDebuggerBackend(WEBGL_BINDINGS_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCanvasDebuggerBackend(WEBGL_BINDINGS_URL);
+   loadFrameScriptUtils();
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  let snapshotActor = yield front.recordAnimationFrame();
+-  let animationOverview = yield snapshotActor.getOverview();
++  let snapshotActor = await front.recordAnimationFrame();
++  let animationOverview = await snapshotActor.getOverview();
+   let functionCalls = animationOverview.calls;
+ 
+-  let firstScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[0]);
++  let firstScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[0]);
+   is(firstScreenshot.index, -1,
+     "The first screenshot didn't encounter any draw call.");
+   is(firstScreenshot.scaling, 0.25,
+     "The first screenshot has the correct scaling.");
+   is(firstScreenshot.width, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT,
+     "The first screenshot has the correct width.");
+   is(firstScreenshot.height, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT,
+     "The first screenshot has the correct height.");
+   is(firstScreenshot.flipped, true,
+     "The first screenshot has the correct 'flipped' flag.");
+   is(firstScreenshot.pixels.length, 0,
+     "The first screenshot should be empty.");
+ 
+-  is((yield evalInDebuggee("gl.getParameter(gl.FRAMEBUFFER_BINDING) === customFramebuffer")),
++  is((await evalInDebuggee("gl.getParameter(gl.FRAMEBUFFER_BINDING) === customFramebuffer")),
+     true,
+     "The debuggee's gl context framebuffer wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.RENDERBUFFER_BINDING) === customRenderbuffer")),
++  is((await evalInDebuggee("gl.getParameter(gl.RENDERBUFFER_BINDING) === customRenderbuffer")),
+     true,
+     "The debuggee's gl context renderbuffer wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.TEXTURE_BINDING_2D) === customTexture")),
++  is((await evalInDebuggee("gl.getParameter(gl.TEXTURE_BINDING_2D) === customTexture")),
+     true,
+     "The debuggee's gl context texture binding wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[0]")),
++  is((await evalInDebuggee("gl.getParameter(gl.VIEWPORT)[0]")),
+     128,
+     "The debuggee's gl context viewport's left coord. wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[1]")),
++  is((await evalInDebuggee("gl.getParameter(gl.VIEWPORT)[1]")),
+     256,
+     "The debuggee's gl context viewport's left coord. wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[2]")),
++  is((await evalInDebuggee("gl.getParameter(gl.VIEWPORT)[2]")),
+     384,
+     "The debuggee's gl context viewport's left coord. wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[3]")),
++  is((await evalInDebuggee("gl.getParameter(gl.VIEWPORT)[3]")),
+     512,
+     "The debuggee's gl context viewport's left coord. wasn't changed.");
+ 
+-  let secondScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[1]);
++  let secondScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[1]);
+   is(secondScreenshot.index, 1,
+     "The second screenshot has the correct index.");
+   is(secondScreenshot.width, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT,
+     "The second screenshot has the correct width.");
+   is(secondScreenshot.height, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT,
+     "The second screenshot has the correct height.");
+   is(secondScreenshot.scaling, 0.25,
+     "The second screenshot has the correct scaling.");
+@@ -75,33 +75,33 @@ function* ifTestingSupported() {
+     "The second screenshot has the correct red component.");
+   is(secondScreenshot.pixels[1], 0,
+     "The second screenshot has the correct green component.");
+   is(secondScreenshot.pixels[2], 255,
+     "The second screenshot has the correct blue component.");
+   is(secondScreenshot.pixels[3], 255,
+     "The second screenshot has the correct alpha component.");
+ 
+-  is((yield evalInDebuggee("gl.getParameter(gl.FRAMEBUFFER_BINDING) === customFramebuffer")),
++  is((await evalInDebuggee("gl.getParameter(gl.FRAMEBUFFER_BINDING) === customFramebuffer")),
+     true,
+     "The debuggee's gl context framebuffer still wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.RENDERBUFFER_BINDING) === customRenderbuffer")),
++  is((await evalInDebuggee("gl.getParameter(gl.RENDERBUFFER_BINDING) === customRenderbuffer")),
+     true,
+     "The debuggee's gl context renderbuffer still wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.TEXTURE_BINDING_2D) === customTexture")),
++  is((await evalInDebuggee("gl.getParameter(gl.TEXTURE_BINDING_2D) === customTexture")),
+     true,
+     "The debuggee's gl context texture binding still wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[0]")),
++  is((await evalInDebuggee("gl.getParameter(gl.VIEWPORT)[0]")),
+     128,
+     "The debuggee's gl context viewport's left coord. still wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[1]")),
++  is((await evalInDebuggee("gl.getParameter(gl.VIEWPORT)[1]")),
+     256,
+     "The debuggee's gl context viewport's left coord. still wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[2]")),
++  is((await evalInDebuggee("gl.getParameter(gl.VIEWPORT)[2]")),
+     384,
+     "The debuggee's gl context viewport's left coord. still wasn't changed.");
+-  is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[3]")),
++  is((await evalInDebuggee("gl.getParameter(gl.VIEWPORT)[3]")),
+     512,
+     "The debuggee's gl context viewport's left coord. still wasn't changed.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-11.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-11.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-11.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-11.js
+@@ -1,32 +1,32 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that loops using setTimeout are recorded and stored
+  * for a canvas context, and that the generated screenshots are correct.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCanvasDebuggerBackend(SET_TIMEOUT_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCanvasDebuggerBackend(SET_TIMEOUT_URL);
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  let snapshotActor = yield front.recordAnimationFrame();
++  let snapshotActor = await front.recordAnimationFrame();
+   ok(snapshotActor,
+     "A snapshot actor was sent after recording.");
+ 
+-  let animationOverview = yield snapshotActor.getOverview();
++  let animationOverview = await snapshotActor.getOverview();
+   ok(snapshotActor,
+     "An animation overview could be retrieved after recording.");
+ 
+   let functionCalls = animationOverview.calls;
+   ok(functionCalls,
+     "An array of function call actors was sent after recording.");
+   is(functionCalls.length, 8,
+     "The number of function call actors is correct.");
+@@ -65,68 +65,68 @@ function* ifTestingSupported() {
+     "The last called function's file is correct.");
+   is(functionCalls[7].line, 30,
+     "The last called function's line is correct.");
+   ok(functionCalls[7].argsPreview.includes("Function"),
+     "The last called function's args preview is correct.");
+   is(functionCalls[7].callerPreview, "Object",
+     "The last called function's caller preview is correct.");
+ 
+-  let firstNonDrawCall = yield functionCalls[1].getDetails();
+-  let secondNonDrawCall = yield functionCalls[3].getDetails();
+-  let lastNonDrawCall = yield functionCalls[7].getDetails();
++  let firstNonDrawCall = await functionCalls[1].getDetails();
++  let secondNonDrawCall = await functionCalls[3].getDetails();
++  let lastNonDrawCall = await functionCalls[7].getDetails();
+ 
+   is(firstNonDrawCall.name, "fillStyle",
+     "The first non-draw function's name is correct.");
+   is(secondNonDrawCall.name, "fillStyle",
+     "The second non-draw function's name is correct.");
+   is(lastNonDrawCall.name, "setTimeout",
+     "The last non-draw function's name is correct.");
+ 
+-  let firstScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[1]);
+-  let secondScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[3]);
+-  let lastScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[7]);
++  let firstScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[1]);
++  let secondScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[3]);
++  let lastScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[7]);
+ 
+   ok(firstScreenshot,
+     "A screenshot was successfully retrieved for the first non-draw function.");
+   ok(secondScreenshot,
+     "A screenshot was successfully retrieved for the second non-draw function.");
+   ok(lastScreenshot,
+     "A screenshot was successfully retrieved for the last non-draw function.");
+ 
+-  let firstActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[0]);
++  let firstActualScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[0]);
+   ok(sameArray(firstScreenshot.pixels, firstActualScreenshot.pixels),
+     "The screenshot for the first non-draw function is correct.");
+   is(firstScreenshot.width, 128,
+     "The screenshot for the first non-draw function has the correct width.");
+   is(firstScreenshot.height, 128,
+     "The screenshot for the first non-draw function has the correct height.");
+ 
+-  let secondActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[2]);
++  let secondActualScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[2]);
+   ok(sameArray(secondScreenshot.pixels, secondActualScreenshot.pixels),
+     "The screenshot for the second non-draw function is correct.");
+   is(secondScreenshot.width, 128,
+     "The screenshot for the second non-draw function has the correct width.");
+   is(secondScreenshot.height, 128,
+     "The screenshot for the second non-draw function has the correct height.");
+ 
+-  let lastActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[6]);
++  let lastActualScreenshot = await snapshotActor.generateScreenshotFor(functionCalls[6]);
+   ok(sameArray(lastScreenshot.pixels, lastActualScreenshot.pixels),
+     "The screenshot for the last non-draw function is correct.");
+   is(lastScreenshot.width, 128,
+     "The screenshot for the last non-draw function has the correct width.");
+   is(lastScreenshot.height, 128,
+     "The screenshot for the last non-draw function has the correct height.");
+ 
+   ok(!sameArray(firstScreenshot.pixels, secondScreenshot.pixels),
+     "The screenshots taken on consecutive draw calls are different (1).");
+   ok(!sameArray(secondScreenshot.pixels, lastScreenshot.pixels),
+     "The screenshots taken on consecutive draw calls are different (2).");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+ 
+ function sameArray(a, b) {
+   if (a.length != b.length) {
+     return false;
+   }
+   for (let i = 0; i < a.length; i++) {
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-12.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-12.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-12.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-12.js
+@@ -1,29 +1,29 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the recording can be disabled via stopRecordingAnimationFrame
+  * in the event no rAF loop is found.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, front } = yield initCanvasDebuggerBackend(NO_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, front } = await initCanvasDebuggerBackend(NO_CANVAS_URL);
+   loadFrameScriptUtils();
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+   let startRecording = front.recordAnimationFrame();
+-  yield front.stopRecordingAnimationFrame();
++  await front.stopRecordingAnimationFrame();
+ 
+-  ok(!(yield startRecording),
++  ok(!(await startRecording),
+     "recordAnimationFrame() does not return a SnapshotActor when cancelled.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-highlight.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-highlight.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-highlight.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-highlight.js
+@@ -1,25 +1,25 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if certain function calls are properly highlighted in the UI.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([recordingFinished, callListPopulated]);
++  await promise.all([recordingFinished, callListPopulated]);
+ 
+   is(CallsListView.itemCount, 8,
+     "All the function calls should now be displayed in the UI.");
+ 
+   is($(".call-item-view", CallsListView.getItemAtIndex(0).target).hasAttribute("draw-call"), true,
+     "The first item's node should have a draw-call attribute.");
+   is($(".call-item-view", CallsListView.getItemAtIndex(1).target).hasAttribute("draw-call"), false,
+     "The second item's node should not have a draw-call attribute.");
+@@ -31,11 +31,11 @@ function* ifTestingSupported() {
+     "The fifth item's node should have a draw-call attribute.");
+   is($(".call-item-view", CallsListView.getItemAtIndex(5).target).hasAttribute("draw-call"), false,
+     "The sixth item's node should not have a draw-call attribute.");
+   is($(".call-item-view", CallsListView.getItemAtIndex(6).target).hasAttribute("draw-call"), true,
+     "The seventh item's node should have a draw-call attribute.");
+   is($(".call-item-view", CallsListView.getItemAtIndex(7).target).hasAttribute("draw-call"), false,
+     "The eigth item's node should not have a draw-call attribute.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-list.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-list.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-list.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-list.js
+@@ -1,26 +1,26 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if all the function calls associated with an animation frame snapshot
+  * are properly displayed in the UI.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([recordingFinished, callListPopulated]);
++  await promise.all([recordingFinished, callListPopulated]);
+ 
+   is(CallsListView.itemCount, 8,
+     "All the function calls should now be displayed in the UI.");
+ 
+   testItem(CallsListView.getItemAtIndex(0),
+     "1", "Object", "clearRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:25");
+ 
+   testItem(CallsListView.getItemAtIndex(1),
+@@ -60,11 +60,11 @@ function* ifTestingSupported() {
+     is($(".call-item-name", item.target).getAttribute("value"), name,
+       "The item's name label has the correct text.");
+     is($(".call-item-args", item.target).getAttribute("value"), args,
+       "The item's args label has the correct text.");
+     is($(".call-item-location", item.target).getAttribute("value"), location,
+       "The item's location label has the correct text.");
+   }
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-search.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-search.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-search.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-search.js
+@@ -1,26 +1,26 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if filtering the items in the call list works properly.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+   let searchbox = $("#calls-searchbox");
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let firstRecordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([firstRecordingFinished, callListPopulated]);
++  await promise.all([firstRecordingFinished, callListPopulated]);
+ 
+   is(searchbox.value, "",
+     "The searchbox should be initially empty.");
+   is(CallsListView.visibleItems.length, 8,
+     "All the items should be initially visible in the calls list.");
+ 
+   searchbox.focus();
+   EventUtils.sendString("clear", window);
+@@ -42,31 +42,31 @@ function* ifTestingSupported() {
+     "The visible item's args have the expected value.");
+   is(CallsListView.visibleItems[0].attachment.actor.callerPreview, "Object",
+     "The visible item's caller has the expected value.");
+ 
+   let secondRecordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+ 
+   SnapshotsListView._onRecordButtonClick();
+-  yield secondRecordingFinished;
++  await secondRecordingFinished;
+ 
+   SnapshotsListView.selectedIndex = 1;
+-  yield callListPopulated;
++  await callListPopulated;
+ 
+   is(searchbox.value, "clear",
+     "The searchbox should still contain the 'clear' string.");
+   is(CallsListView.visibleItems.length, 1,
+     "Only one item should still be visible in the calls list.");
+ 
+   for (let i = 0; i < 5; i++) {
+     searchbox.focus();
+     EventUtils.sendKey("BACK_SPACE", window);
+   }
+ 
+   is(searchbox.value, "",
+     "The searchbox should now be emptied.");
+   is(CallsListView.visibleItems.length, 8,
+     "All the items should be initially visible again in the calls list.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js
+@@ -2,40 +2,40 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the a function call's stack is properly displayed in the UI.
+  */
+ 
+ // Force the old debugger UI since it's directly used (see Bug 1301705)
+ Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+-registerCleanupFunction(function* () {
++registerCleanupFunction(function() {
+   Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+ });
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
+   let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([recordingFinished, callListPopulated]);
++  await promise.all([recordingFinished, callListPopulated]);
+ 
+   let callItem = CallsListView.getItemAtIndex(2);
+   let locationLink = $(".call-item-location", callItem.target);
+ 
+   is($(".call-item-stack", callItem.target), null,
+     "There should be no stack container available yet for the draw call.");
+ 
+   let callStackDisplayed = once(window, EVENTS.CALL_STACK_DISPLAYED);
+   EventUtils.sendMouseEvent({ type: "mousedown" }, locationLink, window);
+-  yield callStackDisplayed;
++  await callStackDisplayed;
+ 
+   isnot($(".call-item-stack", callItem.target), null,
+     "There should be a stack container available now for the draw call.");
+   // We may have more than 4 functions, depending on whether async
+   // stacks are available.
+   ok($all(".call-item-stack-fn", callItem.target).length >= 4,
+      "There should be at least 4 functions on the stack for the draw call.");
+ 
+@@ -62,21 +62,21 @@ function* ifTestingSupported() {
+     "doc_simple-canvas-deep-stack.html:30",
+     "The third function on the stack has the correct location.");
+   is($all(".call-item-stack-fn-location", callItem.target)[3].getAttribute("value"),
+     "doc_simple-canvas-deep-stack.html:35",
+     "The fourth function on the stack has the correct location.");
+ 
+   let jumpedToSource = once(window, EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER);
+   EventUtils.sendMouseEvent({ type: "mousedown" }, $(".call-item-stack-fn-location", callItem.target));
+-  yield jumpedToSource;
++  await jumpedToSource;
+ 
+-  let toolbox = yield gDevTools.getToolbox(target);
++  let toolbox = await gDevTools.getToolbox(target);
+   let { panelWin: { DebuggerView: view } } = toolbox.getPanel("jsdebugger");
+ 
+   is(view.Sources.selectedValue, getSourceActor(view.Sources, SIMPLE_CANVAS_DEEP_STACK_URL),
+     "The expected source was shown in the debugger.");
+   is(view.editor.getCursor().line, 25,
+     "The expected source line is highlighted in the debugger.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js
+@@ -3,55 +3,55 @@
+ 
+ /**
+  * Tests if the a function call's stack is properly displayed in the UI
+  * and jumping to source in the debugger for the topmost call item works.
+  */
+ 
+ // Force the old debugger UI since it's directly used (see Bug 1301705)
+ Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+-registerCleanupFunction(function* () {
++registerCleanupFunction(function() {
+   Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+ });
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
+   let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([recordingFinished, callListPopulated]);
++  await promise.all([recordingFinished, callListPopulated]);
+ 
+   let callItem = CallsListView.getItemAtIndex(2);
+   let locationLink = $(".call-item-location", callItem.target);
+ 
+   is($(".call-item-stack", callItem.target), null,
+     "There should be no stack container available yet for the draw call.");
+ 
+   let callStackDisplayed = once(window, EVENTS.CALL_STACK_DISPLAYED);
+   EventUtils.sendMouseEvent({ type: "mousedown" }, locationLink, window);
+-  yield callStackDisplayed;
++  await callStackDisplayed;
+ 
+   isnot($(".call-item-stack", callItem.target), null,
+     "There should be a stack container available now for the draw call.");
+   // We may have more than 4 functions, depending on whether async
+   // stacks are available.
+   ok($all(".call-item-stack-fn", callItem.target).length >= 4,
+      "There should be at least 4 functions on the stack for the draw call.");
+ 
+   let jumpedToSource = once(window, EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER);
+   EventUtils.sendMouseEvent({ type: "mousedown" }, $(".call-item-location", callItem.target));
+-  yield jumpedToSource;
++  await jumpedToSource;
+ 
+-  let toolbox = yield gDevTools.getToolbox(target);
++  let toolbox = await gDevTools.getToolbox(target);
+   let { panelWin: { DebuggerView: view } } = toolbox.getPanel("jsdebugger");
+ 
+   is(view.Sources.selectedValue, getSourceActor(view.Sources, SIMPLE_CANVAS_DEEP_STACK_URL),
+     "The expected source was shown in the debugger.");
+   is(view.editor.getCursor().line, 23,
+     "The expected source line is highlighted in the debugger.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js
+@@ -1,41 +1,41 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the a function call's stack can be shown/hidden by double-clicking
+  * on a function call item.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
+   let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([recordingFinished, callListPopulated]);
++  await promise.all([recordingFinished, callListPopulated]);
+ 
+   let callItem = CallsListView.getItemAtIndex(2);
+   let view = $(".call-item-view", callItem.target);
+   let contents = $(".call-item-contents", callItem.target);
+ 
+   is(view.hasAttribute("call-stack-populated"), false,
+     "The call item's view should not have the stack populated yet.");
+   is(view.hasAttribute("call-stack-expanded"), false,
+     "The call item's view should not have the stack populated yet.");
+   is($(".call-item-stack", callItem.target), null,
+     "There should be no stack container available yet for the draw call.");
+ 
+   let callStackDisplayed = once(window, EVENTS.CALL_STACK_DISPLAYED);
+   EventUtils.sendMouseEvent({ type: "dblclick" }, contents, window);
+-  yield callStackDisplayed;
++  await callStackDisplayed;
+ 
+   is(view.hasAttribute("call-stack-populated"), true,
+     "The call item's view should have the stack populated now.");
+   is(view.getAttribute("call-stack-expanded"), "true",
+     "The call item's view should have the stack expanded now.");
+   isnot($(".call-item-stack", callItem.target), null,
+     "There should be a stack container available now for the draw call.");
+   is($(".call-item-stack", callItem.target).hidden, false,
+@@ -55,11 +55,11 @@ function* ifTestingSupported() {
+     "There should still be a stack container available for the draw call.");
+   is($(".call-item-stack", callItem.target).hidden, true,
+     "The stack container should now be hidden.");
+   // We may have more than 4 functions, depending on whether async
+   // stacks are available.
+   ok($all(".call-item-stack-fn", callItem.target).length >= 4,
+      "There should still be at least 4 functions on the stack for the draw call.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-clear.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-clear.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-clear.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-clear.js
+@@ -1,43 +1,43 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if clearing the snapshots list works as expected.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, EVENTS, SnapshotsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let firstRecordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   SnapshotsListView._onRecordButtonClick();
+ 
+-  yield firstRecordingFinished;
++  await firstRecordingFinished;
+   ok(true, "Finished recording a snapshot of the animation loop.");
+ 
+   is(SnapshotsListView.itemCount, 1,
+     "There should be one item available in the snapshots list.");
+ 
+   let secondRecordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   SnapshotsListView._onRecordButtonClick();
+ 
+-  yield secondRecordingFinished;
++  await secondRecordingFinished;
+   ok(true, "Finished recording another snapshot of the animation loop.");
+ 
+   is(SnapshotsListView.itemCount, 2,
+     "There should be two items available in the snapshots list.");
+ 
+   let clearingFinished = once(window, EVENTS.SNAPSHOTS_LIST_CLEARED);
+   SnapshotsListView._onClearButtonClick();
+ 
+-  yield clearingFinished;
++  await clearingFinished;
+   ok(true, "Finished recording all snapshots.");
+ 
+   is(SnapshotsListView.itemCount, 0,
+     "There should be no items available in the snapshots list.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js
+@@ -1,34 +1,34 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if screenshots are properly displayed in the UI.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([recordingFinished, callListPopulated, screenshotDisplayed]);
++  await promise.all([recordingFinished, callListPopulated, screenshotDisplayed]);
+ 
+   is($("#screenshot-container").hidden, false,
+     "The screenshot container should now be visible.");
+ 
+   is($("#screenshot-dimensions").getAttribute("value"), "128" + "\u00D7" + "128",
+     "The screenshot dimensions label has the expected value.");
+ 
+   is($("#screenshot-image").getAttribute("flipped"), "false",
+     "The screenshot element should not be flipped vertically.");
+ 
+   ok(window.getComputedStyle($("#screenshot-image")).backgroundImage.includes("#screenshot-rendering"),
+     "The screenshot element should have an offscreen canvas element as a background.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-01.js
+@@ -1,26 +1,26 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if thumbnails are properly displayed in the UI.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, $all, EVENTS, SnapshotsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([recordingFinished, callListPopulated, thumbnailsDisplayed]);
++  await promise.all([recordingFinished, callListPopulated, thumbnailsDisplayed]);
+ 
+   is($all(".filmstrip-thumbnail").length, 4,
+     "There should be 4 thumbnails displayed in the UI.");
+ 
+   let firstThumbnail = $(".filmstrip-thumbnail[index='0']");
+   ok(firstThumbnail,
+     "The first thumbnail element should be for the function call at index 0.");
+   is(firstThumbnail.width, 50,
+@@ -55,11 +55,11 @@ function* ifTestingSupported() {
+     "The fourth thumbnail element should be for the function call at index 6.");
+   is(fourthThumbnail.width, 50,
+     "The fourth thumbnail's width is correct.");
+   is(fourthThumbnail.height, 50,
+     "The fourth thumbnail's height is correct.");
+   is(fourthThumbnail.getAttribute("flipped"), "false",
+     "The fourth thumbnail should not be flipped vertically.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-02.js
+@@ -1,67 +1,67 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if thumbnails are correctly linked with other UI elements like
+  * function call items and their respective screenshots.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED);
+   let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([
++  await promise.all([
+     recordingFinished,
+     callListPopulated,
+     thumbnailsDisplayed,
+     screenshotDisplayed
+   ]);
+ 
+   is($all(".filmstrip-thumbnail[highlighted]").length, 0,
+     "There should be no highlighted thumbnail available yet.");
+   is(CallsListView.selectedIndex, -1,
+     "There should be no selected item in the calls list view.");
+ 
+   EventUtils.sendMouseEvent({ type: "mousedown" }, $all(".filmstrip-thumbnail")[0], window);
+-  yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
++  await once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   info("The first draw call was selected, by clicking the first thumbnail.");
+ 
+   isnot($(".filmstrip-thumbnail[highlighted][index='0']"), null,
+     "There should be a highlighted thumbnail available now, for the first draw call.");
+   is($all(".filmstrip-thumbnail[highlighted]").length, 1,
+     "There should be only one highlighted thumbnail available now.");
+   is(CallsListView.selectedIndex, 0,
+     "The first draw call should be selected in the calls list view.");
+ 
+   EventUtils.sendMouseEvent({ type: "mousedown" }, $all(".call-item-view")[1], window);
+-  yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
++  await once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   info("The second context call was selected, by clicking the second call item.");
+ 
+   isnot($(".filmstrip-thumbnail[highlighted][index='0']"), null,
+     "There should be a highlighted thumbnail available, for the first draw call.");
+   is($all(".filmstrip-thumbnail[highlighted]").length, 1,
+     "There should be only one highlighted thumbnail available.");
+   is(CallsListView.selectedIndex, 1,
+     "The second draw call should be selected in the calls list view.");
+ 
+   EventUtils.sendMouseEvent({ type: "mousedown" }, $all(".call-item-view")[2], window);
+-  yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
++  await once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   info("The second draw call was selected, by clicking the third call item.");
+ 
+   isnot($(".filmstrip-thumbnail[highlighted][index='2']"), null,
+     "There should be a highlighted thumbnail available, for the second draw call.");
+   is($all(".filmstrip-thumbnail[highlighted]").length, 1,
+     "There should be only one highlighted thumbnail available.");
+   is(CallsListView.selectedIndex, 2,
+     "The second draw call should be selected in the calls list view.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-open.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-open.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-open.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-open.js
+@@ -1,17 +1,17 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the frontend UI is properly configured when opening the tool.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { $ } = panel.panelWin;
+ 
+   is($("#snapshots-pane").hasAttribute("hidden"), false,
+     "The snapshots pane should initially be visible.");
+   is($("#debugging-pane").hasAttribute("hidden"), false,
+     "The debugging pane should initially be visible.");
+ 
+   is($("#record-snapshot").getAttribute("hidden"), "true",
+@@ -31,11 +31,11 @@ function* ifTestingSupported() {
+   is($("#screenshot-container").getAttribute("hidden"), "true",
+     "The screenshot container should initially be hidden.");
+   is($("#snapshot-filmstrip").getAttribute("hidden"), "true",
+     "The snapshot filmstrip should initially be hidden.");
+ 
+   is($("#debugging-pane-contents").getAttribute("hidden"), "true",
+     "The rest of the UI should initially be hidden.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-01.js
+@@ -1,20 +1,20 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests whether the frontend behaves correctly while reording a snapshot.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   is($("#record-snapshot").hasAttribute("checked"), false,
+     "The 'record snapshot' button should initially be unchecked.");
+   is($("#record-snapshot").hasAttribute("disabled"), false,
+     "The 'record snapshot' button should initially be enabled.");
+   is($("#record-snapshot").hasAttribute("hidden"), false,
+     "The 'record snapshot' button should now be visible.");
+ 
+@@ -22,39 +22,39 @@ function* ifTestingSupported() {
+     "There should be no items available in the snapshots list view.");
+   is(SnapshotsListView.selectedIndex, -1,
+     "There should be no selected item in the snapshots list view.");
+ 
+   let recordingStarted = once(window, EVENTS.SNAPSHOT_RECORDING_STARTED);
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   SnapshotsListView._onRecordButtonClick();
+ 
+-  yield recordingStarted;
++  await recordingStarted;
+   ok(true, "Started recording a snapshot of the animation loop.");
+ 
+   is($("#record-snapshot").getAttribute("checked"), "true",
+     "The 'record snapshot' button should now be checked.");
+   is($("#record-snapshot").hasAttribute("hidden"), false,
+     "The 'record snapshot' button should still be visible.");
+ 
+   is(SnapshotsListView.itemCount, 1,
+     "There should be one item available in the snapshots list view now.");
+   is(SnapshotsListView.selectedIndex, -1,
+     "There should be no selected item in the snapshots list view yet.");
+ 
+-  yield recordingFinished;
++  await recordingFinished;
+   ok(true, "Finished recording a snapshot of the animation loop.");
+ 
+   is($("#record-snapshot").hasAttribute("checked"), false,
+     "The 'record snapshot' button should now be unchecked.");
+   is($("#record-snapshot").hasAttribute("disabled"), false,
+     "The 'record snapshot' button should now be re-enabled.");
+   is($("#record-snapshot").hasAttribute("hidden"), false,
+     "The 'record snapshot' button should still be visible.");
+ 
+   is(SnapshotsListView.itemCount, 1,
+     "There should still be only one item available in the snapshots list view.");
+   is(SnapshotsListView.selectedIndex, 0,
+     "There should be one selected item in the snapshots list view now.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-02.js
+@@ -1,27 +1,27 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests whether the frontend displays a placeholder snapshot while recording.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, EVENTS, L10N, $, SnapshotsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingStarted = once(window, EVENTS.SNAPSHOT_RECORDING_STARTED);
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let recordingSelected = once(window, EVENTS.SNAPSHOT_RECORDING_SELECTED);
+   SnapshotsListView._onRecordButtonClick();
+ 
+-  yield recordingStarted;
++  await recordingStarted;
+   ok(true, "Started recording a snapshot of the animation loop.");
+ 
+   let item = SnapshotsListView.getItemAtIndex(0);
+ 
+   is($(".snapshot-item-title", item.target).getAttribute("value"),
+     L10N.getFormatStr("snapshotsList.itemLabel", 1),
+     "The placeholder item's title label is correct.");
+ 
+@@ -42,20 +42,20 @@ function* ifTestingSupported() {
+   is($("#screenshot-container").getAttribute("hidden"), "true",
+     "The screenshot container should still be hidden.");
+   is($("#snapshot-filmstrip").getAttribute("hidden"), "true",
+     "The snapshot filmstrip should still be hidden.");
+ 
+   is($("#debugging-pane-contents").getAttribute("hidden"), "true",
+     "The rest of the UI should still be hidden.");
+ 
+-  yield recordingFinished;
++  await recordingFinished;
+   ok(true, "Finished recording a snapshot of the animation loop.");
+ 
+-  yield recordingSelected;
++  await recordingSelected;
+   ok(true, "Finished selecting a snapshot of the animation loop.");
+ 
+   is($("#reload-notice").getAttribute("hidden"), "true",
+     "The reload notice should now be hidden.");
+   is($("#empty-notice").getAttribute("hidden"), "true",
+     "The empty notice should now be hidden.");
+   is($("#waiting-notice").getAttribute("hidden"), "true",
+     "The waiting notice should now be hidden.");
+@@ -63,11 +63,11 @@ function* ifTestingSupported() {
+   is($("#screenshot-container").hasAttribute("hidden"), false,
+     "The screenshot container should now be visible.");
+   is($("#snapshot-filmstrip").hasAttribute("hidden"), false,
+     "The snapshot filmstrip should now be visible.");
+ 
+   is($("#debugging-pane-contents").hasAttribute("hidden"), false,
+     "The rest of the UI should now be visible.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-03.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-03.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-03.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-03.js
+@@ -1,37 +1,37 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests whether the frontend displays the correct info for a snapshot
+  * after finishing recording.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   SnapshotsListView._onRecordButtonClick();
+ 
+-  yield recordingFinished;
++  await recordingFinished;
+   ok(true, "Finished recording a snapshot of the animation loop.");
+ 
+   let item = SnapshotsListView.getItemAtIndex(0);
+ 
+   is(SnapshotsListView.selectedItem, item,
+     "The first item should now be selected in the snapshots list view (1).");
+   is(SnapshotsListView.selectedIndex, 0,
+     "The first item should now be selected in the snapshots list view (2).");
+ 
+   is($(".snapshot-item-calls", item.target).getAttribute("value"), "4 draws, 8 calls",
+     "The placeholder item's calls label is correct.");
+   is($(".snapshot-item-save", item.target).getAttribute("value"), "Save",
+     "The placeholder item's save label is correct.");
+   is($(".snapshot-item-save", item.target).getAttribute("disabled"), "false",
+     "The placeholder item's save label should be clickable.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-04.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-04.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-04.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-04.js
+@@ -3,32 +3,32 @@
+ 
+ /**
+  * Bug 1122766
+  * Tests that the canvas actor correctly returns from recordAnimationFrame
+  * in the scenario where a loop starts with rAF and has rAF in the beginning
+  * of its loop, when the recording starts before the rAFs start.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(RAF_BEGIN_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(RAF_BEGIN_URL);
+   let { window, EVENTS, gFront, SnapshotsListView } = panel.panelWin;
+   loadFrameScriptUtils();
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   SnapshotsListView._onRecordButtonClick();
+ 
+   // Wait until after the recording started to trigger the content.
+   // Use the gFront method rather than the SNAPSHOT_RECORDING_STARTED event
+   // which triggers before the underlying actor call
+-  yield waitUntil(function* () { return !(yield gFront.isRecording()); });
++  await waitUntil(async function () { return !(await gFront.isRecording()); });
+ 
+   // Start animation in content
+   evalInDebuggee("start();");
+ 
+-  yield recordingFinished;
++  await recordingFinished;
+   ok(true, "Finished recording a snapshot of the animation loop.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-01.js
+@@ -1,26 +1,26 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the frontend UI is properly reconfigured after reloading.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS } = panel.panelWin;
+ 
+   let reset = once(window, EVENTS.UI_RESET);
+   let navigated = reload(target);
+ 
+-  yield reset;
++  await reset;
+   ok(true, "The UI was reset after the refresh button was clicked.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "The target finished reloading.");
+ 
+   is($("#snapshots-pane").hasAttribute("hidden"), false,
+     "The snapshots pane should still be visible.");
+   is($("#debugging-pane").hasAttribute("hidden"), false,
+     "The debugging pane should still be visible.");
+ 
+   is($("#record-snapshot").hasAttribute("checked"), false,
+@@ -45,11 +45,11 @@ function* ifTestingSupported() {
+   is($("#snapshot-filmstrip").getAttribute("hidden"), "true",
+     "The snapshot filmstrip should still be hidden.");
+   is($("#screenshot-container").getAttribute("hidden"), "true",
+     "The screenshot container should still be hidden.");
+ 
+   is($("#debugging-pane-contents").getAttribute("hidden"), "true",
+     "The rest of the UI should still be hidden.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-02.js
+@@ -1,39 +1,39 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the frontend UI is properly reconfigured after reloading.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+   is(SnapshotsListView.itemCount, 0,
+     "There should be no snapshots initially displayed in the UI.");
+   is(CallsListView.itemCount, 0,
+     "There should be no function calls initially displayed in the UI.");
+ 
+   is($("#screenshot-container").hidden, true,
+     "The screenshot should not be initially displayed in the UI.");
+   is($("#snapshot-filmstrip").hidden, true,
+     "There should be no thumbnails initially displayed in the UI (1).");
+   is($all(".filmstrip-thumbnail").length, 0,
+     "There should be no thumbnails initially displayed in the UI (2).");
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED);
+   let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([
++  await promise.all([
+     recordingFinished,
+     callListPopulated,
+     thumbnailsDisplayed,
+     screenshotDisplayed
+   ]);
+ 
+   is(SnapshotsListView.itemCount, 1,
+     "There should be one snapshot displayed in the UI.");
+@@ -45,26 +45,26 @@ function* ifTestingSupported() {
+   is($("#snapshot-filmstrip").hidden, false,
+     "All the thumbnails should now be displayed in the UI (1).");
+   is($all(".filmstrip-thumbnail").length, 4,
+     "All the thumbnails should now be displayed in the UI (2).");
+ 
+   let reset = once(window, EVENTS.UI_RESET);
+   let navigated = reload(target);
+ 
+-  yield reset;
++  await reset;
+   ok(true, "The UI was reset after the refresh button was clicked.");
+ 
+   is(SnapshotsListView.itemCount, 0,
+     "There should be no snapshots displayed in the UI after navigating.");
+   is(CallsListView.itemCount, 0,
+     "There should be no function calls displayed in the UI after navigating.");
+   is($("#snapshot-filmstrip").hidden, true,
+     "There should be no thumbnails displayed in the UI after navigating.");
+   is($("#screenshot-container").hidden, true,
+     "The screenshot should not be displayed in the UI after navigating.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "The target finished reloading.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-01.js
+@@ -1,25 +1,25 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the slider in the calls list view works as advertised.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([recordingFinished, callListPopulated]);
++  await promise.all([recordingFinished, callListPopulated]);
+ 
+   is(CallsListView.selectedIndex, -1,
+     "No item in the function calls list should be initially selected.");
+ 
+   is($("#calls-slider").value, 0,
+     "The slider should be moved all the way to the start.");
+   is($("#calls-slider").min, 0,
+     "The slider minimum value should be 0.");
+@@ -29,11 +29,11 @@ function* ifTestingSupported() {
+   CallsListView.selectedIndex = 1;
+   is($("#calls-slider").value, 1,
+     "The slider should be changed according to the current selection.");
+ 
+   $("#calls-slider").value = 2;
+   is(CallsListView.selectedIndex, 2,
+     "The calls selection should be changed according to the current slider value.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js
+@@ -1,85 +1,85 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the slider in the calls list view works as advertised.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, gFront, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([recordingFinished, callListPopulated, thumbnailsDisplayed]);
++  await promise.all([recordingFinished, callListPopulated, thumbnailsDisplayed]);
+ 
+   let firstSnapshot = SnapshotsListView.getItemAtIndex(0);
+-  let firstSnapshotOverview = yield firstSnapshot.attachment.actor.getOverview();
++  let firstSnapshotOverview = await firstSnapshot.attachment.actor.getOverview();
+ 
+   let thumbnails = firstSnapshotOverview.thumbnails;
+   is(thumbnails.length, 4,
+     "There should be 4 thumbnails cached for the snapshot item.");
+ 
+   let thumbnailImageElementSet = waitForMozSetImageElement(window);
+   $("#calls-slider").value = 1;
+-  let thumbnailPixels = yield thumbnailImageElementSet;
++  let thumbnailPixels = await thumbnailImageElementSet;
+ 
+   ok(sameArray(thumbnailPixels, thumbnails[0].pixels),
+     "The screenshot element should have a thumbnail as an immediate background.");
+ 
+-  yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
++  await once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   ok(true, "The full-sized screenshot was displayed for the item at index 1.");
+ 
+   thumbnailImageElementSet = waitForMozSetImageElement(window);
+   $("#calls-slider").value = 2;
+-  thumbnailPixels = yield thumbnailImageElementSet;
++  thumbnailPixels = await thumbnailImageElementSet;
+ 
+   ok(sameArray(thumbnailPixels, thumbnails[1].pixels),
+     "The screenshot element should have a thumbnail as an immediate background.");
+ 
+-  yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
++  await once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   ok(true, "The full-sized screenshot was displayed for the item at index 2.");
+ 
+   thumbnailImageElementSet = waitForMozSetImageElement(window);
+   $("#calls-slider").value = 7;
+-  thumbnailPixels = yield thumbnailImageElementSet;
++  thumbnailPixels = await thumbnailImageElementSet;
+ 
+   ok(sameArray(thumbnailPixels, thumbnails[3].pixels),
+     "The screenshot element should have a thumbnail as an immediate background.");
+ 
+-  yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
++  await once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   ok(true, "The full-sized screenshot was displayed for the item at index 7.");
+ 
+   thumbnailImageElementSet = waitForMozSetImageElement(window);
+   $("#calls-slider").value = 4;
+-  thumbnailPixels = yield thumbnailImageElementSet;
++  thumbnailPixels = await thumbnailImageElementSet;
+ 
+   ok(sameArray(thumbnailPixels, thumbnails[2].pixels),
+     "The screenshot element should have a thumbnail as an immediate background.");
+ 
+-  yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
++  await once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   ok(true, "The full-sized screenshot was displayed for the item at index 4.");
+ 
+   thumbnailImageElementSet = waitForMozSetImageElement(window);
+   $("#calls-slider").value = 0;
+-  thumbnailPixels = yield thumbnailImageElementSet;
++  thumbnailPixels = await thumbnailImageElementSet;
+ 
+   ok(sameArray(thumbnailPixels, thumbnails[0].pixels),
+     "The screenshot element should have a thumbnail as an immediate background.");
+ 
+-  yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
++  await once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   ok(true, "The full-sized screenshot was displayed for the item at index 0.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ 
+ function waitForMozSetImageElement(panel) {
+   let deferred = defer();
+   panel._onMozSetImageElement = deferred.resolve;
+   return deferred.promise;
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-01.js
+@@ -1,67 +1,67 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if selecting snapshots in the frontend displays the appropriate data
+  * respective to their recorded animation frame.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+-  yield recordAndWaitForFirstSnapshot();
++  await recordAndWaitForFirstSnapshot();
+   info("First snapshot recorded.");
+ 
+   is(SnapshotsListView.selectedIndex, 0,
+     "A snapshot should be automatically selected after first recording.");
+   is(CallsListView.selectedIndex, -1,
+     "There should be no call item automatically selected in the snapshot.");
+ 
+-  yield recordAndWaitForAnotherSnapshot();
++  await recordAndWaitForAnotherSnapshot();
+   info("Second snapshot recorded.");
+ 
+   is(SnapshotsListView.selectedIndex, 0,
+     "A snapshot should not be automatically selected after another recording.");
+   is(CallsListView.selectedIndex, -1,
+     "There should still be no call item automatically selected in the snapshot.");
+ 
+   let secondSnapshotTarget = SnapshotsListView.getItemAtIndex(1).target;
+   let snapshotSelected = waitForSnapshotSelection();
+   EventUtils.sendMouseEvent({ type: "mousedown" }, secondSnapshotTarget, window);
+ 
+-  yield snapshotSelected;
++  await snapshotSelected;
+   info("Second snapshot selected.");
+ 
+   is(SnapshotsListView.selectedIndex, 1,
+     "The second snapshot should now be selected.");
+   is(CallsListView.selectedIndex, -1,
+     "There should still be no call item automatically selected in the snapshot.");
+ 
+   let firstDrawCallContents = $(".call-item-contents", CallsListView.getItemAtIndex(2).target);
+   let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   EventUtils.sendMouseEvent({ type: "mousedown" }, firstDrawCallContents, window);
+ 
+-  yield screenshotDisplayed;
++  await screenshotDisplayed;
+   info("First draw call in the second snapshot selected.");
+ 
+   is(SnapshotsListView.selectedIndex, 1,
+     "The second snapshot should still be selected.");
+   is(CallsListView.selectedIndex, 2,
+     "The first draw call should now be selected in the snapshot.");
+ 
+   let firstSnapshotTarget = SnapshotsListView.getItemAtIndex(0).target;
+   snapshotSelected = waitForSnapshotSelection();
+   EventUtils.sendMouseEvent({ type: "mousedown" }, firstSnapshotTarget, window);
+ 
+-  yield snapshotSelected;
++  await snapshotSelected;
+   info("First snapshot re-selected.");
+ 
+   is(SnapshotsListView.selectedIndex, 0,
+     "The first snapshot should now be re-selected.");
+   is(CallsListView.selectedIndex, -1,
+     "There should still be no call item automatically selected in the snapshot.");
+ 
+   function recordAndWaitForFirstSnapshot() {
+@@ -83,11 +83,11 @@ function* ifTestingSupported() {
+     let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+     return promise.all([
+       callListPopulated,
+       thumbnailsDisplayed,
+       screenshotDisplayed
+     ]);
+   }
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-02.js
+@@ -1,30 +1,30 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if selecting snapshots in the frontend displays the appropriate data
+  * respective to their recorded animation frame.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   SnapshotsListView._onRecordButtonClick();
+   let snapshotTarget = SnapshotsListView.getItemAtIndex(0).target;
+ 
+   EventUtils.sendMouseEvent({ type: "mousedown" }, snapshotTarget, window);
+   EventUtils.sendMouseEvent({ type: "mousedown" }, snapshotTarget, window);
+   EventUtils.sendMouseEvent({ type: "mousedown" }, snapshotTarget, window);
+ 
+   ok(true, "clicking in-progress snapshot does not fail");
+ 
+   let finished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield finished;
++  await finished;
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stepping.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stepping.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stepping.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stepping.js
+@@ -1,25 +1,25 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the stepping buttons in the call list toolbar work as advertised.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  yield promise.all([recordingFinished, callListPopulated]);
++  await promise.all([recordingFinished, callListPopulated]);
+ 
+   checkSteppingButtons(1, 1, 1, 1);
+   is(CallsListView.selectedIndex, -1,
+     "There should be no selected item in the calls list view initially.");
+ 
+   CallsListView._onResume();
+   checkSteppingButtons(1, 1, 1, 1);
+   is(CallsListView.selectedIndex, 0,
+@@ -66,11 +66,11 @@ function* ifTestingSupported() {
+       is($("#step-out").getAttribute("disabled"), "true",
+         "The stepOut button doesn't have the expected disabled state.");
+     } else {
+       is($("#step-out").hasAttribute("disabled"), false,
+         "The stepOut button doesn't have the expected enabled state.");
+     }
+   }
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-01.js
+@@ -1,36 +1,36 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that you can stop a recording that does not have a rAF cycle.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(NO_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(NO_CANVAS_URL);
+   let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingStarted = once(window, EVENTS.SNAPSHOT_RECORDING_STARTED);
+   SnapshotsListView._onRecordButtonClick();
+ 
+-  yield recordingStarted;
++  await recordingStarted;
+ 
+   is($("#empty-notice").hidden, true, "Empty notice not shown");
+   is($("#waiting-notice").hidden, false, "Waiting notice shown");
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let recordingCancelled = once(window, EVENTS.SNAPSHOT_RECORDING_CANCELLED);
+   SnapshotsListView._onRecordButtonClick();
+ 
+-  yield promise.all([recordingFinished, recordingCancelled]);
++  await promise.all([recordingFinished, recordingCancelled]);
+ 
+   ok(true, "Recording stopped and was considered failed.");
+ 
+   is(SnapshotsListView.itemCount, 0, "No snapshots in the list.");
+   is($("#empty-notice").hidden, false, "Empty notice shown");
+   is($("#waiting-notice").hidden, true, "Waiting notice not shown");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-02.js
+@@ -1,35 +1,35 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that a recording that does not have a rAF cycle fails after timeout.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(NO_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(NO_CANVAS_URL);
+   let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingStarted = once(window, EVENTS.SNAPSHOT_RECORDING_STARTED);
+   SnapshotsListView._onRecordButtonClick();
+ 
+-  yield recordingStarted;
++  await recordingStarted;
+ 
+   is($("#empty-notice").hidden, true, "Empty notice not shown");
+   is($("#waiting-notice").hidden, false, "Waiting notice shown");
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let recordingCancelled = once(window, EVENTS.SNAPSHOT_RECORDING_CANCELLED);
+ 
+-  yield promise.all([recordingFinished, recordingCancelled]);
++  await promise.all([recordingFinished, recordingCancelled]);
+ 
+   ok(true, "Recording stopped and was considered failed.");
+ 
+   is(SnapshotsListView.itemCount, 0, "No snapshots in the list.");
+   is($("#empty-notice").hidden, false, "Empty notice shown");
+   is($("#waiting-notice").hidden, true, "Waiting notice not shown");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-03.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-03.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-03.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-03.js
+@@ -1,36 +1,36 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that a recording that has a rAF cycle, but no draw calls, fails
+  * after timeout.
+  */
+ 
+-function* ifTestingSupported() {
+-  let { target, panel } = yield initCanvasDebuggerFrontend(RAF_NO_CANVAS_URL);
++async function ifTestingSupported() {
++  let { target, panel } = await initCanvasDebuggerFrontend(RAF_NO_CANVAS_URL);
+   let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;
+ 
+-  yield reload(target);
++  await reload(target);
+ 
+   let recordingStarted = once(window, EVENTS.SNAPSHOT_RECORDING_STARTED);
+   SnapshotsListView._onRecordButtonClick();
+ 
+-  yield recordingStarted;
++  await recordingStarted;
+ 
+   is($("#empty-notice").hidden, true, "Empty notice not shown");
+   is($("#waiting-notice").hidden, false, "Waiting notice shown");
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let recordingCancelled = once(window, EVENTS.SNAPSHOT_RECORDING_CANCELLED);
+ 
+-  yield promise.all([recordingFinished, recordingCancelled]);
++  await promise.all([recordingFinished, recordingCancelled]);
+ 
+   ok(true, "Recording stopped and was considered failed.");
+ 
+   is(SnapshotsListView.itemCount, 0, "No snapshots in the list.");
+   is($("#empty-notice").hidden, false, "Empty notice shown");
+   is($("#waiting-notice").hidden, true, "Waiting notice not shown");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_profiling-canvas.js b/devtools/client/canvasdebugger/test/browser_profiling-canvas.js
+--- a/devtools/client/canvasdebugger/test/browser_profiling-canvas.js
++++ b/devtools/client/canvasdebugger/test/browser_profiling-canvas.js
+@@ -1,33 +1,33 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if functions inside a single animation frame are recorded and stored
+  * for a canvas context profiling.
+  */
+ 
+-function* ifTestingSupported() {
++async function ifTestingSupported() {
+   let currentTime = window.performance.now();
+-  let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
++  let { target, front } = await initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  let snapshotActor = yield front.recordAnimationFrame();
++  let snapshotActor = await front.recordAnimationFrame();
+   ok(snapshotActor,
+     "A snapshot actor was sent after recording.");
+ 
+-  let animationOverview = yield snapshotActor.getOverview();
++  let animationOverview = await snapshotActor.getOverview();
+   ok(animationOverview,
+     "An animation overview could be retrieved after recording.");
+ 
+   let functionCalls = animationOverview.calls;
+   ok(functionCalls,
+     "An array of function call actors was sent after recording.");
+   is(functionCalls.length, 8,
+     "The number of function call actors is correct.");
+@@ -35,11 +35,11 @@ function* ifTestingSupported() {
+   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);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/canvasdebugger/test/browser_profiling-webgl.js b/devtools/client/canvasdebugger/test/browser_profiling-webgl.js
+--- a/devtools/client/canvasdebugger/test/browser_profiling-webgl.js
++++ b/devtools/client/canvasdebugger/test/browser_profiling-webgl.js
+@@ -1,83 +1,83 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if functions inside a single animation frame are recorded and stored
+  * for a canvas context profiling.
+  */
+ 
+-function* ifTestingSupported() {
++async function ifTestingSupported() {
+   let currentTime = window.performance.now();
+   info("Start to estimate WebGL drawArrays function.");
+-  var { target, front } = yield initCanvasDebuggerBackend(WEBGL_DRAW_ARRAYS);
++  var { target, front } = await initCanvasDebuggerBackend(WEBGL_DRAW_ARRAYS);
+ 
+   let navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  let snapshotActor = yield front.recordAnimationFrame();
++  let snapshotActor = await front.recordAnimationFrame();
+   ok(snapshotActor,
+     "A snapshot actor was sent after recording.");
+ 
+-  let animationOverview = yield snapshotActor.getOverview();
++  let animationOverview = await snapshotActor.getOverview();
+   ok(animationOverview,
+     "An animation overview could be retrieved after recording.");
+ 
+   let functionCalls = animationOverview.calls;
+   ok(functionCalls,
+     "An array of function call actors was sent after recording.");
+ 
+   testFunctionCallTimestamp(functionCalls, currentTime);
+ 
+   info("Check triangle and vertex counts in drawArrays()");
+   is(animationOverview.primitive.tris, 5, "The count of triangles is correct.");
+   is(animationOverview.primitive.vertices, 26, "The count of vertices is correct.");
+   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);
++  await removeTab(target.tab);
+ 
+   info("Start to estimate WebGL drawElements function.");
+-  var { target, front } = yield initCanvasDebuggerBackend(WEBGL_DRAW_ELEMENTS);
++  var { target, front } = await initCanvasDebuggerBackend(WEBGL_DRAW_ELEMENTS);
+ 
+   navigated = once(target, "navigate");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  snapshotActor = yield front.recordAnimationFrame();
++  snapshotActor = await front.recordAnimationFrame();
+   ok(snapshotActor,
+     "A snapshot actor was sent after recording.");
+   
+-  animationOverview = yield snapshotActor.getOverview();
++  animationOverview = await snapshotActor.getOverview();
+   ok(animationOverview,
+     "An animation overview could be retrieved after recording.");
+   
+   functionCalls = animationOverview.calls;
+   ok(functionCalls,
+     "An array of function call actors was sent after recording.");
+   
+   testFunctionCallTimestamp(functionCalls, currentTime);
+   
+   info("Check triangle and vertex counts in drawElements()");
+   is(animationOverview.primitive.tris, 5, "The count of triangles is correct.");
+   is(animationOverview.primitive.vertices, 26, "The count of vertices is correct.");
+   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);
++  await 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." );
+diff --git a/devtools/client/canvasdebugger/test/head.js b/devtools/client/canvasdebugger/test/head.js
+--- a/devtools/client/canvasdebugger/test/head.js
++++ b/devtools/client/canvasdebugger/test/head.js
+@@ -63,19 +63,23 @@ function ifTestingSupported() {
+   finish();
+ }
+ 
+ function ifTestingUnsupported() {
+   todo(false, "Skipping test because some required functionality isn't supported.");
+   finish();
+ }
+ 
+-function test() {
++async function test() {
+   let generator = isTestingSupported() ? ifTestingSupported : ifTestingUnsupported;
+-  Task.spawn(generator).catch(handleError);
++  try {
++    await generator();
++  } catch(e) {
++    handleError(e);
++  }
+ }
+ 
+ function createCanvas() {
+   return document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ }
+ 
+ function isTestingSupported() {
+   if (!gRequiresWebGL) {
+@@ -109,56 +113,56 @@ function initServer() {
+   DebuggerServer.init();
+   DebuggerServer.registerAllActors();
+ }
+ 
+ function initCallWatcherBackend(aUrl) {
+   info("Initializing a call watcher front.");
+   initServer();
+ 
+-  return Task.spawn(function* () {
+-    let tab = yield addTab(aUrl);
++  return (async function () {
++    let tab = await addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+-    yield target.makeRemote();
++    await target.makeRemote();
+ 
+     let front = new CallWatcherFront(target.client, target.form);
+     return { target, front };
+-  });
++  })();
+ }
+ 
+ function initCanvasDebuggerBackend(aUrl) {
+   info("Initializing a canvas debugger front.");
+   initServer();
+ 
+-  return Task.spawn(function* () {
+-    let tab = yield addTab(aUrl);
++  return (async function () {
++    let tab = await addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+-    yield target.makeRemote();
++    await target.makeRemote();
+ 
+     let front = new CanvasFront(target.client, target.form);
+     return { target, front };
+-  });
++  })();
+ }
+ 
+ function initCanvasDebuggerFrontend(aUrl) {
+   info("Initializing a canvas debugger pane.");
+ 
+-  return Task.spawn(function* () {
+-    let tab = yield addTab(aUrl);
++  return (async function () {
++    let tab = await addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+-    yield target.makeRemote();
++    await target.makeRemote();
+ 
+     Services.prefs.setBoolPref("devtools.canvasdebugger.enabled", true);
+-    let toolbox = yield gDevTools.showToolbox(target, "canvasdebugger");
++    let toolbox = await gDevTools.showToolbox(target, "canvasdebugger");
+     let panel = toolbox.getCurrentPanel();
+     return { target, panel };
+-  });
++  })();
+ }
+ 
+ function teardown({target}) {
+   info("Destroying the specified canvas debugger.");
+ 
+   let {tab} = target;
+   return gDevTools.closeToolbox(target).then(() => {
+     removeTab(tab);

+ 30 - 0
frg/work-js/mozilla-release/patches/1440321-1c-commandline-61a1.patch

@@ -0,0 +1,30 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  54d5f359c067f72680564c0692ab086cbabfeef9
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1c. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/commandline/test/head.js b/devtools/client/commandline/test/head.js
+--- a/devtools/client/commandline/test/head.js
++++ b/devtools/client/commandline/test/head.js
+@@ -8,16 +8,17 @@
+ "use strict";
+ 
+ const TEST_BASE_HTTP = "http://example.com/browser/devtools/client/commandline/test/";
+ const TEST_BASE_HTTPS = "https://example.com/browser/devtools/client/commandline/test/";
+ 
+ var { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ var { console } = require("resource://gre/modules/Console.jsm");
+ var flags = require("devtools/shared/flags");
++var { Task } = require("devtools/shared/task");
+ 
+ // Import the GCLI test helper
+ var testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+ Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
+ Services.scriptloader.loadSubScript(testDir + "/mockCommands.js", this, "UTF-8");
+ 
+ flags.testing = true;
+ SimpleTest.registerCleanupFunction(() => {

+ 51 - 0
frg/work-js/mozilla-release/patches/1440321-1d-debugger-61a1.patch

@@ -0,0 +1,51 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  38aaef88f8e55b4d19f7e9a38b66637ccd093037
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1d. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/debugger/new/test/mochitest/head.js b/devtools/client/debugger/new/test/mochitest/head.js
+--- a/devtools/client/debugger/new/test/mochitest/head.js
++++ b/devtools/client/debugger/new/test/mochitest/head.js
+@@ -33,16 +33,17 @@
+  */
+ 
+ // shared-head.js handles imports, constants, and utility functions
+ Services.scriptloader.loadSubScript(
+   "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
+   this
+ );
+ var { Toolbox } = require("devtools/client/framework/toolbox");
++var { Task } = require("devtools/shared/task");
+ const sourceUtils = {
+   isLoaded: source => source.get("loadedState") === "loaded"
+ };
+ 
+ const EXAMPLE_URL =
+   "http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/";
+ 
+ Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
+diff --git a/devtools/client/debugger/test/mochitest/head.js b/devtools/client/debugger/test/mochitest/head.js
+--- a/devtools/client/debugger/test/mochitest/head.js
++++ b/devtools/client/debugger/test/mochitest/head.js
+@@ -16,16 +16,17 @@ Services.prefs.setBoolPref("devtools.deb
+ 
+ var { BrowserToolboxProcess } = ChromeUtils.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});
+ var { DebuggerServer } = require("devtools/server/main");
+ var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ var ObjectClient = require("devtools/shared/client/object-client");
+ var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
+ var EventEmitter = require("devtools/shared/old-event-emitter");
+ var { Toolbox } = require("devtools/client/framework/toolbox");
++var { Task } = require("devtools/shared/task");
+ 
+ const chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
+ 
+ // Override promise with deprecated-sync-thenables
+ promise = require("devtools/shared/deprecated-sync-thenables");
+ 
+ const EXAMPLE_URL = "http://example.com/browser/devtools/client/debugger/test/mochitest/";
+ const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "code_frame-script.js";

+ 216 - 0
frg/work-js/mozilla-release/patches/1440321-1e-dom-61a1.patch

@@ -0,0 +1,216 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  0656b5bddd3fd71eddf226b1b681dccc27193a36
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1e. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/dom/dom-panel.js b/devtools/client/dom/dom-panel.js
+--- a/devtools/client/dom/dom-panel.js
++++ b/devtools/client/dom/dom-panel.js
+@@ -5,17 +5,16 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ const { Cu } = require("chrome");
+ const ObjectClient = require("devtools/shared/client/object-client");
+ 
+ const defer = require("devtools/shared/defer");
+ const EventEmitter = require("devtools/shared/old-event-emitter");
+-const { Task } = require("devtools/shared/task");
+ 
+ /**
+  * This object represents DOM panel. It's responsibility is to
+  * render Document Object Model of the current debugger target.
+  */
+ function DomPanel(iframeWindow, toolbox) {
+   this.panelWin = iframeWindow;
+   this._toolbox = toolbox;
+@@ -31,37 +30,37 @@ function DomPanel(iframeWindow, toolbox)
+ 
+ DomPanel.prototype = {
+   /**
+    * Open is effectively an asynchronous constructor.
+    *
+    * @return object
+    *         A promise that is resolved when the DOM panel completes opening.
+    */
+-  open: Task.async(function* () {
++  async open() {
+     if (this._opening) {
+       return this._opening;
+     }
+ 
+     let deferred = defer();
+     this._opening = deferred.promise;
+ 
+     // Local monitoring needs to make the target remote.
+     if (!this.target.isRemote) {
+-      yield this.target.makeRemote();
++      await this.target.makeRemote();
+     }
+ 
+     this.initialize();
+ 
+     this.isReady = true;
+     this.emit("ready");
+     deferred.resolve(this);
+ 
+     return this._opening;
+-  }),
++  },
+ 
+   // Initialization
+ 
+   initialize: function() {
+     this.panelWin.addEventListener("devtools/content/message",
+       this.onContentMessage, true);
+ 
+     this.target.on("navigate", this.onTabNavigated);
+@@ -73,32 +72,32 @@ DomPanel.prototype = {
+       openLink: this.openLink.bind(this),
+     };
+ 
+     exportIntoContentScope(this.panelWin, provider, "DomProvider");
+ 
+     this.shouldRefresh = true;
+   },
+ 
+-  destroy: Task.async(function* () {
++  async destroy() {
+     if (this._destroying) {
+       return this._destroying;
+     }
+ 
+     let deferred = defer();
+     this._destroying = deferred.promise;
+ 
+     this.target.off("navigate", this.onTabNavigated);
+     this._toolbox.off("select", this.onPanelVisibilityChange);
+ 
+     this.emit("destroyed");
+ 
+     deferred.resolve();
+     return this._destroying;
+-  }),
++  },
+ 
+   // Events
+ 
+   refresh: function() {
+     // Do not refresh if the panel isn't visible.
+     if (!this.isPanelVisible()) {
+       return;
+     }
+diff --git a/devtools/client/dom/test/browser_dom_array.js b/devtools/client/dom/test/browser_dom_array.js
+--- a/devtools/client/dom/test/browser_dom_array.js
++++ b/devtools/client/dom/test/browser_dom_array.js
+@@ -9,23 +9,23 @@ const TEST_PAGE_URL = URL_ROOT + "page_a
+ const TEST_ARRAY = [
+   "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
+   "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
+ ];
+ 
+ /**
+  * Basic test that checks content of the DOM panel.
+  */
+-add_task(function* () {
++add_task(async function() {
+   info("Test DOM Panel Array Expansion started");
+ 
+-  let { panel } = yield addTestTab(TEST_PAGE_URL);
++  let { panel } = await addTestTab(TEST_PAGE_URL);
+ 
+   // Expand specified row and wait till children are displayed.
+-  yield expandRow(panel, "_a");
++  await expandRow(panel, "_a");
+ 
+   // Verify that children is displayed now.
+   let childRows = getAllRowsForLabel(panel, "_a");
+ 
+   let item = childRows.pop();
+   is(item.name, "length", "length property is correct");
+   is(item.value, 26, "length property value is 26");
+ 
+diff --git a/devtools/client/dom/test/browser_dom_basic.js b/devtools/client/dom/test/browser_dom_basic.js
+--- a/devtools/client/dom/test/browser_dom_basic.js
++++ b/devtools/client/dom/test/browser_dom_basic.js
+@@ -5,20 +5,20 @@
+ 
+ "use strict";
+ 
+ const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
+ 
+ /**
+  * Basic test that checks content of the DOM panel.
+  */
+-add_task(function* () {
++add_task(async function() {
+   info("Test DOM panel basic started");
+ 
+-  let { panel } = yield addTestTab(TEST_PAGE_URL);
++  let { panel } = await addTestTab(TEST_PAGE_URL);
+ 
+   // Expand specified row and wait till children are displayed.
+-  yield expandRow(panel, "_a");
++  await expandRow(panel, "_a");
+ 
+   // Verify that child is displayed now.
+   let childRow = getRowByLabel(panel, "_data");
+   ok(childRow, "Child row must exist");
+ });
+diff --git a/devtools/client/dom/test/browser_dom_refresh.js b/devtools/client/dom/test/browser_dom_refresh.js
+--- a/devtools/client/dom/test/browser_dom_refresh.js
++++ b/devtools/client/dom/test/browser_dom_refresh.js
+@@ -5,21 +5,21 @@
+ 
+ "use strict";
+ 
+ const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
+ 
+ /**
+  * Basic test that checks the Refresh action in DOM panel.
+  */
+-add_task(function* () {
++add_task(async function() {
+   info("Test DOM panel basic started");
+ 
+-  let { panel } = yield addTestTab(TEST_PAGE_URL);
++  let { panel } = await addTestTab(TEST_PAGE_URL);
+ 
+   // Create a new variable in the page scope and refresh the panel.
+-  yield evaluateJSAsync(panel, "var _b = 10");
+-  yield refreshPanel(panel);
++  await evaluateJSAsync(panel, "var _b = 10");
++  await refreshPanel(panel);
+ 
+   // Verify that the variable is displayed now.
+   let row = getRowByLabel(panel, "_b");
+   ok(row, "New variable must be displayed");
+ });
+diff --git a/devtools/client/dom/test/head.js b/devtools/client/dom/test/head.js
+--- a/devtools/client/dom/test/head.js
++++ b/devtools/client/dom/test/head.js
+@@ -220,17 +220,17 @@ function _afterDispatchDone(store, type)
+   });
+ }
+ 
+ function waitForDispatch(panel, type, eventRepeat = 1) {
+   const store = panel.panelWin.view.mainFrame.store;
+   const actionType = constants[type];
+   let count = 0;
+ 
+-  return Task.spawn(function* () {
++  return (async function() {
+     info("Waiting for " + type + " to dispatch " + eventRepeat + " time(s)");
+     while (count < eventRepeat) {
+-      yield _afterDispatchDone(store, actionType);
++      await _afterDispatchDone(store, actionType);
+       count++;
+       info(type + " dispatched " + count + " time(s)");
+     }
+-  });
++  })();
+ }

+ 5197 - 0
frg/work-js/mozilla-release/patches/1440321-1f-framework-61a1.patch

@@ -0,0 +1,5197 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  5690c2d0fa1edc99d677f6b680d3bf9133a10f20
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1f. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/framework/connect/connect.js b/devtools/client/framework/connect/connect.js
+--- a/devtools/client/framework/connect/connect.js
++++ b/devtools/client/framework/connect/connect.js
+@@ -7,17 +7,16 @@
+ "use strict";
+ 
+ var {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ var Services = require("Services");
+ var {gDevTools} = require("devtools/client/framework/devtools");
+ var {TargetFactory} = require("devtools/client/framework/target");
+ var {Toolbox} = require("devtools/client/framework/toolbox");
+ var {DebuggerClient} = require("devtools/shared/client/debugger-client");
+-var {Task} = require("devtools/shared/task");
+ var {LocalizationHelper} = require("devtools/shared/l10n");
+ var L10N = new LocalizationHelper("devtools/client/locales/connection-screen.properties");
+ 
+ var gClient;
+ var gConnectionTimeout;
+ 
+ /**
+  * Once DOM is ready, we prefil the host/port inputs with
+@@ -43,49 +42,49 @@ window.addEventListener("DOMContentLoade
+       showError("unexpected");
+     });
+   });
+ }, {capture: true, once: true});
+ 
+ /**
+  * Called when the "connect" button is clicked.
+  */
+-var submit = Task.async(function* () {
++var submit = async function () {
+   // Show the "connecting" screen
+   document.body.classList.add("connecting");
+ 
+   let host = document.getElementById("host").value;
+   let port = document.getElementById("port").value;
+ 
+   // Save the host/port values
+   try {
+     Services.prefs.setCharPref("devtools.debugger.remote-host", host);
+     Services.prefs.setIntPref("devtools.debugger.remote-port", port);
+   } catch (e) {
+     // Fails in e10s mode, but not a critical feature.
+   }
+ 
+   // Initiate the connection
+-  let transport = yield DebuggerClient.socketConnect({ host, port });
++  let transport = await DebuggerClient.socketConnect({ host, port });
+   gClient = new DebuggerClient(transport);
+   let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout");
+   gConnectionTimeout = setTimeout(handleConnectionTimeout, delay);
+-  let response = yield gClient.connect();
+-  yield onConnectionReady(...response);
+-});
++  let response = await gClient.connect();
++  await onConnectionReady(...response);
++};
+ 
+ /**
+  * Connection is ready. List actors and build buttons.
+  */
+-var onConnectionReady = Task.async(function* ([aType, aTraits]) {
++var onConnectionReady = async function ([aType, aTraits]) {
+   clearTimeout(gConnectionTimeout);
+ 
+   let addons = [];
+   try {
+-    let response = yield gClient.listAddons();
++    let response = await gClient.listAddons();
+     if (!response.error && response.addons.length > 0) {
+       addons = response.addons;
+     }
+   } catch(e) {
+     // listAddons throws if the runtime doesn't support addons
+   }
+ 
+   let parent = document.getElementById("addonActors");
+@@ -99,17 +98,17 @@ var onConnectionReady = Task.async(funct
+     }
+   }
+   else {
+     // Hide the section when there are no add-ons
+     parent.previousElementSibling.remove();
+     parent.remove();
+   }
+ 
+-  let response = yield gClient.listTabs();
++  let response = await gClient.listTabs();
+ 
+   parent = document.getElementById("tabActors");
+ 
+   // Add Global Process debugging...
+   let globals = Cu.cloneInto(response, {});
+   delete globals.tabs;
+   delete globals.selected;
+   // ...only if there are appropriate actors (a 'from' property will always
+@@ -151,17 +150,17 @@ var onConnectionReady = Task.async(funct
+   document.body.classList.remove("connecting");
+   document.body.classList.add("actors-mode");
+ 
+   // Ensure the first link is focused
+   let firstLink = parent.querySelector("a:first-of-type");
+   if (firstLink) {
+     firstLink.focus();
+   }
+-});
++};
+ 
+ /**
+  * Build one button for an add-on actor.
+  */
+ function buildAddonLink(addon, parent) {
+   let a = document.createElement("a");
+   a.onclick = async function () {
+     const isTabActor = addon.isWebExtension;
+diff --git a/devtools/client/framework/devtools.js b/devtools/client/framework/devtools.js
+--- a/devtools/client/framework/devtools.js
++++ b/devtools/client/framework/devtools.js
+@@ -24,17 +24,16 @@ loader.lazyRequireGetter(this, "Debugger
+ loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
+ 
+ loader.lazyRequireGetter(this, "WebExtensionInspectedWindowFront",
+       "devtools/shared/fronts/webextension-inspected-window", true);
+ 
+ const {defaultTools: DefaultTools, defaultThemes: DefaultThemes} =
+   require("devtools/client/definitions");
+ const EventEmitter = require("devtools/shared/old-event-emitter");
+-const {Task} = require("devtools/shared/task");
+ const {getTheme, setTheme, addThemeObserver, removeThemeObserver} =
+   require("devtools/client/shared/theme");
+ 
+ const FORBIDDEN_IDS = new Set(["toolbox", ""]);
+ const MAX_ORDINAL = 99;
+ 
+ /**
+  * DevTools is a class that represents a set of developer tools, it holds a
+@@ -442,67 +441,67 @@ DevTools.prototype = {
+    * @param {Toolbox.HostType} hostType
+    *        The type of host (bottom, window, side)
+    * @param {object} hostOptions
+    *        Options for host specifically
+    *
+    * @return {Toolbox} toolbox
+    *        The toolbox that was opened
+    */
+-  showToolbox: Task.async(function* (target, toolId, hostType, hostOptions) {
++  async showToolbox(target, toolId, hostType, hostOptions) {
+     let toolbox = this._toolboxes.get(target);
+     if (toolbox) {
+       if (hostType != null && toolbox.hostType != hostType) {
+-        yield toolbox.switchHost(hostType);
++        await toolbox.switchHost(hostType);
+       }
+ 
+       if (toolId != null && toolbox.currentToolId != toolId) {
+-        yield toolbox.selectTool(toolId);
++        await toolbox.selectTool(toolId);
+       }
+ 
+       toolbox.raise();
+     } else {
+       // As toolbox object creation is async, we have to be careful about races
+       // Check for possible already in process of loading toolboxes before
+       // actually trying to create a new one.
+       let promise = this._creatingToolboxes.get(target);
+       if (promise) {
+-        return yield promise;
++        return await promise;
+       }
+       let toolboxPromise = this.createToolbox(target, toolId, hostType, hostOptions);
+       this._creatingToolboxes.set(target, toolboxPromise);
+-      toolbox = yield toolboxPromise;
++      toolbox = await toolboxPromise;
+       this._creatingToolboxes.delete(target);
+     }
+     return toolbox;
+-  }),
++  },
+ 
+-  createToolbox: Task.async(function* (target, toolId, hostType, hostOptions) {
++  async createToolbox(target, toolId, hostType, hostOptions) {
+     let manager = new ToolboxHostManager(target, hostType, hostOptions);
+ 
+-    let toolbox = yield manager.create(toolId);
++    let toolbox = await manager.create(toolId);
+ 
+     this._toolboxes.set(target, toolbox);
+ 
+     this.emit("toolbox-created", toolbox);
+ 
+     toolbox.once("destroy", () => {
+       this.emit("toolbox-destroy", target);
+     });
+ 
+     toolbox.once("destroyed", () => {
+       this._toolboxes.delete(target);
+       this.emit("toolbox-destroyed", target);
+     });
+ 
+-    yield toolbox.open();
++    await toolbox.open();
+     this.emit("toolbox-ready", toolbox);
+ 
+     return toolbox;
+-  }),
++  },
+ 
+   /**
+    * Return the toolbox for a given target.
+    *
+    * @param  {object} target
+    *         Target value e.g. the target that owns this toolbox
+    *
+    * @return {Toolbox} toolbox
+@@ -515,27 +514,27 @@ DevTools.prototype = {
+   /**
+    * Close the toolbox for a given target
+    *
+    * @return promise
+    *         This promise will resolve to false if no toolbox was found
+    *         associated to the target. true, if the toolbox was successfully
+    *         closed.
+    */
+-  closeToolbox: Task.async(function* (target) {
+-    let toolbox = yield this._creatingToolboxes.get(target);
++  async closeToolbox(target) {
++    let toolbox = await this._creatingToolboxes.get(target);
+     if (!toolbox) {
+       toolbox = this._toolboxes.get(target);
+     }
+     if (!toolbox) {
+       return false;
+     }
+-    yield toolbox.destroy();
++    await toolbox.destroy();
+     return true;
+-  }),
++  },
+ 
+   /**
+    * Wrapper on TargetFactory.forTab, constructs a Target for the provided tab.
+    *
+    * @param  {XULTab} tab
+    *         The tab to use in creating a new target.
+    *
+    * @return {TabTarget} A target object
+diff --git a/devtools/client/framework/sidebar.js b/devtools/client/framework/sidebar.js
+--- a/devtools/client/framework/sidebar.js
++++ b/devtools/client/framework/sidebar.js
+@@ -1,16 +1,15 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ var Services = require("Services");
+-var {Task} = require("devtools/shared/task");
+ var EventEmitter = require("devtools/shared/event-emitter");
+ var Telemetry = require("devtools/client/shared/telemetry");
+ 
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+ 
+ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ 
+@@ -330,39 +329,39 @@ ToolSidebar.prototype = {
+ 
+   /**
+    * Remove an existing tab.
+    * @param {String} tabId The ID of the tab that was used to register it, or
+    * the tab id attribute value if the tab existed before the sidebar got created.
+    * @param {String} tabPanelId Optional. If provided, this ID will be used
+    * instead of the tabId to retrieve and remove the corresponding <tabpanel>
+    */
+-  removeTab: Task.async(function* (tabId, tabPanelId) {
++  async removeTab(tabId, tabPanelId) {
+     // Remove the tab if it can be found
+     let tab = this.getTab(tabId);
+     if (!tab) {
+       return;
+     }
+ 
+     let win = this.getWindowForTab(tabId);
+     if (win && ("destroy" in win)) {
+-      yield win.destroy();
++      await win.destroy();
+     }
+ 
+     tab.remove();
+ 
+     // Also remove the tabpanel
+     let panel = this.getTabPanel(tabPanelId || tabId);
+     if (panel) {
+       panel.remove();
+     }
+ 
+     this._tabs.delete(tabId);
+     this.emit("tab-unregistered", tabId);
+-  }),
++  },
+ 
+   /**
+    * Show or hide a specific tab.
+    * @param {Boolean} isVisible True to show the tab/tabpanel, False to hide it.
+    * @param {String} id The ID of the tab to be hidden.
+    */
+   toggleTab: function (isVisible, id) {
+     // Toggle the tab.
+@@ -544,17 +543,17 @@ ToolSidebar.prototype = {
+       return;
+     }
+     return panel.firstChild.contentWindow;
+   },
+ 
+   /**
+    * Clean-up.
+    */
+-  destroy: Task.async(function* () {
++  async destroy() {
+     if (this._destroyed) {
+       return;
+     }
+     this._destroyed = true;
+ 
+     Services.prefs.setIntPref("devtools.toolsidebar-width." + this._uid, this._tabbox.width);
+ 
+     if (this._allTabsBtn) {
+@@ -565,17 +564,17 @@ ToolSidebar.prototype = {
+ 
+     // Note that we check for the existence of this._tabbox.tabpanels at each
+     // step as the container window may have been closed by the time one of the
+     // panel's destroy promise resolves.
+     while (this._tabbox.tabpanels && this._tabbox.tabpanels.hasChildNodes()) {
+       let panel = this._tabbox.tabpanels.firstChild;
+       let win = panel.firstChild.contentWindow;
+       if (win && ("destroy" in win)) {
+-        yield win.destroy();
++        await win.destroy();
+       }
+       panel.remove();
+     }
+ 
+     while (this._tabbox.tabs && this._tabbox.tabs.hasChildNodes()) {
+       this._tabbox.tabs.firstChild.remove();
+     }
+ 
+@@ -584,10 +583,10 @@ ToolSidebar.prototype = {
+     }
+ 
+     this._toolPanel.emit("sidebar-destroyed", this);
+ 
+     this._tabs = null;
+     this._tabbox = null;
+     this._panelDoc = null;
+     this._toolPanel = null;
+-  })
++  }
+ };
+diff --git a/devtools/client/framework/test/browser_browser_toolbox.js b/devtools/client/framework/test/browser_browser_toolbox.js
+--- a/devtools/client/framework/test/browser_browser_toolbox.js
++++ b/devtools/client/framework/test/browser_browser_toolbox.js
+@@ -5,17 +5,17 @@
+ // There are shutdown issues for which multiple rejections are left uncaught.
+ // See bug 1018184 for resolving these issues.
+ const { PromiseTestUtils } = scopedCuImport("resource://testing-common/PromiseTestUtils.jsm");
+ PromiseTestUtils.whitelistRejectionsGlobally(/File closed/);
+ 
+ // On debug test slave, it takes about 50s to run the test.
+ requestLongerTimeout(4);
+ 
+-add_task(async function() {
++add_task(async function () {
+   await new Promise(done => {
+     let options = {"set": [
+       ["devtools.debugger.prompt-connection", false],
+       ["devtools.debugger.remote-enabled", true],
+       ["devtools.chrome.enabled", true],
+       // Test-only pref to allow passing `testScript` argument to the browser
+       // toolbox
+       ["devtools.browser-toolbox.allow-unsafe-script", true],
+diff --git a/devtools/client/framework/test/browser_browser_toolbox_debugger.js b/devtools/client/framework/test/browser_browser_toolbox_debugger.js
+--- a/devtools/client/framework/test/browser_browser_toolbox_debugger.js
++++ b/devtools/client/framework/test/browser_browser_toolbox_debugger.js
+@@ -15,18 +15,18 @@ PromiseTestUtils.whitelistRejectionsGlob
+ // On debug test runner, it takes about 50s to run the test.
+ requestLongerTimeout(4);
+ 
+ const { fetch } = require("devtools/shared/DevToolsUtils");
+ 
+ const debuggerHeadURL = CHROME_URL_ROOT + "../../debugger/new/test/mochitest/head.js";
+ const testScriptURL = CHROME_URL_ROOT + "test_browser_toolbox_debugger.js";
+ 
+-add_task(function* runTest() {
+-  yield new Promise(done => {
++add_task(async function runTest() {
++  await new Promise(done => {
+     let options = {"set": [
+       ["devtools.debugger.prompt-connection", false],
+       ["devtools.debugger.remote-enabled", true],
+       ["devtools.chrome.enabled", true],
+       // Test-only pref to allow passing `testScript` argument to the browser
+       // toolbox
+       ["devtools.browser-toolbox.allow-unsafe-script", true],
+       // On debug test runner, it takes more than the default time (20s)
+@@ -91,17 +91,16 @@ add_task(function* runTest() {
+       } else {
+         msg = "SUCCESS: " + msg;
+         dump(msg + "\n");
+       }
+     };
+ 
+     const registerCleanupFunction = () => {};
+ 
+-    const { Task } = ChromeUtils.import("resource://gre/modules/Task.jsm", {});
+     const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+     const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
+ 
+     // Copied from shared-head.js:
+     // test_browser_toolbox_debugger.js uses waitForPaused, which relies on waitUntil
+     // which is normally provided by shared-head.js
+     const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm", {});
+     function waitUntil(predicate, interval = 10) {
+@@ -114,44 +113,44 @@ add_task(function* runTest() {
+         }, interval);
+       });
+     }
+   }).toSource().replace(/^\(function \(\) \{|\}\)$/g, "");
+   // Stringify testHead's function and remove `(function {` prefix and `})` suffix
+   // to ensure inner symbols gets exposed to next pieces of code
+ 
+   // Then inject new debugger head file
+-  let { content } = yield fetch(debuggerHeadURL);
++  let { content } = await fetch(debuggerHeadURL);
+   let debuggerHead = content;
+   // We remove its import of shared-head, which isn't available in browser toolbox process
+   // And isn't needed thanks to testHead's symbols
+   debuggerHead = debuggerHead.replace(/Services.scriptloader.loadSubScript[^\)]*\);/, "");
+ 
+   // Finally, fetch the debugger test script that is going to be execute in the browser
+   // toolbox process
+-  let testScript = (yield fetch(testScriptURL)).content;
++  let testScript = (await fetch(testScriptURL)).content;
+   let source =
+     "try { let testUrl = \""+testUrl+"\";" + testHead + debuggerHead + testScript + "} catch (e) {" +
+     "  dump('Exception: '+ e + ' at ' + e.fileName + ':' + " +
+     "       e.lineNumber + '\\nStack: ' + e.stack + '\\n');" +
+     "}";
+   env.set("MOZ_TOOLBOX_TEST_SCRIPT", source);
+   registerCleanupFunction(() => {
+     env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
+   });
+ 
+   let { BrowserToolboxProcess } = ChromeUtils.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});
+   // Use two promises, one for each BrowserToolboxProcess.init callback
+   // arguments, to ensure that we wait for toolbox run and close events.
+   let closePromise;
+-  yield new Promise(onRun => {
++  await new Promise(onRun => {
+     closePromise = new Promise(onClose => {
+       info("Opening the browser toolbox\n");
+       BrowserToolboxProcess.init(onClose, onRun);
+     });
+   });
+   ok(true, "Browser toolbox started\n");
+ 
+-  yield closePromise;
++  await closePromise;
+   ok(true, "Browser toolbox process just closed");
+ 
+   clearInterval(interval);
+ });
+diff --git a/devtools/client/framework/test/browser_devtools_api.js b/devtools/client/framework/test/browser_devtools_api.js
+--- a/devtools/client/framework/test/browser_devtools_api.js
++++ b/devtools/client/framework/test/browser_devtools_api.js
+@@ -134,58 +134,58 @@ function runTests2() {
+     ok(events.init, "init event fired");
+     ok(events.build, "build event fired");
+     ok(events.ready, "ready event fired");
+ 
+     continueTests(toolbox);
+   });
+ }
+ 
+-var continueTests = Task.async(function* (toolbox, panel) {
++var continueTests = async function (toolbox, panel) {
+   ok(toolbox.getCurrentPanel(), "panel value is correct");
+   is(toolbox.currentToolId, toolId2, "toolbox _currentToolId is correct");
+ 
+   let toolDefinitions = gDevTools.getToolDefinitionMap();
+   ok(toolDefinitions.has(toolId2), "The tool is in gDevTools");
+ 
+   let toolDefinition = toolDefinitions.get(toolId2);
+   is(toolDefinition.id, toolId2, "toolDefinition id is correct");
+ 
+   info("Testing toolbox tool-unregistered event");
+   let toolSelected = toolbox.once("select");
+-  let unregisteredTool = yield new Promise(resolve => {
++  let unregisteredTool = await new Promise(resolve => {
+     toolbox.once("tool-unregistered", (e, id) => resolve(id));
+     gDevTools.unregisterTool(toolId2);
+   });
+-  yield toolSelected;
++  await toolSelected;
+ 
+   is(unregisteredTool, toolId2, "Event returns correct id");
+   ok(!toolbox.isToolRegistered(toolId2),
+     "Toolbox: The tool is not registered");
+   ok(!gDevTools.getToolDefinitionMap().has(toolId2),
+     "The tool is no longer registered");
+ 
+   info("Testing toolbox tool-registered event");
+-  let registeredTool = yield new Promise(resolve => {
++  let registeredTool = await new Promise(resolve => {
+     toolbox.once("tool-registered", (e, id) => resolve(id));
+     gDevTools.registerTool(toolDefinition);
+   });
+ 
+   is(registeredTool, toolId2, "Event returns correct id");
+   ok(toolbox.isToolRegistered(toolId2),
+     "Toolbox: The tool is registered");
+   ok(gDevTools.getToolDefinitionMap().has(toolId2),
+     "The tool is registered");
+ 
+   info("Unregistering tool");
+   gDevTools.unregisterTool(toolId2);
+ 
+   info("Destroying toolbox");
+   destroyToolbox(toolbox);
+-});
++};
+ 
+ function destroyToolbox(toolbox) {
+   toolbox.destroy().then(function () {
+     let target = TargetFactory.forTab(gBrowser.selectedTab);
+     ok(gDevTools._toolboxes.get(target) == null, "gDevTools doesn't know about target");
+     ok(toolbox.target == null, "toolbox doesn't know about target.");
+     finishUp();
+   });
+diff --git a/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js b/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
+--- a/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
++++ b/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
+@@ -3,32 +3,32 @@
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // Test that network requests originating from the toolbox don't get recorded in
+ // the network panel.
+ 
+-add_task(function* () {
++add_task(async function () {
+   // TODO: This test tries to verify the normal behavior of the netmonitor and
+   // therefore needs to avoid the explicit check for tests. Bug 1167188 will
+   // allow us to remove this workaround.
+   let isTesting = flags.testing;
+   flags.testing = false;
+ 
+-  let tab = yield addTab(URL_ROOT + "doc_viewsource.html");
++  let tab = await addTab(URL_ROOT + "doc_viewsource.html");
+   let target = TargetFactory.forTab(tab);
+-  let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
++  let toolbox = await gDevTools.showToolbox(target, "styleeditor");
+   let panel = toolbox.getPanel("styleeditor");
+ 
+   is(panel.UI.editors.length, 1, "correct number of editors opened");
+ 
+-  let monitor = yield toolbox.selectTool("netmonitor");
++  let monitor = await toolbox.selectTool("netmonitor");
+   let { store, windowRequire } = monitor.panelWin;
+ 
+   is(store.getState().requests.requests.size, 0, "No network requests appear in the network panel");
+ 
+-  yield gDevTools.closeToolbox(target);
++  await gDevTools.closeToolbox(target);
+   tab = target = toolbox = panel = null;
+   gBrowser.removeCurrentTab();
+   flags.testing = isTesting;
+ });
+diff --git a/devtools/client/framework/test/browser_keybindings_02.js b/devtools/client/framework/test/browser_keybindings_02.js
+--- a/devtools/client/framework/test/browser_keybindings_02.js
++++ b/devtools/client/framework/test/browser_keybindings_02.js
+@@ -13,50 +13,50 @@ var {Toolbox} = require("devtools/client
+ 
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+ 
+ function getZoomValue() {
+   return parseFloat(Services.prefs.getCharPref("devtools.toolbox.zoomValue"));
+ }
+ 
+-add_task(function* () {
++add_task(async function () {
+   info("Create a test tab and open the toolbox");
+-  let tab = yield addTab(URL);
++  let tab = await addTab(URL);
+   let target = TargetFactory.forTab(tab);
+-  let toolbox = yield gDevTools.showToolbox(target, "webconsole");
++  let toolbox = await gDevTools.showToolbox(target, "webconsole");
+ 
+   let {SIDE, BOTTOM} = Toolbox.HostType;
+   for (let type of [SIDE, BOTTOM, SIDE]) {
+     info("Switch to host type " + type);
+-    yield toolbox.switchHost(type);
++    await toolbox.switchHost(type);
+ 
+     info("Try to use the toolbox shortcuts");
+-    yield checkKeyBindings(toolbox);
++    await checkKeyBindings(toolbox);
+   }
+ 
+   Services.prefs.clearUserPref("devtools.toolbox.zoomValue");
+   Services.prefs.setCharPref("devtools.toolbox.host", BOTTOM);
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   gBrowser.removeCurrentTab();
+ });
+ 
+ function zoomWithKey(toolbox, key) {
+   let shortcut = L10N.getStr(key);
+   if (!shortcut) {
+     info("Key was empty, skipping zoomWithKey");
+     return;
+   }
+   info("Zooming with key: " + key);
+   let currentZoom = getZoomValue();
+   synthesizeKeyShortcut(shortcut, toolbox.win);
+   isnot(getZoomValue(), currentZoom, "The zoom level was changed in the toolbox");
+ }
+ 
+-function* checkKeyBindings(toolbox) {
++function checkKeyBindings(toolbox) {
+   zoomWithKey(toolbox, "toolbox.zoomIn.key");
+   zoomWithKey(toolbox, "toolbox.zoomIn2.key");
+   zoomWithKey(toolbox, "toolbox.zoomIn3.key");
+ 
+   zoomWithKey(toolbox, "toolbox.zoomReset.key");
+ 
+   zoomWithKey(toolbox, "toolbox.zoomOut.key");
+   zoomWithKey(toolbox, "toolbox.zoomOut2.key");
+diff --git a/devtools/client/framework/test/browser_keybindings_03.js b/devtools/client/framework/test/browser_keybindings_03.js
+--- a/devtools/client/framework/test/browser_keybindings_03.js
++++ b/devtools/client/framework/test/browser_keybindings_03.js
+@@ -10,44 +10,44 @@
+ 
+ const URL = "data:text/html;charset=utf8,test page for toolbox switching";
+ 
+ var {Toolbox} = require("devtools/client/framework/toolbox");
+ 
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+ 
+-add_task(function* () {
++add_task(async function () {
+   info("Create a test tab and open the toolbox");
+-  let tab = yield addTab(URL);
++  let tab = await addTab(URL);
+   let target = TargetFactory.forTab(tab);
+-  let toolbox = yield gDevTools.showToolbox(target, "webconsole");
++  let toolbox = await gDevTools.showToolbox(target, "webconsole");
+ 
+   let shortcut = L10N.getStr("toolbox.toggleHost.key");
+ 
+   let {SIDE, BOTTOM, WINDOW} = Toolbox.HostType;
+   checkHostType(toolbox, BOTTOM, SIDE);
+ 
+   info("Switching from bottom to side");
+   let onHostChanged = toolbox.once("host-changed");
+   synthesizeKeyShortcut(shortcut, toolbox.win);
+-  yield onHostChanged;
++  await onHostChanged;
+   checkHostType(toolbox, SIDE, BOTTOM);
+ 
+   info("Switching from side to bottom");
+   onHostChanged = toolbox.once("host-changed");
+   synthesizeKeyShortcut(shortcut, toolbox.win);
+-  yield onHostChanged;
++  await onHostChanged;
+   checkHostType(toolbox, BOTTOM, SIDE);
+ 
+   info("Switching to window");
+-  yield toolbox.switchHost(WINDOW);
++  await toolbox.switchHost(WINDOW);
+   checkHostType(toolbox, WINDOW, BOTTOM);
+ 
+   info("Switching from window to bottom");
+   onHostChanged = toolbox.once("host-changed");
+   synthesizeKeyShortcut(shortcut, toolbox.win);
+-  yield onHostChanged;
++  await onHostChanged;
+   checkHostType(toolbox, BOTTOM, WINDOW);
+ 
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/framework/test/browser_menu_api.js b/devtools/client/framework/test/browser_menu_api.js
+--- a/devtools/client/framework/test/browser_menu_api.js
++++ b/devtools/client/framework/test/browser_menu_api.js
+@@ -6,41 +6,41 @@
+ "use strict";
+ 
+ // Test that the Menu API works
+ 
+ const URL = "data:text/html;charset=utf8,test page for menu api";
+ const Menu = require("devtools/client/framework/menu");
+ const MenuItem = require("devtools/client/framework/menu-item");
+ 
+-add_task(function* () {
++add_task(async function () {
+   info("Create a test tab and open the toolbox");
+-  let tab = yield addTab(URL);
++  let tab = await addTab(URL);
+   let target = TargetFactory.forTab(tab);
+-  let toolbox = yield gDevTools.showToolbox(target, "webconsole");
++  let toolbox = await gDevTools.showToolbox(target, "webconsole");
+ 
+-  yield testMenuItems();
+-  yield testMenuPopup(toolbox);
+-  yield testSubmenu(toolbox);
++  await testMenuItems();
++  await testMenuPopup(toolbox);
++  await testSubmenu(toolbox);
+ });
+ 
+-function* testMenuItems() {
++function testMenuItems() {
+   let menu = new Menu();
+   let menuItem1 = new MenuItem();
+   let menuItem2 = new MenuItem();
+ 
+   menu.append(menuItem1);
+   menu.append(menuItem2);
+ 
+   is(menu.items.length, 2, "Correct number of 'items'");
+   is(menu.items[0], menuItem1, "Correct reference to MenuItem");
+   is(menu.items[1], menuItem2, "Correct reference to MenuItem");
+ }
+ 
+-function* testMenuPopup(toolbox) {
++async function testMenuPopup(toolbox) {
+   let clickFired = false;
+ 
+   let menu = new Menu({
+     id: "menu-popup",
+   });
+   menu.append(new MenuItem({ type: "separator" }));
+ 
+   let MENU_ITEMS = [
+@@ -97,26 +97,26 @@ function* testMenuPopup(toolbox) {
+ 
+   is(menuItems[2].getAttribute("label"), MENU_ITEMS[2].label, "Correct label");
+   is(menuItems[2].getAttribute("type"), "radio", "Correct type attr");
+   ok(!menuItems[2].hasAttribute("checked"), "Doesn't have checked attr");
+ 
+   is(menuItems[3].getAttribute("label"), MENU_ITEMS[3].label, "Correct label");
+   is(menuItems[3].getAttribute("disabled"), "true", "disabled attr menuitem");
+ 
+-  yield once(menu, "open");
++  await once(menu, "open");
+   let closed = once(menu, "close");
+   EventUtils.synthesizeMouseAtCenter(menuItems[0], {}, toolbox.win);
+-  yield closed;
++  await closed;
+   ok(clickFired, "Click has fired");
+ 
+   ok(!toolbox.doc.querySelector("#menu-popup"), "Popup removed from the DOM");
+ }
+ 
+-function* testSubmenu(toolbox) {
++async function testSubmenu(toolbox) {
+   let clickFired = false;
+   let menu = new Menu({
+     id: "menu-popup",
+   });
+   let submenu = new Menu({
+     id: "submenu-popup",
+   });
+   submenu.append(new MenuItem({
+@@ -151,31 +151,31 @@ function* testSubmenu(toolbox) {
+   is(menus[1].getAttribute("accesskey"), "A", "Correct accesskey");
+   ok(menus[1].hasAttribute("disabled"), "Correct disabled state");
+   ok(menus[1].id, "submenu-parent-with-attrs", "Correct id");
+ 
+   let subMenuItems = menus[0].querySelectorAll("menupopup > menuitem");
+   is(subMenuItems.length, 1, "Correct number of submenu items");
+   is(subMenuItems[0].getAttribute("label"), "Submenu item", "Correct label");
+ 
+-  yield once(menu, "open");
++  await once(menu, "open");
+   let closed = once(menu, "close");
+ 
+   info("Using keyboard navigation to open, close, and reopen the submenu");
+   let shown = once(menus[0], "popupshown");
+   EventUtils.synthesizeKey("KEY_ArrowDown");
+   EventUtils.synthesizeKey("KEY_ArrowRight");
+-  yield shown;
++  await shown;
+ 
+   let hidden = once(menus[0], "popuphidden");
+   EventUtils.synthesizeKey("KEY_ArrowLeft");
+-  yield hidden;
++  await hidden;
+ 
+   shown = once(menus[0], "popupshown");
+   EventUtils.synthesizeKey("KEY_ArrowRight");
+-  yield shown;
++  await shown;
+ 
+   info("Clicking the submenu item");
+   EventUtils.synthesizeMouseAtCenter(subMenuItems[0], {}, toolbox.win);
+ 
+-  yield closed;
++  await closed;
+   ok(clickFired, "Click has fired");
+ }
+diff --git a/devtools/client/framework/test/browser_source_map-01.js b/devtools/client/framework/test/browser_source_map-01.js
+--- a/devtools/client/framework/test/browser_source_map-01.js
++++ b/devtools/client/framework/test/browser_source_map-01.js
+@@ -15,36 +15,36 @@ const { PromiseTestUtils } = scopedCuImp
+ PromiseTestUtils.whitelistRejectionsGlobally(/this\.worker is null/);
+ PromiseTestUtils.whitelistRejectionsGlobally(/Component not initialized/);
+ 
+ // Empty page
+ const PAGE_URL = `${URL_ROOT}doc_empty-tab-01.html`;
+ const JS_URL = `${URL_ROOT}code_binary_search.js`;
+ const COFFEE_URL = `${URL_ROOT}code_binary_search.coffee`;
+ 
+-add_task(function* () {
+-  yield pushPref("devtools.debugger.new-debugger-frontend", true);
++add_task(async function () {
++  await pushPref("devtools.debugger.new-debugger-frontend", true);
+ 
+-  const toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
++  const toolbox = await openNewTabAndToolbox(PAGE_URL, "jsdebugger");
+   const service = toolbox.sourceMapURLService;
+ 
+   // Inject JS script
+   let sourceSeen = waitForSourceLoad(toolbox, JS_URL);
+-  yield createScript(JS_URL);
+-  yield sourceSeen;
++  await createScript(JS_URL);
++  await sourceSeen;
+ 
+   let loc1 = { url: JS_URL, line: 6 };
+-  let newLoc1 = yield service.originalPositionFor(loc1.url, loc1.line, 4);
++  let newLoc1 = await service.originalPositionFor(loc1.url, loc1.line, 4);
+   checkLoc1(loc1, newLoc1);
+ 
+   let loc2 = { url: JS_URL, line: 8, column: 3 };
+-  let newLoc2 = yield service.originalPositionFor(loc2.url, loc2.line, loc2.column);
++  let newLoc2 = await service.originalPositionFor(loc2.url, loc2.line, loc2.column);
+   checkLoc2(loc2, newLoc2);
+ 
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   gBrowser.removeCurrentTab();
+   finish();
+ });
+ 
+ function checkLoc1(oldLoc, newLoc) {
+   is(oldLoc.line, 6, "Correct line for JS:6");
+   is(oldLoc.column, null, "Correct column for JS:6");
+   is(oldLoc.url, JS_URL, "Correct url for JS:6");
+diff --git a/devtools/client/framework/test/browser_source_map-absolute.js b/devtools/client/framework/test/browser_source_map-absolute.js
+--- a/devtools/client/framework/test/browser_source_map-absolute.js
++++ b/devtools/client/framework/test/browser_source_map-absolute.js
+@@ -10,25 +10,25 @@
+ const { PromiseTestUtils } = scopedCuImport("resource://testing-common/PromiseTestUtils.jsm");
+ PromiseTestUtils.whitelistRejectionsGlobally(/this\.worker is null/);
+ 
+ // Empty page
+ const PAGE_URL = `${URL_ROOT}doc_empty-tab-01.html`;
+ const JS_URL = `${URL_ROOT}code_binary_search_absolute.js`;
+ const ORIGINAL_URL = `${URL_ROOT}code_binary_search.coffee`;
+ 
+-add_task(function* () {
+-  yield pushPref("devtools.debugger.new-debugger-frontend", true);
++add_task(async function () {
++  await pushPref("devtools.debugger.new-debugger-frontend", true);
+ 
+-  const toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
++  const toolbox = await openNewTabAndToolbox(PAGE_URL, "jsdebugger");
+   const service = toolbox.sourceMapURLService;
+ 
+   // Inject JS script
+   let sourceSeen = waitForSourceLoad(toolbox, JS_URL);
+-  yield createScript(JS_URL);
+-  yield sourceSeen;
++  await createScript(JS_URL);
++  await sourceSeen;
+ 
+   info(`checking original location for ${JS_URL}:6`);
+-  let newLoc = yield service.originalPositionFor(JS_URL, 6, 4);
++  let newLoc = await service.originalPositionFor(JS_URL, 6, 4);
+ 
+   is(newLoc.sourceUrl, ORIGINAL_URL, "check mapped URL");
+   is(newLoc.line, 4, "check mapped line number");
+ });
+diff --git a/devtools/client/framework/test/browser_source_map-cross-domain.js b/devtools/client/framework/test/browser_source_map-cross-domain.js
+--- a/devtools/client/framework/test/browser_source_map-cross-domain.js
++++ b/devtools/client/framework/test/browser_source_map-cross-domain.js
+@@ -23,17 +23,17 @@ const PAGE_URL = `data:text/html,
+ 
+ </html>`;
+ 
+ const ORIGINAL_URL = "webpack:///code_cross_domain.js";
+ 
+ const GENERATED_LINE = 82;
+ const ORIGINAL_LINE = 12;
+ 
+-add_task(function* () {
+-  const toolbox = yield openNewTabAndToolbox(PAGE_URL, "webconsole");
++add_task(async function () {
++  const toolbox = await openNewTabAndToolbox(PAGE_URL, "webconsole");
+   const service = toolbox.sourceMapURLService;
+ 
+   info(`checking original location for ${JS_URL}:${GENERATED_LINE}`);
+-  let newLoc = yield service.originalPositionFor(JS_URL, GENERATED_LINE);
++  let newLoc = await service.originalPositionFor(JS_URL, GENERATED_LINE);
+   is(newLoc.sourceUrl, ORIGINAL_URL, "check mapped URL");
+   is(newLoc.line, ORIGINAL_LINE, "check mapped line number");
+ });
+diff --git a/devtools/client/framework/test/browser_source_map-init.js b/devtools/client/framework/test/browser_source_map-init.js
+--- a/devtools/client/framework/test/browser_source_map-init.js
++++ b/devtools/client/framework/test/browser_source_map-init.js
+@@ -23,21 +23,21 @@ const PAGE_URL = `data:text/html,
+ 
+ </html>`;
+ 
+ const ORIGINAL_URL = "webpack:///code_no_race.js";
+ 
+ const GENERATED_LINE = 84;
+ const ORIGINAL_LINE = 11;
+ 
+-add_task(function* () {
++add_task(async function () {
+   // Opening the debugger causes the source actors to be created.
+-  const toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
++  const toolbox = await openNewTabAndToolbox(PAGE_URL, "jsdebugger");
+   // In bug 1391768, when the sourceMapURLService was created, it was
+   // ignoring any source actors that already existed, leading to
+   // source-mapping failures for those.
+   const service = toolbox.sourceMapURLService;
+ 
+   info(`checking original location for ${JS_URL}:${GENERATED_LINE}`);
+-  let newLoc = yield service.originalPositionFor(JS_URL, GENERATED_LINE);
++  let newLoc = await service.originalPositionFor(JS_URL, GENERATED_LINE);
+   is(newLoc.sourceUrl, ORIGINAL_URL, "check mapped URL");
+   is(newLoc.line, ORIGINAL_LINE, "check mapped line number");
+ });
+diff --git a/devtools/client/framework/test/browser_source_map-inline.js b/devtools/client/framework/test/browser_source_map-inline.js
+--- a/devtools/client/framework/test/browser_source_map-inline.js
++++ b/devtools/client/framework/test/browser_source_map-inline.js
+@@ -12,29 +12,29 @@ PromiseTestUtils.whitelistRejectionsGlob
+ PromiseTestUtils.whitelistRejectionsGlobally(/Component not initialized/);
+ 
+ const TEST_ROOT = "http://example.com/browser/devtools/client/framework/test/";
+ // Empty page
+ const PAGE_URL = `${TEST_ROOT}doc_empty-tab-01.html`;
+ const JS_URL = `${TEST_ROOT}code_inline_bundle.js`;
+ const ORIGINAL_URL = "webpack:///code_inline_original.js";
+ 
+-add_task(function* () {
+-  yield pushPref("devtools.debugger.new-debugger-frontend", true);
++add_task(async function () {
++  await pushPref("devtools.debugger.new-debugger-frontend", true);
+ 
+-  const toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
++  const toolbox = await openNewTabAndToolbox(PAGE_URL, "jsdebugger");
+   const service = toolbox.sourceMapURLService;
+ 
+   // Inject JS script
+   let sourceSeen = waitForSourceLoad(toolbox, JS_URL);
+-  yield createScript(JS_URL);
+-  yield sourceSeen;
++  await createScript(JS_URL);
++  await sourceSeen;
+ 
+   info(`checking original location for ${JS_URL}:84`);
+-  let newLoc = yield service.originalPositionFor(JS_URL, 84);
++  let newLoc = await service.originalPositionFor(JS_URL, 84);
+ 
+   is(newLoc.sourceUrl, ORIGINAL_URL, "check mapped URL");
+   is(newLoc.line, 11, "check mapped line number");
+ 
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   gBrowser.removeCurrentTab();
+   finish();
+ });
+diff --git a/devtools/client/framework/test/browser_source_map-no-race.js b/devtools/client/framework/test/browser_source_map-no-race.js
+--- a/devtools/client/framework/test/browser_source_map-no-race.js
++++ b/devtools/client/framework/test/browser_source_map-no-race.js
+@@ -23,19 +23,19 @@ const PAGE_URL = `data:text/html,
+ 
+ </html>`;
+ 
+ const ORIGINAL_URL = "webpack:///code_no_race.js";
+ 
+ const GENERATED_LINE = 84;
+ const ORIGINAL_LINE = 11;
+ 
+-add_task(function* () {
++add_task(async function () {
+   // Start with the empty page, then navigate, so that we can properly
+   // listen for new sources arriving.
+-  const toolbox = yield openNewTabAndToolbox(PAGE_URL, "webconsole");
++  const toolbox = await openNewTabAndToolbox(PAGE_URL, "webconsole");
+   const service = toolbox.sourceMapURLService;
+ 
+   info(`checking original location for ${JS_URL}:${GENERATED_LINE}`);
+-  let newLoc = yield service.originalPositionFor(JS_URL, GENERATED_LINE);
++  let newLoc = await service.originalPositionFor(JS_URL, GENERATED_LINE);
+   is(newLoc.sourceUrl, ORIGINAL_URL, "check mapped URL");
+   is(newLoc.line, ORIGINAL_LINE, "check mapped line number");
+ });
+diff --git a/devtools/client/framework/test/browser_source_map-reload.js b/devtools/client/framework/test/browser_source_map-reload.js
+--- a/devtools/client/framework/test/browser_source_map-reload.js
++++ b/devtools/client/framework/test/browser_source_map-reload.js
+@@ -10,41 +10,41 @@ const PAGE_URL = URL_ROOT + "doc_reload.
+ const JS_URL = URL_ROOT + "sjs_code_reload.sjs";
+ 
+ const ORIGINAL_URL_1 = "webpack:///code_reload_1.js";
+ const ORIGINAL_URL_2 = "webpack:///code_reload_2.js";
+ 
+ const GENERATED_LINE = 86;
+ const ORIGINAL_LINE = 13;
+ 
+-add_task(function* () {
+-  yield pushPref("devtools.debugger.new-debugger-frontend", true);
++add_task(async function () {
++  await pushPref("devtools.debugger.new-debugger-frontend", true);
+ 
+   // Start with the empty page, then navigate, so that we can properly
+   // listen for new sources arriving.
+-  const toolbox = yield openNewTabAndToolbox(INITIAL_URL, "webconsole");
++  const toolbox = await openNewTabAndToolbox(INITIAL_URL, "webconsole");
+   const service = toolbox.sourceMapURLService;
+   const tab = toolbox.target.tab;
+ 
+   let sourceSeen = waitForSourceLoad(toolbox, JS_URL);
+   tab.linkedBrowser.loadURI(PAGE_URL);
+-  yield sourceSeen;
++  await sourceSeen;
+ 
+   info(`checking original location for ${JS_URL}:${GENERATED_LINE}`);
+-  let newLoc = yield service.originalPositionFor(JS_URL, GENERATED_LINE);
++  let newLoc = await service.originalPositionFor(JS_URL, GENERATED_LINE);
+   is(newLoc.sourceUrl, ORIGINAL_URL_1, "check mapped URL");
+   is(newLoc.line, ORIGINAL_LINE, "check mapped line number");
+ 
+   // Reload the page.  The sjs ensures that a different source file
+   // will be loaded.
+   sourceSeen = waitForSourceLoad(toolbox, JS_URL);
+-  yield refreshTab();
+-  yield sourceSeen;
++  await refreshTab();
++  await sourceSeen;
+ 
+   info(`checking post-reload original location for ${JS_URL}:${GENERATED_LINE}`);
+-  newLoc = yield service.originalPositionFor(JS_URL, GENERATED_LINE);
++  newLoc = await service.originalPositionFor(JS_URL, GENERATED_LINE);
+   is(newLoc.sourceUrl, ORIGINAL_URL_2, "check post-reload mapped URL");
+   is(newLoc.line, ORIGINAL_LINE, "check post-reload mapped line number");
+ 
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   gBrowser.removeCurrentTab();
+   finish();
+ });
+diff --git a/devtools/client/framework/test/browser_target_from_url.js b/devtools/client/framework/test/browser_target_from_url.js
+--- a/devtools/client/framework/test/browser_target_from_url.js
++++ b/devtools/client/framework/test/browser_target_from_url.js
+@@ -18,127 +18,127 @@ SimpleTest.registerCleanupFunction(() =>
+ function assertIsTabTarget(target, url, chrome = false) {
+   is(target.url, url);
+   is(target.isLocalTab, false);
+   is(target.chrome, chrome);
+   is(target.isTabActor, true);
+   is(target.isRemote, true);
+ }
+ 
+-add_task(function* () {
+-  let tab = yield addTab(TEST_URI);
++add_task(async function () {
++  let tab = await addTab(TEST_URI);
+   let browser = tab.linkedBrowser;
+   let target;
+ 
+   info("Test invalid type");
+   try {
+-    yield targetFromURL(new URL("http://foo?type=x"));
++    await targetFromURL(new URL("http://foo?type=x"));
+     ok(false, "Shouldn't pass");
+   } catch (e) {
+     is(e.message, "targetFromURL, unsupported type 'x' parameter");
+   }
+ 
+   info("Test browser window");
+   let windowId = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIDOMWindowUtils)
+                        .outerWindowID;
+-  target = yield targetFromURL(new URL("http://foo?type=window&id=" + windowId));
++  target = await targetFromURL(new URL("http://foo?type=window&id=" + windowId));
+   is(target.url, window.location.href);
+   is(target.isLocalTab, false);
+   is(target.chrome, true);
+   is(target.isTabActor, true);
+   is(target.isRemote, true);
+ 
+   info("Test tab");
+   windowId = browser.outerWindowID;
+-  target = yield targetFromURL(new URL("http://foo?type=tab&id=" + windowId));
++  target = await targetFromURL(new URL("http://foo?type=tab&id=" + windowId));
+   assertIsTabTarget(target, TEST_URI);
+ 
+   info("Test tab with chrome privileges");
+-  target = yield targetFromURL(new URL("http://foo?type=tab&id=" + windowId + "&chrome"));
++  target = await targetFromURL(new URL("http://foo?type=tab&id=" + windowId + "&chrome"));
+   assertIsTabTarget(target, TEST_URI, true);
+ 
+   info("Test invalid tab id");
+   try {
+-    yield targetFromURL(new URL("http://foo?type=tab&id=10000"));
++    await targetFromURL(new URL("http://foo?type=tab&id=10000"));
+     ok(false, "Shouldn't pass");
+   } catch (e) {
+     is(e.message, "targetFromURL, tab with outerWindowID '10000' doesn't exist");
+   }
+ 
+   info("Test parent process");
+-  target = yield targetFromURL(new URL("http://foo?type=process"));
++  target = await targetFromURL(new URL("http://foo?type=process"));
+   let topWindow = Services.wm.getMostRecentWindow("navigator:browser");
+   assertIsTabTarget(target, topWindow.location.href, true);
+ 
+-  yield testRemoteTCP();
+-  yield testRemoteWebSocket();
++  await testRemoteTCP();
++  await testRemoteWebSocket();
+ 
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* setupDebuggerServer(websocket) {
++async function setupDebuggerServer(websocket) {
+   info("Create a separate loader instance for the DebuggerServer.");
+   let loader = new DevToolsLoader();
+   let { DebuggerServer } = loader.require("devtools/server/main");
+ 
+   DebuggerServer.init();
+   DebuggerServer.registerAllActors();
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let listener = DebuggerServer.createListener();
+   ok(listener, "Socket listener created");
+   // Pass -1 to automatically choose an available port
+   listener.portOrPath = -1;
+   listener.webSocket = websocket;
+-  yield listener.open();
++  await listener.open();
+   is(DebuggerServer.listeningSockets, 1, "1 listening socket");
+ 
+   return { DebuggerServer, listener };
+ }
+ 
+ function teardownDebuggerServer({ DebuggerServer, listener }) {
+   info("Close the listener socket");
+   listener.close();
+   is(DebuggerServer.listeningSockets, 0, "0 listening sockets");
+ 
+   info("Destroy the temporary debugger server");
+   DebuggerServer.destroy();
+ }
+ 
+-function* testRemoteTCP() {
++async function testRemoteTCP() {
+   info("Test remote process via TCP Connection");
+ 
+-  let server = yield setupDebuggerServer(false);
++  let server = await setupDebuggerServer(false);
+ 
+   let { port } = server.listener;
+-  let target = yield targetFromURL(new URL("http://foo?type=process&host=127.0.0.1&port=" + port));
++  let target = await targetFromURL(new URL("http://foo?type=process&host=127.0.0.1&port=" + port));
+   let topWindow = Services.wm.getMostRecentWindow("navigator:browser");
+   assertIsTabTarget(target, topWindow.location.href, true);
+ 
+   let settings = target.client._transport.connectionSettings;
+   is(settings.host, "127.0.0.1");
+   is(settings.port, port);
+   is(settings.webSocket, false);
+ 
+-  yield target.client.close();
++  await target.client.close();
+ 
+   teardownDebuggerServer(server);
+ }
+ 
+-function* testRemoteWebSocket() {
++async function testRemoteWebSocket() {
+   info("Test remote process via WebSocket Connection");
+ 
+-  let server = yield setupDebuggerServer(true);
++  let server = await setupDebuggerServer(true);
+ 
+   let { port } = server.listener;
+-  let target = yield targetFromURL(new URL("http://foo?type=process&host=127.0.0.1&port=" + port + "&ws=true"));
++  let target = await targetFromURL(new URL("http://foo?type=process&host=127.0.0.1&port=" + port + "&ws=true"));
+   let topWindow = Services.wm.getMostRecentWindow("navigator:browser");
+   assertIsTabTarget(target, topWindow.location.href, true);
+ 
+   let settings = target.client._transport.connectionSettings;
+   is(settings.host, "127.0.0.1");
+   is(settings.port, port);
+   is(settings.webSocket, true);
+-  yield target.client.close();
++  await target.client.close();
+ 
+   teardownDebuggerServer(server);
+ }
+diff --git a/devtools/client/framework/test/browser_target_support.js b/devtools/client/framework/test/browser_target_support.js
+--- a/devtools/client/framework/test/browser_target_support.js
++++ b/devtools/client/framework/test/browser_target_support.js
+@@ -4,46 +4,46 @@
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ // Test support methods on Target, such as `hasActor`, `getActorDescription`,
+ // `actorHasMethod` and `getTrait`.
+ 
+ var { WebAudioFront } =
+   require("devtools/shared/fronts/webaudio");
+ 
+-function* testTarget(client, target) {
+-  yield target.makeRemote();
++async function testTarget(client, target) {
++  await target.makeRemote();
+ 
+   is(target.hasActor("timeline"), true, "target.hasActor() true when actor exists.");
+   is(target.hasActor("webaudio"), true, "target.hasActor() true when actor exists.");
+   is(target.hasActor("notreal"), false, "target.hasActor() false when actor does not exist.");
+   // Create a front to ensure the actor is loaded
+   let front = new WebAudioFront(target.client, target.form);
+ 
+-  let desc = yield target.getActorDescription("webaudio");
++  let desc = await target.getActorDescription("webaudio");
+   is(desc.typeName, "webaudio",
+     "target.getActorDescription() returns definition data for corresponding actor");
+   is(desc.events["start-context"]["type"], "startContext",
+     "target.getActorDescription() returns event data for corresponding actor");
+ 
+-  desc = yield target.getActorDescription("nope");
++  desc = await target.getActorDescription("nope");
+   is(desc, undefined, "target.getActorDescription() returns undefined for non-existing actor");
+-  desc = yield target.getActorDescription();
++  desc = await target.getActorDescription();
+   is(desc, undefined, "target.getActorDescription() returns undefined for undefined actor");
+ 
+-  let hasMethod = yield target.actorHasMethod("audionode", "getType");
++  let hasMethod = await target.actorHasMethod("audionode", "getType");
+   is(hasMethod, true,
+     "target.actorHasMethod() returns true for existing actor with method");
+-  hasMethod = yield target.actorHasMethod("audionode", "nope");
++  hasMethod = await target.actorHasMethod("audionode", "nope");
+   is(hasMethod, false,
+     "target.actorHasMethod() returns false for existing actor with no method");
+-  hasMethod = yield target.actorHasMethod("nope", "nope");
++  hasMethod = await target.actorHasMethod("nope", "nope");
+   is(hasMethod, false,
+     "target.actorHasMethod() returns false for non-existing actor with no method");
+-  hasMethod = yield target.actorHasMethod();
++  hasMethod = await target.actorHasMethod();
+   is(hasMethod, false,
+     "target.actorHasMethod() returns false for undefined params");
+ 
+   is(target.getTrait("customHighlighters"), true,
+     "target.getTrait() returns boolean when trait exists");
+   is(target.getTrait("giddyup"), undefined,
+     "target.getTrait() returns undefined when trait does not exist");
+ 
+diff --git a/devtools/client/framework/test/browser_toolbox_getpanelwhenready.js b/devtools/client/framework/test/browser_toolbox_getpanelwhenready.js
+--- a/devtools/client/framework/test/browser_toolbox_getpanelwhenready.js
++++ b/devtools/client/framework/test/browser_toolbox_getpanelwhenready.js
+@@ -5,32 +5,32 @@
+ 
+ // Tests that getPanelWhenReady returns the correct panel in promise
+ // resolutions regardless of whether it has opened first.
+ 
+ var toolbox = null;
+ 
+ const URL = "data:text/html;charset=utf8,test for getPanelWhenReady";
+ 
+-add_task(function* () {
+-  let tab = yield addTab(URL);
++add_task(async function () {
++  let tab = await addTab(URL);
+   let target = TargetFactory.forTab(tab);
+-  toolbox = yield gDevTools.showToolbox(target);
++  toolbox = await gDevTools.showToolbox(target);
+ 
+   let debuggerPanelPromise = toolbox.getPanelWhenReady("jsdebugger");
+-  yield toolbox.selectTool("jsdebugger");
+-  let debuggerPanel = yield debuggerPanelPromise;
++  await toolbox.selectTool("jsdebugger");
++  let debuggerPanel = await debuggerPanelPromise;
+ 
+   is(debuggerPanel, toolbox.getPanel("jsdebugger"),
+       "The debugger panel from getPanelWhenReady before loading is the actual panel");
+ 
+-  let debuggerPanel2 = yield toolbox.getPanelWhenReady("jsdebugger");
++  let debuggerPanel2 = await toolbox.getPanelWhenReady("jsdebugger");
+   is(debuggerPanel2, toolbox.getPanel("jsdebugger"),
+       "The debugger panel from getPanelWhenReady after loading is the actual panel");
+ 
+-  yield cleanup();
++  await cleanup();
+ });
+ 
+-function* cleanup() {
+-  yield toolbox.destroy();
++async function cleanup() {
++  await toolbox.destroy();
+   gBrowser.removeCurrentTab();
+   toolbox = null;
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_highlight.js b/devtools/client/framework/test/browser_toolbox_highlight.js
+--- a/devtools/client/framework/test/browser_toolbox_highlight.js
++++ b/devtools/client/framework/test/browser_toolbox_highlight.js
+@@ -5,69 +5,69 @@
+ 
+ "use strict";
+ 
+ var {Toolbox} = require("devtools/client/framework/toolbox");
+ 
+ var toolbox = null;
+ 
+ function test() {
+-  Task.spawn(function* () {
++  (async function () {
+     const URL = "data:text/plain;charset=UTF-8,Nothing to see here, move along";
+ 
+     const TOOL_ID_1 = "jsdebugger";
+     const TOOL_ID_2 = "webconsole";
+-    yield addTab(URL);
++    await addTab(URL);
+ 
+     const target = TargetFactory.forTab(gBrowser.selectedTab);
+-    toolbox = yield gDevTools.showToolbox(target, TOOL_ID_1, Toolbox.HostType.BOTTOM);
++    toolbox = await gDevTools.showToolbox(target, TOOL_ID_1, Toolbox.HostType.BOTTOM);
+ 
+     // select tool 2
+-    yield toolbox.selectTool(TOOL_ID_2);
++    await toolbox.selectTool(TOOL_ID_2);
+     // and highlight the first one
+-    yield highlightTab(TOOL_ID_1);
++    await highlightTab(TOOL_ID_1);
+     // to see if it has the proper class.
+-    yield checkHighlighted(TOOL_ID_1);
++    await checkHighlighted(TOOL_ID_1);
+     // Now switch back to first tool
+-    yield toolbox.selectTool(TOOL_ID_1);
++    await toolbox.selectTool(TOOL_ID_1);
+     // to check again. But there is no easy way to test if
+     // it is showing orange or not.
+-    yield checkNoHighlightWhenSelected(TOOL_ID_1);
++    await checkNoHighlightWhenSelected(TOOL_ID_1);
+     // Switch to tool 2 again
+-    yield toolbox.selectTool(TOOL_ID_2);
++    await toolbox.selectTool(TOOL_ID_2);
+     // and check again.
+-    yield checkHighlighted(TOOL_ID_1);
++    await checkHighlighted(TOOL_ID_1);
+     // Highlight another tool
+-    yield highlightTab(TOOL_ID_2);
++    await highlightTab(TOOL_ID_2);
+     // Check that both tools are highlighted.
+-    yield checkHighlighted(TOOL_ID_1);
++    await checkHighlighted(TOOL_ID_1);
+     // Check second tool being both highlighted and selected.
+-    yield checkNoHighlightWhenSelected(TOOL_ID_2);
++    await checkNoHighlightWhenSelected(TOOL_ID_2);
+     // Select tool 1
+-    yield toolbox.selectTool(TOOL_ID_1);
++    await toolbox.selectTool(TOOL_ID_1);
+     // Check second tool is still highlighted
+-    yield checkHighlighted(TOOL_ID_2);
++    await checkHighlighted(TOOL_ID_2);
+     // Unhighlight the second tool
+-    yield unhighlightTab(TOOL_ID_2);
++    await unhighlightTab(TOOL_ID_2);
+     // to see the classes gone.
+-    yield checkNoHighlight(TOOL_ID_2);
++    await checkNoHighlight(TOOL_ID_2);
+     // Now unhighlight the tool
+-    yield unhighlightTab(TOOL_ID_1);
++    await unhighlightTab(TOOL_ID_1);
+     // to see the classes gone.
+-    yield checkNoHighlight(TOOL_ID_1);
++    await checkNoHighlight(TOOL_ID_1);
+ 
+     // Now close the toolbox and exit.
+     executeSoon(() => {
+       toolbox.destroy().then(() => {
+         toolbox = null;
+         gBrowser.removeCurrentTab();
+         finish();
+       });
+     });
+-  })
++  })()
+   .catch(error => {
+     ok(false, "There was an error running the test.");
+   });
+ }
+ 
+ function highlightTab(toolId) {
+   info(`Highlighting tool ${toolId}'s tab.`);
+   return toolbox.highlightTool(toolId);
+diff --git a/devtools/client/framework/test/browser_toolbox_hosts.js b/devtools/client/framework/test/browser_toolbox_hosts.js
+--- a/devtools/client/framework/test/browser_toolbox_hosts.js
++++ b/devtools/client/framework/test/browser_toolbox_hosts.js
+@@ -6,134 +6,134 @@
+ "use strict";
+ 
+ var {Toolbox} = require("devtools/client/framework/toolbox");
+ var {SIDE, BOTTOM, WINDOW} = Toolbox.HostType;
+ var toolbox, target;
+ 
+ const URL = "data:text/html;charset=utf8,test for opening toolbox in different hosts";
+ 
+-add_task(function* runTest() {
++add_task(async function runTest() {
+   info("Create a test tab and open the toolbox");
+-  let tab = yield addTab(URL);
++  let tab = await addTab(URL);
+   target = TargetFactory.forTab(tab);
+-  toolbox = yield gDevTools.showToolbox(target, "webconsole");
++  toolbox = await gDevTools.showToolbox(target, "webconsole");
+ 
+-  yield testBottomHost();
+-  yield testSidebarHost();
+-  yield testWindowHost();
+-  yield testToolSelect();
+-  yield testDestroy();
+-  yield testRememberHost();
+-  yield testPreviousHost();
++  await testBottomHost();
++  await testSidebarHost();
++  await testWindowHost();
++  await testToolSelect();
++  await testDestroy();
++  await testRememberHost();
++  await testPreviousHost();
+ 
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+ 
+   toolbox = target = null;
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* testBottomHost() {
++function testBottomHost() {
+   checkHostType(toolbox, BOTTOM);
+ 
+   // test UI presence
+   let nbox = gBrowser.getNotificationBox();
+   let iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-bottom-iframe");
+   ok(iframe, "toolbox bottom iframe exists");
+ 
+   checkToolboxLoaded(iframe);
+ }
+ 
+-function* testSidebarHost() {
+-  yield toolbox.switchHost(SIDE);
++async function testSidebarHost() {
++  await toolbox.switchHost(SIDE);
+   checkHostType(toolbox, SIDE);
+ 
+   // test UI presence
+   let nbox = gBrowser.getNotificationBox();
+   let bottom = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-bottom-iframe");
+   ok(!bottom, "toolbox bottom iframe doesn't exist");
+ 
+   let iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-side-iframe");
+   ok(iframe, "toolbox side iframe exists");
+ 
+   checkToolboxLoaded(iframe);
+ }
+ 
+-function* testWindowHost() {
+-  yield toolbox.switchHost(WINDOW);
++async function testWindowHost() {
++  await toolbox.switchHost(WINDOW);
+   checkHostType(toolbox, WINDOW);
+ 
+   let nbox = gBrowser.getNotificationBox();
+   let sidebar = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-side-iframe");
+   ok(!sidebar, "toolbox sidebar iframe doesn't exist");
+ 
+   let win = Services.wm.getMostRecentWindow("devtools:toolbox");
+   ok(win, "toolbox separate window exists");
+ 
+   let iframe = win.document.getElementById("toolbox-iframe");
+   checkToolboxLoaded(iframe);
+ }
+ 
+-function* testToolSelect() {
++async function testToolSelect() {
+   // make sure we can load a tool after switching hosts
+-  yield toolbox.selectTool("inspector");
++  await toolbox.selectTool("inspector");
+ }
+ 
+-function* testDestroy() {
+-  yield toolbox.destroy();
++async function testDestroy() {
++  await toolbox.destroy();
+   target = TargetFactory.forTab(gBrowser.selectedTab);
+-  toolbox = yield gDevTools.showToolbox(target);
++  toolbox = await gDevTools.showToolbox(target);
+ }
+ 
+-function* testRememberHost() {
++function testRememberHost() {
+   // last host was the window - make sure it's the same when re-opening
+   is(toolbox.hostType, WINDOW, "host remembered");
+ 
+   let win = Services.wm.getMostRecentWindow("devtools:toolbox");
+   ok(win, "toolbox separate window exists");
+ }
+ 
+-function* testPreviousHost() {
++async function testPreviousHost() {
+   // last host was the window - make sure it's the same when re-opening
+   is(toolbox.hostType, WINDOW, "host remembered");
+ 
+   info("Switching to side");
+-  yield toolbox.switchHost(SIDE);
++  await toolbox.switchHost(SIDE);
+   checkHostType(toolbox, SIDE, WINDOW);
+ 
+   info("Switching to bottom");
+-  yield toolbox.switchHost(BOTTOM);
++  await toolbox.switchHost(BOTTOM);
+   checkHostType(toolbox, BOTTOM, SIDE);
+ 
+   info("Switching from bottom to side");
+-  yield toolbox.switchToPreviousHost();
++  await toolbox.switchToPreviousHost();
+   checkHostType(toolbox, SIDE, BOTTOM);
+ 
+   info("Switching from side to bottom");
+-  yield toolbox.switchToPreviousHost();
++  await toolbox.switchToPreviousHost();
+   checkHostType(toolbox, BOTTOM, SIDE);
+ 
+   info("Switching to window");
+-  yield toolbox.switchHost(WINDOW);
++  await toolbox.switchHost(WINDOW);
+   checkHostType(toolbox, WINDOW, BOTTOM);
+ 
+   info("Switching from window to bottom");
+-  yield toolbox.switchToPreviousHost();
++  await toolbox.switchToPreviousHost();
+   checkHostType(toolbox, BOTTOM, WINDOW);
+ 
+   info("Forcing the previous host to match the current (bottom)");
+   Services.prefs.setCharPref("devtools.toolbox.previousHost", BOTTOM);
+ 
+   info("Switching from bottom to side (since previous=current=bottom");
+-  yield toolbox.switchToPreviousHost();
++  await toolbox.switchToPreviousHost();
+   checkHostType(toolbox, SIDE, BOTTOM);
+ 
+   info("Forcing the previous host to match the current (side)");
+   Services.prefs.setCharPref("devtools.toolbox.previousHost", SIDE);
+   info("Switching from side to bottom (since previous=current=side");
+-  yield toolbox.switchToPreviousHost();
++  await toolbox.switchToPreviousHost();
+   checkHostType(toolbox, BOTTOM, SIDE);
+ }
+ 
+ function checkToolboxLoaded(iframe) {
+   let tabs = iframe.contentDocument.querySelector(".toolbox-tabs");
+   ok(tabs, "toolbox UI has been loaded into iframe");
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_hosts_size.js b/devtools/client/framework/test/browser_toolbox_hosts_size.js
+--- a/devtools/client/framework/test/browser_toolbox_hosts_size.js
++++ b/devtools/client/framework/test/browser_toolbox_hosts_size.js
+@@ -5,65 +5,65 @@
+ 
+ // Tests that getPanelWhenReady returns the correct panel in promise
+ // resolutions regardless of whether it has opened first.
+ 
+ const URL = "data:text/html;charset=utf8,test for host sizes";
+ 
+ var {Toolbox} = require("devtools/client/framework/toolbox");
+ 
+-add_task(function* () {
++add_task(async function () {
+   // Set size prefs to make the hosts way too big, so that the size has
+   // to be clamped to fit into the browser window.
+   Services.prefs.setIntPref("devtools.toolbox.footer.height", 10000);
+   Services.prefs.setIntPref("devtools.toolbox.sidebar.width", 10000);
+ 
+-  let tab = yield addTab(URL);
++  let tab = await addTab(URL);
+   let nbox = gBrowser.getNotificationBox();
+   let {clientHeight: nboxHeight, clientWidth: nboxWidth} = nbox;
+-  let toolbox = yield gDevTools.showToolbox(TargetFactory.forTab(tab));
++  let toolbox = await gDevTools.showToolbox(TargetFactory.forTab(tab));
+ 
+   is(nbox.clientHeight, nboxHeight, "Opening the toolbox hasn't changed the height of the nbox");
+   is(nbox.clientWidth, nboxWidth, "Opening the toolbox hasn't changed the width of the nbox");
+ 
+   let iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-bottom-iframe");
+   is(iframe.clientHeight, nboxHeight - 25, "The iframe fits within the available space");
+ 
+-  yield toolbox.switchHost(Toolbox.HostType.SIDE);
++  await toolbox.switchHost(Toolbox.HostType.SIDE);
+   iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-side-iframe");
+   iframe.style.minWidth = "1px"; // Disable the min width set in css
+   is(iframe.clientWidth, nboxWidth - 25, "The iframe fits within the available space");
+ 
+-  yield cleanup(toolbox);
++  await cleanup(toolbox);
+ });
+ 
+-add_task(function* () {
++add_task(async function () {
+   // Set size prefs to something reasonable, so we can check to make sure
+   // they are being set properly.
+   Services.prefs.setIntPref("devtools.toolbox.footer.height", 100);
+   Services.prefs.setIntPref("devtools.toolbox.sidebar.width", 100);
+ 
+-  let tab = yield addTab(URL);
++  let tab = await addTab(URL);
+   let nbox = gBrowser.getNotificationBox();
+   let {clientHeight: nboxHeight, clientWidth: nboxWidth} = nbox;
+-  let toolbox = yield gDevTools.showToolbox(TargetFactory.forTab(tab));
++  let toolbox = await gDevTools.showToolbox(TargetFactory.forTab(tab));
+ 
+   is(nbox.clientHeight, nboxHeight, "Opening the toolbox hasn't changed the height of the nbox");
+   is(nbox.clientWidth, nboxWidth, "Opening the toolbox hasn't changed the width of the nbox");
+ 
+   let iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-bottom-iframe");
+   is(iframe.clientHeight, 100, "The iframe is resized properly");
+ 
+-  yield toolbox.switchHost(Toolbox.HostType.SIDE);
++  await toolbox.switchHost(Toolbox.HostType.SIDE);
+   iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-side-iframe");
+   iframe.style.minWidth = "1px"; // Disable the min width set in css
+   is(iframe.clientWidth, 100, "The iframe is resized properly");
+ 
+-  yield cleanup(toolbox);
++  await cleanup(toolbox);
+ });
+ 
+-function* cleanup(toolbox) {
++async function cleanup(toolbox) {
+   Services.prefs.clearUserPref("devtools.toolbox.host");
+   Services.prefs.clearUserPref("devtools.toolbox.footer.height");
+   Services.prefs.clearUserPref("devtools.toolbox.sidebar.width");
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   gBrowser.removeCurrentTab();
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_keyboard_navigation.js b/devtools/client/framework/test/browser_toolbox_keyboard_navigation.js
+--- a/devtools/client/framework/test/browser_toolbox_keyboard_navigation.js
++++ b/devtools/client/framework/test/browser_toolbox_keyboard_navigation.js
+@@ -14,19 +14,19 @@ function containsFocus(aDoc, aElm) {
+   let elm = aDoc.activeElement;
+   while (elm) {
+     if (elm === aElm) { return true; }
+     elm = elm.parentNode;
+   }
+   return false;
+ }
+ 
+-add_task(function* () {
++add_task(async function () {
+   info("Create a test tab and open the toolbox");
+-  let toolbox = yield openNewTabAndToolbox(TEST_URL, "webconsole");
++  let toolbox = await openNewTabAndToolbox(TEST_URL, "webconsole");
+   let doc = toolbox.doc;
+ 
+   let toolbar = doc.querySelector(".devtools-tabbar");
+   let toolbarControls = [...toolbar.querySelectorAll(
+     ".devtools-tab, button")].filter(elm =>
+       !elm.hidden && doc.defaultView.getComputedStyle(elm).getPropertyValue(
+         "display") !== "none");
+ 
+diff --git a/devtools/client/framework/test/browser_toolbox_minimize.js b/devtools/client/framework/test/browser_toolbox_minimize.js
+--- a/devtools/client/framework/test/browser_toolbox_minimize.js
++++ b/devtools/client/framework/test/browser_toolbox_minimize.js
+@@ -9,98 +9,98 @@
+ // minimized to just the tabbar height, and maximized again.
+ // Also test that while minimized, switching to a tool, clicking on the
+ // settings, or clicking on the selected tool's tab maximizes the toolbox again.
+ // Finally test that the minimize button doesn't exist in other host types.
+ 
+ const URL = "data:text/html;charset=utf8,test page";
+ const {Toolbox} = require("devtools/client/framework/toolbox");
+ 
+-add_task(function* () {
++add_task(async function () {
+   info("Create a test tab and open the toolbox");
+-  let tab = yield addTab(URL);
++  let tab = await addTab(URL);
+   let target = TargetFactory.forTab(tab);
+-  let toolbox = yield gDevTools.showToolbox(target, "webconsole");
++  let toolbox = await gDevTools.showToolbox(target, "webconsole");
+ 
+   let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
+   ok(button, "The minimize button exists in the default bottom host");
+ 
+   info("Try to minimize the toolbox");
+-  yield minimize(toolbox);
++  await minimize(toolbox);
+   ok(parseInt(toolbox._host.frame.style.marginBottom, 10) < 0,
+      "The toolbox host has been hidden away with a negative-margin");
+ 
+   info("Try to maximize again the toolbox");
+-  yield maximize(toolbox);
++  await maximize(toolbox);
+   ok(parseInt(toolbox._host.frame.style.marginBottom, 10) == 0,
+      "The toolbox host is shown again");
+ 
+   info("Try to minimize again using the keyboard shortcut");
+-  yield minimizeWithShortcut(toolbox);
++  await minimizeWithShortcut(toolbox);
+   ok(parseInt(toolbox._host.frame.style.marginBottom, 10) < 0,
+      "The toolbox host has been hidden away with a negative-margin");
+ 
+   info("Try to maximize again using the keyboard shortcut");
+-  yield maximizeWithShortcut(toolbox);
++  await maximizeWithShortcut(toolbox);
+   ok(parseInt(toolbox._host.frame.style.marginBottom, 10) == 0,
+      "The toolbox host is shown again");
+ 
+   info("Minimize again and switch to another tool");
+-  yield minimize(toolbox);
++  await minimize(toolbox);
+   let onMaximized = toolbox._host.once("maximized");
+-  yield toolbox.selectTool("inspector");
+-  yield onMaximized;
++  await toolbox.selectTool("inspector");
++  await onMaximized;
+ 
+   info("Minimize again and click on the tab of the current tool");
+-  yield minimize(toolbox);
++  await minimize(toolbox);
+   onMaximized = toolbox._host.once("maximized");
+   let tabButton = toolbox.doc.querySelector("#toolbox-tab-inspector");
+   EventUtils.synthesizeMouseAtCenter(tabButton, {}, toolbox.win);
+-  yield onMaximized;
++  await onMaximized;
+ 
+   info("Minimize again and click on the settings tab");
+-  yield minimize(toolbox);
++  await minimize(toolbox);
+   onMaximized = toolbox._host.once("maximized");
+   let settingsButton = toolbox.doc.querySelector("#toolbox-tab-options");
+   EventUtils.synthesizeMouseAtCenter(settingsButton, {}, toolbox.win);
+-  yield onMaximized;
++  await onMaximized;
+ 
+   info("Switch to a different host");
+-  yield toolbox.switchHost(Toolbox.HostType.SIDE);
++  await toolbox.switchHost(Toolbox.HostType.SIDE);
+   button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
+   ok(!button, "The minimize button doesn't exist in the side host");
+ 
+   Services.prefs.clearUserPref("devtools.toolbox.host");
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* minimize(toolbox) {
++async function minimize(toolbox) {
+   let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
+   let onMinimized = toolbox._host.once("minimized");
+   EventUtils.synthesizeMouseAtCenter(button, {}, toolbox.win);
+-  yield onMinimized;
++  await onMinimized;
+ }
+ 
+-function* minimizeWithShortcut(toolbox) {
++async function minimizeWithShortcut(toolbox) {
+   let key = toolbox.doc.getElementById("toolbox-minimize-key")
+                        .getAttribute("key");
+   let onMinimized = toolbox._host.once("minimized");
+   EventUtils.synthesizeKey(key, {accelKey: true, shiftKey: true},
+                            toolbox.win);
+-  yield onMinimized;
++  await onMinimized;
+ }
+ 
+-function* maximize(toolbox) {
++async function maximize(toolbox) {
+   let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
+   let onMaximized = toolbox._host.once("maximized");
+   EventUtils.synthesizeMouseAtCenter(button, {}, toolbox.win);
+-  yield onMaximized;
++  await onMaximized;
+ }
+ 
+-function* maximizeWithShortcut(toolbox) {
++async function maximizeWithShortcut(toolbox) {
+   let key = toolbox.doc.getElementById("toolbox-minimize-key")
+                        .getAttribute("key");
+   let onMaximized = toolbox._host.once("maximized");
+   EventUtils.synthesizeKey(key, {accelKey: true, shiftKey: true},
+                            toolbox.win);
+-  yield onMaximized;
++  await onMaximized;
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_options.js b/devtools/client/framework/test/browser_toolbox_options.js
+--- a/devtools/client/framework/test/browser_toolbox_options.js
++++ b/devtools/client/framework/test/browser_toolbox_options.js
+@@ -9,30 +9,30 @@
+ // Tests that changing preferences in the options panel updates the prefs
+ // and toggles appropriate things in the toolbox.
+ 
+ var doc = null, toolbox = null, panelWin = null, modifiedPrefs = [];
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+ const {PrefObserver} = require("devtools/client/shared/prefs");
+ 
+-add_task(function* () {
++add_task(async function () {
+   const URL = "data:text/html;charset=utf8,test for dynamically registering " +
+               "and unregistering tools";
+   registerNewTool();
+-  let tab = yield addTab(URL);
++  let tab = await addTab(URL);
+   let target = TargetFactory.forTab(tab);
+-  toolbox = yield gDevTools.showToolbox(target);
++  toolbox = await gDevTools.showToolbox(target);
+   doc = toolbox.doc;
+-  yield registerNewPerToolboxTool();
+-  yield testSelectTool();
+-  yield testOptionsShortcut();
+-  yield testOptions();
+-  yield testToggleTools();
+-  yield cleanup();
++  await registerNewPerToolboxTool();
++  await testSelectTool();
++  await testOptionsShortcut();
++  await testOptions();
++  await testToggleTools();
++  await cleanup();
+ });
+ 
+ function registerNewTool() {
+   let toolDefinition = {
+     id: "test-tool",
+     isTargetSupported: () => true,
+     visibilityswitch: "devtools.test-tool.enabled",
+     url: "about:blank",
+@@ -68,74 +68,74 @@ function registerNewPerToolboxTool() {
+   toolbox.addAdditionalTool(toolDefinition);
+ 
+   ok(!gDevTools.getToolDefinitionMap().has("test-pertoolbox-tool"),
+      "The per-toolbox tool is not registered globally");
+   ok(toolbox.hasAdditionalTool("test-pertoolbox-tool"),
+      "The per-toolbox tool has been registered to the toolbox");
+ }
+ 
+-function* testSelectTool() {
++async function testSelectTool() {
+   info("Checking to make sure that the options panel can be selected.");
+ 
+   let onceSelected = toolbox.once("options-selected");
+   toolbox.selectTool("options");
+-  yield onceSelected;
++  await onceSelected;
+   ok(true, "Toolbox selected via selectTool method");
+ }
+ 
+-function* testOptionsShortcut() {
++async function testOptionsShortcut() {
+   info("Selecting another tool, then reselecting options panel with keyboard.");
+ 
+-  yield toolbox.selectTool("webconsole");
++  await toolbox.selectTool("webconsole");
+   is(toolbox.currentToolId, "webconsole", "webconsole is selected");
+   synthesizeKeyShortcut(L10N.getStr("toolbox.options.key"));
+   is(toolbox.currentToolId, "options", "Toolbox selected via shortcut key (1)");
+   synthesizeKeyShortcut(L10N.getStr("toolbox.options.key"));
+   is(toolbox.currentToolId, "webconsole", "webconsole is selected (1)");
+ 
+-  yield toolbox.selectTool("webconsole");
++  await toolbox.selectTool("webconsole");
+   is(toolbox.currentToolId, "webconsole", "webconsole is selected");
+   synthesizeKeyShortcut(L10N.getStr("toolbox.help.key"));
+   is(toolbox.currentToolId, "options", "Toolbox selected via shortcut key (2)");
+   synthesizeKeyShortcut(L10N.getStr("toolbox.options.key"));
+   is(toolbox.currentToolId, "webconsole", "webconsole is reselected (2)");
+   synthesizeKeyShortcut(L10N.getStr("toolbox.help.key"));
+   is(toolbox.currentToolId, "options", "Toolbox selected via shortcut key (2)");
+ }
+ 
+-function* testOptions() {
++async function testOptions() {
+   let tool = toolbox.getPanel("options");
+   panelWin = tool.panelWin;
+   let prefNodes = tool.panelDoc.querySelectorAll(
+     "input[type=checkbox][data-pref]");
+ 
+   // Store modified pref names so that they can be cleared on error.
+   for (let node of tool.panelDoc.querySelectorAll("[data-pref]")) {
+     let pref = node.getAttribute("data-pref");
+     modifiedPrefs.push(pref);
+   }
+ 
+   for (let node of prefNodes) {
+     let prefValue = GetPref(node.getAttribute("data-pref"));
+ 
+     // Test clicking the checkbox for each options pref
+-    yield testMouseClick(node, prefValue);
++    await testMouseClick(node, prefValue);
+ 
+     // Do again with opposite values to reset prefs
+-    yield testMouseClick(node, !prefValue);
++    await testMouseClick(node, !prefValue);
+   }
+ 
+   let prefSelects = tool.panelDoc.querySelectorAll("select[data-pref]");
+   for (let node of prefSelects) {
+-    yield testSelect(node);
++    await testSelect(node);
+   }
+ }
+ 
+-function* testSelect(select) {
++async function testSelect(select) {
+   let pref = select.getAttribute("data-pref");
+   let options = Array.from(select.options);
+   info("Checking select for: " + pref);
+ 
+   is(select.options[select.selectedIndex].value, GetPref(pref),
+     "select starts out selected");
+ 
+   for (let option of options) {
+@@ -152,24 +152,24 @@ function* testSelect(select) {
+       is(GetPref(pref), option.value, "Preference been switched for " + pref);
+       deferred.resolve();
+     });
+ 
+     select.selectedIndex = options.indexOf(option);
+     let changeEvent = new Event("change");
+     select.dispatchEvent(changeEvent);
+ 
+-    yield deferred.promise;
++    await deferred.promise;
+ 
+     ok(changeSeen, "Correct pref was changed");
+     observer.destroy();
+   }
+ }
+ 
+-function* testMouseClick(node, prefValue) {
++async function testMouseClick(node, prefValue) {
+   let deferred = defer();
+ 
+   let observer = new PrefObserver("devtools.");
+ 
+   let pref = node.getAttribute("data-pref");
+   let changeSeen = false;
+   observer.once(pref, () => {
+     changeSeen = true;
+@@ -181,23 +181,23 @@ function* testMouseClick(node, prefValue
+ 
+   // We use executeSoon here to ensure that the element is in view and
+   // clickable.
+   executeSoon(function () {
+     info("Click event synthesized for pref " + pref);
+     EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
+   });
+ 
+-  yield deferred.promise;
++  await deferred.promise;
+ 
+   ok(changeSeen, "Correct pref was changed");
+   observer.destroy();
+ }
+ 
+-function* testToggleTools() {
++async function testToggleTools() {
+   let toolNodes = panelWin.document.querySelectorAll(
+     "#default-tools-box input[type=checkbox]:not([data-unsupported])," +
+     "#additional-tools-box input[type=checkbox]:not([data-unsupported])");
+   let enabledTools = [...toolNodes].filter(node => node.checked);
+ 
+   let toggleableTools = gDevTools.getDefaultTools()
+                                  .filter(tool => {
+                                    return tool.visibilityswitch;
+@@ -215,62 +215,62 @@ function* testToggleTools() {
+   // Store modified pref names so that they can be cleared on error.
+   for (let tool of toggleableTools) {
+     let pref = tool.visibilityswitch;
+     modifiedPrefs.push(pref);
+   }
+ 
+   // Toggle each tool
+   for (let node of toolNodes) {
+-    yield toggleTool(node);
++    await toggleTool(node);
+   }
+   // Toggle again to reset tool enablement state
+   for (let node of toolNodes) {
+-    yield toggleTool(node);
++    await toggleTool(node);
+   }
+ 
+   // Test that a tool can still be added when no tabs are present:
+   // Disable all tools
+   for (let node of enabledTools) {
+-    yield toggleTool(node);
++    await toggleTool(node);
+   }
+   // Re-enable the tools which are enabled by default
+   for (let node of enabledTools) {
+-    yield toggleTool(node);
++    await toggleTool(node);
+   }
+ 
+   // Toggle first, middle, and last tools to ensure that toolbox tabs are
+   // inserted in order
+   let firstTool = toolNodes[0];
+   let middleTool = toolNodes[(toolNodes.length / 2) | 0];
+   let lastTool = toolNodes[toolNodes.length - 1];
+ 
+-  yield toggleTool(firstTool);
+-  yield toggleTool(firstTool);
+-  yield toggleTool(middleTool);
+-  yield toggleTool(middleTool);
+-  yield toggleTool(lastTool);
+-  yield toggleTool(lastTool);
++  await toggleTool(firstTool);
++  await toggleTool(firstTool);
++  await toggleTool(middleTool);
++  await toggleTool(middleTool);
++  await toggleTool(lastTool);
++  await toggleTool(lastTool);
+ }
+ 
+-function* toggleTool(node) {
++async function toggleTool(node) {
+   let deferred = defer();
+ 
+   let toolId = node.getAttribute("id");
+   if (node.checked) {
+     gDevTools.once("tool-unregistered",
+       checkUnregistered.bind(null, toolId, deferred));
+   } else {
+     gDevTools.once("tool-registered",
+       checkRegistered.bind(null, toolId, deferred));
+   }
+   node.scrollIntoView();
+   EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
+ 
+-  yield deferred.promise;
++  await deferred.promise;
+ }
+ 
+ function checkUnregistered(toolId, deferred, event, data) {
+   if (data == toolId) {
+     ok(true, "Correct tool removed");
+     // checking tab on the toolbox
+     ok(!doc.getElementById("toolbox-tab-" + toolId),
+       "Tab removed for " + toolId);
+@@ -301,17 +301,17 @@ function GetPref(name) {
+       return Services.prefs.getIntPref(name);
+     case Services.prefs.PREF_BOOL:
+       return Services.prefs.getBoolPref(name);
+     default:
+       throw new Error("Unknown type");
+   }
+ }
+ 
+-function* cleanup() {
++async function cleanup() {
+   gDevTools.unregisterTool("test-tool");
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   gBrowser.removeCurrentTab();
+   for (let pref of modifiedPrefs) {
+     Services.prefs.clearUserPref(pref);
+   }
+   toolbox = doc = panelWin = modifiedPrefs = null;
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_options_disable_cache-01.js b/devtools/client/framework/test/browser_toolbox_options_disable_cache-01.js
+--- a/devtools/client/framework/test/browser_toolbox_options_disable_cache-01.js
++++ b/devtools/client/framework/test/browser_toolbox_options_disable_cache-01.js
+@@ -6,32 +6,32 @@
+ "use strict";
+ 
+ requestLongerTimeout(2);
+ 
+ // Tests that disabling the cache for a tab works as it should when toolboxes
+ // are not toggled.
+ loadHelperScript("helper_disable_cache.js");
+ 
+-add_task(function* () {
++add_task(async function () {
+   // Disable rcwn to make cache behavior deterministic.
+-  yield pushPref("network.http.rcwn.enabled", false);
++  await pushPref("network.http.rcwn.enabled", false);
+ 
+   // Ensure that the setting is cleared after the test.
+   registerCleanupFunction(() => {
+     info("Resetting devtools.cache.disabled to false.");
+     Services.prefs.setBoolPref("devtools.cache.disabled", false);
+   });
+ 
+   // Initialise tabs: 1 and 2 with a toolbox, 3 and 4 without.
+   for (let tab of tabs) {
+-    yield initTab(tab, tab.startToolbox);
++    await initTab(tab, tab.startToolbox);
+   }
+ 
+   // Ensure cache is enabled for all tabs.
+-  yield checkCacheStateForAllTabs([true, true, true, true]);
++  await checkCacheStateForAllTabs([true, true, true, true]);
+ 
+   // Check the checkbox in tab 0 and ensure cache is disabled for tabs 0 and 1.
+-  yield setDisableCacheCheckboxChecked(tabs[0], true);
+-  yield checkCacheStateForAllTabs([false, false, true, true]);
++  await setDisableCacheCheckboxChecked(tabs[0], true);
++  await checkCacheStateForAllTabs([false, false, true, true]);
+ 
+-  yield finishUp();
++  await finishUp();
+ });
+diff --git a/devtools/client/framework/test/browser_toolbox_options_disable_cache-02.js b/devtools/client/framework/test/browser_toolbox_options_disable_cache-02.js
+--- a/devtools/client/framework/test/browser_toolbox_options_disable_cache-02.js
++++ b/devtools/client/framework/test/browser_toolbox_options_disable_cache-02.js
+@@ -6,45 +6,45 @@
+ "use strict";
+ 
+ requestLongerTimeout(2);
+ 
+ // Tests that disabling the cache for a tab works as it should when toolboxes
+ // are toggled.
+ loadHelperScript("helper_disable_cache.js");
+ 
+-add_task(function* () {
++add_task(async function () {
+   // Disable rcwn to make cache behavior deterministic.
+-  yield pushPref("network.http.rcwn.enabled", false);
++  await pushPref("network.http.rcwn.enabled", false);
+ 
+   // Ensure that the setting is cleared after the test.
+   registerCleanupFunction(() => {
+     info("Resetting devtools.cache.disabled to false.");
+     Services.prefs.setBoolPref("devtools.cache.disabled", false);
+   });
+ 
+   // Initialise tabs: 1 and 2 with a toolbox, 3 and 4 without.
+   for (let tab of tabs) {
+-    yield initTab(tab, tab.startToolbox);
++    await initTab(tab, tab.startToolbox);
+   }
+ 
+   // Disable cache in tab 0
+-  yield setDisableCacheCheckboxChecked(tabs[0], true);
++  await setDisableCacheCheckboxChecked(tabs[0], true);
+ 
+   // Open toolbox in tab 2 and ensure the cache is then disabled.
+-  tabs[2].toolbox = yield gDevTools.showToolbox(tabs[2].target, "options");
+-  yield checkCacheEnabled(tabs[2], false);
++  tabs[2].toolbox = await gDevTools.showToolbox(tabs[2].target, "options");
++  await checkCacheEnabled(tabs[2], false);
+ 
+   // Close toolbox in tab 2 and ensure the cache is enabled again
+-  yield tabs[2].toolbox.destroy();
++  await tabs[2].toolbox.destroy();
+   tabs[2].target = TargetFactory.forTab(tabs[2].tab);
+-  yield checkCacheEnabled(tabs[2], true);
++  await checkCacheEnabled(tabs[2], true);
+ 
+   // Open toolbox in tab 2 and ensure the cache is then disabled.
+-  tabs[2].toolbox = yield gDevTools.showToolbox(tabs[2].target, "options");
+-  yield checkCacheEnabled(tabs[2], false);
++  tabs[2].toolbox = await gDevTools.showToolbox(tabs[2].target, "options");
++  await checkCacheEnabled(tabs[2], false);
+ 
+   // Check the checkbox in tab 2 and ensure cache is enabled for all tabs.
+-  yield setDisableCacheCheckboxChecked(tabs[2], false);
+-  yield checkCacheStateForAllTabs([true, true, true, true]);
++  await setDisableCacheCheckboxChecked(tabs[2], false);
++  await checkCacheStateForAllTabs([true, true, true, true]);
+ 
+-  yield finishUp();
++  await finishUp();
+ });
+diff --git a/devtools/client/framework/test/browser_toolbox_options_disable_js.js b/devtools/client/framework/test/browser_toolbox_options_disable_js.js
+--- a/devtools/client/framework/test/browser_toolbox_options_disable_js.js
++++ b/devtools/client/framework/test/browser_toolbox_options_disable_js.js
+@@ -14,98 +14,98 @@ function test() {
+   });
+ }
+ 
+ function testSelectTool(toolbox) {
+   toolbox.once("options-selected", () => testToggleJS(toolbox));
+   toolbox.selectTool("options");
+ }
+ 
+-let testToggleJS = Task.async(function* (toolbox) {
++let testToggleJS = async function (toolbox) {
+   ok(true, "Toolbox selected via selectTool method");
+ 
+-  yield testJSEnabled();
+-  yield testJSEnabledIframe();
++  await testJSEnabled();
++  await testJSEnabledIframe();
+ 
+   // Disable JS.
+-  yield toggleJS(toolbox);
++  await toggleJS(toolbox);
+ 
+-  yield testJSDisabled();
+-  yield testJSDisabledIframe();
++  await testJSDisabled();
++  await testJSDisabledIframe();
+ 
+   // Re-enable JS.
+-  yield toggleJS(toolbox);
++  await toggleJS(toolbox);
+ 
+-  yield testJSEnabled();
+-  yield testJSEnabledIframe();
++  await testJSEnabled();
++  await testJSEnabledIframe();
+ 
+   finishUp(toolbox);
+-});
++};
+ 
+-function* testJSEnabled() {
++async function testJSEnabled() {
+   info("Testing that JS is enabled");
+ 
+   // We use waitForTick here because switching docShell.allowJavascript to true
+   // takes a while to become live.
+-  yield waitForTick();
++  await waitForTick();
+ 
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
++  await ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+     let doc = content.document;
+     let output = doc.getElementById("output");
+     doc.querySelector("#logJSEnabled").click();
+     is(output.textContent, "JavaScript Enabled", 'Output is "JavaScript Enabled"');
+   });
+ }
+ 
+-function* testJSEnabledIframe() {
++async function testJSEnabledIframe() {
+   info("Testing that JS is enabled in the iframe");
+ 
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
++  await ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+     let doc = content.document;
+     let iframe = doc.querySelector("iframe");
+     let iframeDoc = iframe.contentDocument;
+     let output = iframeDoc.getElementById("output");
+     iframeDoc.querySelector("#logJSEnabled").click();
+     is(output.textContent, "JavaScript Enabled",
+                             'Output is "JavaScript Enabled" in iframe');
+   });
+ }
+ 
+-function* toggleJS(toolbox) {
++async function toggleJS(toolbox) {
+   let panel = toolbox.getCurrentPanel();
+   let cbx = panel.panelDoc.getElementById("devtools-disable-javascript");
+ 
+   if (cbx.checked) {
+     info("Clearing checkbox to re-enable JS");
+   } else {
+     info("Checking checkbox to disable JS");
+   }
+ 
+   let browserLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+   cbx.click();
+-  yield browserLoaded;
++  await browserLoaded;
+ }
+ 
+-function* testJSDisabled() {
++async function testJSDisabled() {
+   info("Testing that JS is disabled");
+ 
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
++  await ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+     let doc = content.document;
+     let output = doc.getElementById("output");
+     doc.querySelector("#logJSDisabled").click();
+ 
+     ok(output.textContent !== "JavaScript Disabled",
+        'output is not "JavaScript Disabled"');
+   });
+ }
+ 
+-function* testJSDisabledIframe() {
++async function testJSDisabledIframe() {
+   info("Testing that JS is disabled in the iframe");
+ 
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
++  await ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+     let doc = content.document;
+     let iframe = doc.querySelector("iframe");
+     let iframeDoc = iframe.contentDocument;
+     let output = iframeDoc.getElementById("output");
+     iframeDoc.querySelector("#logJSDisabled").click();
+     ok(output.textContent !== "JavaScript Disabled",
+        'output is not "JavaScript Disabled" in iframe');
+   });
+diff --git a/devtools/client/framework/test/browser_toolbox_races.js b/devtools/client/framework/test/browser_toolbox_races.js
+--- a/devtools/client/framework/test/browser_toolbox_races.js
++++ b/devtools/client/framework/test/browser_toolbox_races.js
+@@ -8,22 +8,22 @@
+ // Toggling the toolbox three time can take more than 45s on slow test machine
+ requestLongerTimeout(2);
+ 
+ // Test toggling the toolbox quickly and see if there is any race breaking it.
+ 
+ const URL = "data:text/html;charset=utf-8,Toggling devtools quickly";
+ const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser");
+ 
+-add_task(function* () {
++add_task(async function () {
+   // Make sure this test starts with the selectedTool pref cleared. Previous
+   // tests select various tools, and that sets this pref.
+   Services.prefs.clearUserPref("devtools.toolbox.selectedTool");
+ 
+-  let tab = yield addTab(URL);
++  let tab = await addTab(URL);
+ 
+   let created = 0, ready = 0, destroy = 0, destroyed = 0;
+   let onCreated = () => {
+     created++;
+   };
+   let onReady = () => {
+     ready++;
+   };
+@@ -46,30 +46,30 @@ add_task(function* () {
+   info("Trying to toggle the toolbox 3 times");
+   while (created < 3) {
+     // Sent multiple event to try to race the code during toolbox creation and destruction
+     toggle();
+     toggle();
+     toggle();
+ 
+     // Release the event loop to let a chance to actually create or destroy the toolbox!
+-    yield wait(50);
++    await wait(50);
+   }
+   info("Toggled the toolbox 3 times");
+ 
+   // Now wait for the 3rd toolbox to be fully ready before closing it.
+   // We close the last toolbox manually, out of the first while() loop to
+   // avoid races and be sure we end up we no toolbox and waited for all the
+   // requests to be done.
+   while (ready != 3) {
+-    yield wait(100);
++    await wait(100);
+   }
+   toggle();
+   while (destroyed != 3) {
+-    yield wait(100);
++    await wait(100);
+   }
+ 
+   is(created, 3, "right number of created events");
+   is(ready, 3, "right number of ready events");
+   is(destroy, 3, "right number of destroy events");
+   is(destroyed, 3, "right number of destroyed events");
+ 
+   gDevTools.off("toolbox-created", onCreated);
+diff --git a/devtools/client/framework/test/browser_toolbox_ready.js b/devtools/client/framework/test/browser_toolbox_ready.js
+--- a/devtools/client/framework/test/browser_toolbox_ready.js
++++ b/devtools/client/framework/test/browser_toolbox_ready.js
+@@ -1,21 +1,21 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ const TEST_URL = "data:text/html,test for toolbox being ready";
+ 
+-add_task(function* () {
+-  let tab = yield addTab(TEST_URL);
++add_task(async function () {
++  let tab = await addTab(TEST_URL);
+   let target = TargetFactory.forTab(tab);
+ 
+-  const toolbox = yield gDevTools.showToolbox(target, "webconsole");
++  const toolbox = await gDevTools.showToolbox(target, "webconsole");
+   ok(toolbox.isReady, "toolbox isReady is set");
+   ok(toolbox.threadClient, "toolbox has a thread client");
+ 
+-  const toolbox2 = yield gDevTools.showToolbox(toolbox.target, toolbox.toolId);
++  const toolbox2 = await gDevTools.showToolbox(toolbox.target, toolbox.toolId);
+   is(toolbox2, toolbox, "same toolbox");
+ 
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/framework/test/browser_toolbox_remoteness_change.js b/devtools/client/framework/test/browser_toolbox_remoteness_change.js
+--- a/devtools/client/framework/test/browser_toolbox_remoteness_change.js
++++ b/devtools/client/framework/test/browser_toolbox_remoteness_change.js
+@@ -2,42 +2,42 @@
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ var {Toolbox} = require("devtools/client/framework/toolbox");
+ 
+ const URL_1 = "about:robots";
+ const URL_2 = "data:text/html;charset=UTF-8," +
+   encodeURIComponent("<div id=\"remote-page\">foo</div>");
+ 
+-add_task(function* () {
++add_task(async function () {
+   info("Open a tab on a URL supporting only running in parent process");
+-  let tab = yield addTab(URL_1);
++  let tab = await addTab(URL_1);
+   is(tab.linkedBrowser.currentURI.spec, URL_1, "We really are on the expected document");
+   is(tab.linkedBrowser.getAttribute("remote"), "", "And running in parent process");
+ 
+-  let toolbox = yield openToolboxForTab(tab);
++  let toolbox = await openToolboxForTab(tab);
+ 
+   let onToolboxDestroyed = toolbox.once("destroyed");
+   let onToolboxCreated = gDevTools.once("toolbox-created");
+ 
+   info("Navigate to a URL supporting remote process");
+   let onLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+   gBrowser.loadURI(URL_2);
+-  yield onLoaded;
++  await onLoaded;
+ 
+   is(tab.linkedBrowser.getAttribute("remote"), "true", "Navigated to a data: URI and switching to remote");
+ 
+   info("Waiting for the toolbox to be destroyed");
+-  yield onToolboxDestroyed;
++  await onToolboxDestroyed;
+ 
+   info("Waiting for a new toolbox to be created");
+-  toolbox = yield onToolboxCreated;
++  toolbox = await onToolboxCreated;
+ 
+   info("Waiting for the new toolbox to be ready");
+-  yield toolbox.once("ready");
++  await toolbox.once("ready");
+ 
+   info("Veryify we are inspecting the new document");
+-  let console = yield toolbox.selectTool("webconsole");
++  let console = await toolbox.selectTool("webconsole");
+   let { jsterm } = console.hud;
+-  let url = yield jsterm.execute("document.location.href");
++  let url = await jsterm.execute("document.location.href");
+   // Uses includes as the old console frontend prints a timestamp
+   ok(url.textContent.includes(URL_2), "The console inspects the second document");
+ });
+diff --git a/devtools/client/framework/test/browser_toolbox_select_event.js b/devtools/client/framework/test/browser_toolbox_select_event.js
+--- a/devtools/client/framework/test/browser_toolbox_select_event.js
++++ b/devtools/client/framework/test/browser_toolbox_select_event.js
+@@ -4,80 +4,80 @@
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const PAGE_URL = "data:text/html;charset=utf-8,test select events";
+ 
+ requestLongerTimeout(2);
+ 
+-add_task(function* () {
+-  let tab = yield addTab(PAGE_URL);
++add_task(async function () {
++  let tab = await addTab(PAGE_URL);
+ 
+-  let toolbox = yield openToolboxForTab(tab, "webconsole", "bottom");
+-  yield testSelectEvent("inspector");
+-  yield testSelectEvent("webconsole");
+-  yield testSelectEvent("styleeditor");
+-  yield testSelectEvent("inspector");
+-  yield testSelectEvent("webconsole");
+-  yield testSelectEvent("styleeditor");
++  let toolbox = await openToolboxForTab(tab, "webconsole", "bottom");
++  await testSelectEvent("inspector");
++  await testSelectEvent("webconsole");
++  await testSelectEvent("styleeditor");
++  await testSelectEvent("inspector");
++  await testSelectEvent("webconsole");
++  await testSelectEvent("styleeditor");
+ 
+-  yield testToolSelectEvent("inspector");
+-  yield testToolSelectEvent("webconsole");
+-  yield testToolSelectEvent("styleeditor");
+-  yield toolbox.destroy();
++  await testToolSelectEvent("inspector");
++  await testToolSelectEvent("webconsole");
++  await testToolSelectEvent("styleeditor");
++  await toolbox.destroy();
+ 
+-  toolbox = yield openToolboxForTab(tab, "webconsole", "side");
+-  yield testSelectEvent("inspector");
+-  yield testSelectEvent("webconsole");
+-  yield testSelectEvent("styleeditor");
+-  yield testSelectEvent("inspector");
+-  yield testSelectEvent("webconsole");
+-  yield testSelectEvent("styleeditor");
+-  yield toolbox.destroy();
++  toolbox = await openToolboxForTab(tab, "webconsole", "side");
++  await testSelectEvent("inspector");
++  await testSelectEvent("webconsole");
++  await testSelectEvent("styleeditor");
++  await testSelectEvent("inspector");
++  await testSelectEvent("webconsole");
++  await testSelectEvent("styleeditor");
++  await toolbox.destroy();
+ 
+-  toolbox = yield openToolboxForTab(tab, "webconsole", "window");
+-  yield testSelectEvent("inspector");
+-  yield testSelectEvent("webconsole");
+-  yield testSelectEvent("styleeditor");
+-  yield testSelectEvent("inspector");
+-  yield testSelectEvent("webconsole");
+-  yield testSelectEvent("styleeditor");
+-  yield toolbox.destroy();
++  toolbox = await openToolboxForTab(tab, "webconsole", "window");
++  await testSelectEvent("inspector");
++  await testSelectEvent("webconsole");
++  await testSelectEvent("styleeditor");
++  await testSelectEvent("inspector");
++  await testSelectEvent("webconsole");
++  await testSelectEvent("styleeditor");
++  await toolbox.destroy();
+ 
+-  yield testSelectToolRace();
++  await testSelectToolRace();
+ 
+   /**
+    * Assert that selecting the given toolId raises a select event
+    * @param {toolId} Id of the tool to test
+    */
+-  function* testSelectEvent(toolId) {
++  async function testSelectEvent(toolId) {
+     let onSelect = toolbox.once("select");
+     toolbox.selectTool(toolId);
+-    let id = yield onSelect;
++    let id = await onSelect;
+     is(id, toolId, toolId + " selected");
+   }
+ 
+   /**
+    * Assert that selecting the given toolId raises its corresponding
+    * selected event
+    * @param {toolId} Id of the tool to test
+    */
+-  function* testToolSelectEvent(toolId) {
++  async function testToolSelectEvent(toolId) {
+     let onSelected = toolbox.once(toolId + "-selected");
+     toolbox.selectTool(toolId);
+-    yield onSelected;
++    await onSelected;
+     is(toolbox.currentToolId, toolId, toolId + " tool selected");
+   }
+ 
+   /**
+    * Assert that two calls to selectTool won't race
+    */
+-  function* testSelectToolRace() {
+-    let toolbox = yield openToolboxForTab(tab, "webconsole");
++  async function testSelectToolRace() {
++    let toolbox = await openToolboxForTab(tab, "webconsole");
+     let selected = false;
+     let onSelect = (event, id) => {
+       if (selected) {
+         ok(false, "Got more than one 'select' event");
+       } else {
+         selected = true;
+       }
+     };
+@@ -87,15 +87,15 @@ add_task(function* () {
+     // Check that both promises don't resolve too early
+     let checkSelectToolResolution = panel => {
+       ok(selected, "selectTool resolves only after 'select' event is fired");
+       let inspector = toolbox.getPanel("inspector");
+       is(panel, inspector, "selecTool resolves to the panel instance");
+     };
+     p1.then(checkSelectToolResolution);
+     p2.then(checkSelectToolResolution);
+-    yield p1;
+-    yield p2;
++    await p1;
++    await p2;
+ 
+-    yield toolbox.destroy();
++    await toolbox.destroy();
+   }
+ });
+ 
+diff --git a/devtools/client/framework/test/browser_toolbox_selected_tool_unavailable.js b/devtools/client/framework/test/browser_toolbox_selected_tool_unavailable.js
+--- a/devtools/client/framework/test/browser_toolbox_selected_tool_unavailable.js
++++ b/devtools/client/framework/test/browser_toolbox_selected_tool_unavailable.js
+@@ -20,29 +20,29 @@ const testToolDefinition = {
+         toolbox: toolbox,
+         isReady: true,
+         destroy: () => {},
+         panelDoc: iframeWindow.document
+       };
+     }
+ };
+ 
+-add_task(function* () {
++add_task(async function () {
+   gDevTools.registerTool(testToolDefinition);
+-  let tab = yield addTab("about:blank");
++  let tab = await addTab("about:blank");
+   let target = TargetFactory.forTab(tab);
+ 
+-  let toolbox = yield gDevTools.showToolbox(target, testToolDefinition.id);
++  let toolbox = await gDevTools.showToolbox(target, testToolDefinition.id);
+   is(toolbox.currentToolId, "test-tool", "test-tool was selected");
+-  yield gDevTools.closeToolbox(target);
++  await gDevTools.closeToolbox(target);
+ 
+   // Make the previously selected tool unavailable.
+   testToolDefinition.isTargetSupported = () => false;
+ 
+   target = TargetFactory.forTab(tab);
+-  toolbox = yield gDevTools.showToolbox(target);
++  toolbox = await gDevTools.showToolbox(target);
+   is(toolbox.currentToolId, "webconsole", "web console was selected");
+ 
+-  yield gDevTools.closeToolbox(target);
++  await gDevTools.closeToolbox(target);
+   gDevTools.unregisterTool(testToolDefinition.id);
+   tab = toolbox = target = null;
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/framework/test/browser_toolbox_selectionchanged_event.js b/devtools/client/framework/test/browser_toolbox_selectionchanged_event.js
+--- a/devtools/client/framework/test/browser_toolbox_selectionchanged_event.js
++++ b/devtools/client/framework/test/browser_toolbox_selectionchanged_event.js
+@@ -2,40 +2,40 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const PAGE_URL = "data:text/html;charset=utf-8,<body><div></div></body>";
+ 
+-add_task(function* () {
+-  let tab = yield addTab(PAGE_URL);
+-  let toolbox = yield openToolboxForTab(tab, "inspector", "bottom");
++add_task(async function () {
++  let tab = await addTab(PAGE_URL);
++  let toolbox = await openToolboxForTab(tab, "inspector", "bottom");
+   let inspector = toolbox.getCurrentPanel();
+ 
+-  let root = yield inspector.walker.getRootNode();
+-  let body = yield inspector.walker.querySelector(root, "body");
+-  let node = yield inspector.walker.querySelector(root, "div");
++  let root = await inspector.walker.getRootNode();
++  let body = await inspector.walker.querySelector(root, "body");
++  let node = await inspector.walker.querySelector(root, "div");
+ 
+   is(inspector.selection.nodeFront, body, "Body is selected by default");
+ 
+   // Listen to selection changed
+   let onSelectionChanged = toolbox.once("selection-changed");
+ 
+   info("Select the div and wait for the selection-changed event to be fired.");
+   inspector.selection.setNodeFront(node, "browser-context-menu");
+ 
+-  yield onSelectionChanged;
++  await onSelectionChanged;
+ 
+   is(inspector.selection.nodeFront, node, "Div is now selected");
+ 
+   // Listen to cleared selection changed
+   let onClearSelectionChanged = toolbox.once("selection-changed");
+ 
+   info("Clear the selection and wait for the selection-changed event to be fired.");
+   inspector.selection.setNodeFront(undefined, "browser-context-menu");
+ 
+-  yield onClearSelectionChanged;
++  await onClearSelectionChanged;
+ 
+   is(inspector.selection.nodeFront, undefined, "The selection is undefined as expected");
+ });
+ 
+diff --git a/devtools/client/framework/test/browser_toolbox_sidebar_existing_tabs.js b/devtools/client/framework/test/browser_toolbox_sidebar_existing_tabs.js
+--- a/devtools/client/framework/test/browser_toolbox_sidebar_existing_tabs.js
++++ b/devtools/client/framework/test/browser_toolbox_sidebar_existing_tabs.js
+@@ -20,23 +20,23 @@ const testToolDefinition = {
+       toolbox: toolbox,
+       isReady: true,
+       destroy: () => {},
+       panelDoc: iframeWindow.document,
+     });
+   }
+ };
+ 
+-add_task(function* () {
+-  let tab = yield addTab("about:blank");
++add_task(async function () {
++  let tab = await addTab("about:blank");
+ 
+   let target = TargetFactory.forTab(tab);
+ 
+   gDevTools.registerTool(testToolDefinition);
+-  let toolbox = yield gDevTools.showToolbox(target, testToolDefinition.id);
++  let toolbox = await gDevTools.showToolbox(target, testToolDefinition.id);
+ 
+   let toolPanel = toolbox.getPanel(testToolDefinition.id);
+   let tabbox = toolPanel.panelDoc.getElementById("sidebar");
+ 
+   info("Creating the sidebar widget");
+   let sidebar = new ToolSidebar(tabbox, toolPanel, "bug1101569");
+ 
+   info("Checking that existing tabs have been registered");
+@@ -58,12 +58,12 @@ add_task(function* () {
+   is(sidebar.getCurrentTabID(), "tab1", "getCurrentTabID returns the expected id");
+ 
+   info("Removing a tab");
+   sidebar.removeTab("tab2", "tabpanel2");
+   ok(!sidebar.getTab("tab2"), "Tab 2 was removed correctly");
+   ok(!sidebar.getTabPanel("tabpanel2"), "Tabpanel 2 was removed correctly");
+ 
+   sidebar.destroy();
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   gDevTools.unregisterTool(testToolDefinition.id);
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/framework/test/browser_toolbox_sidebar_overflow_menu.js b/devtools/client/framework/test/browser_toolbox_sidebar_overflow_menu.js
+--- a/devtools/client/framework/test/browser_toolbox_sidebar_overflow_menu.js
++++ b/devtools/client/framework/test/browser_toolbox_sidebar_overflow_menu.js
+@@ -21,22 +21,22 @@ const testToolDefinition = {
+       toolbox: toolbox,
+       isReady: true,
+       destroy: () => {},
+       panelDoc: iframeWindow.document,
+     };
+   }
+ };
+ 
+-add_task(function* () {
+-  let tab = yield addTab("about:blank");
++add_task(async function () {
++  let tab = await addTab("about:blank");
+   let target = TargetFactory.forTab(tab);
+ 
+   gDevTools.registerTool(testToolDefinition);
+-  let toolbox = yield gDevTools.showToolbox(target, testToolDefinition.id);
++  let toolbox = await gDevTools.showToolbox(target, testToolDefinition.id);
+ 
+   let toolPanel = toolbox.getPanel(testToolDefinition.id);
+   let tabbox = toolPanel.panelDoc.getElementById("sidebar");
+ 
+   info("Creating the sidebar widget");
+   let sidebar = new ToolSidebar(tabbox, toolPanel, "bug1101569", {
+     showAllTabsMenu: true
+   });
+@@ -68,13 +68,13 @@ add_task(function* () {
+     is(tabbox.selectedTab.id, "sidebar-tab-" + id,
+       "The selected tab is now nb " + nb);
+   }
+ 
+   info("Fake an underflow event so that the all-tabs menu gets hidden");
+   sidebar._onTabBoxUnderflow();
+   is(allTabsMenu.getAttribute("hidden"), "true", "The all-tabs menu is hidden");
+ 
+-  yield sidebar.destroy();
+-  yield toolbox.destroy();
++  await sidebar.destroy();
++  await toolbox.destroy();
+   gDevTools.unregisterTool(testToolDefinition.id);
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/framework/test/browser_toolbox_split_console.js b/devtools/client/framework/test/browser_toolbox_split_console.js
+--- a/devtools/client/framework/test/browser_toolbox_split_console.js
++++ b/devtools/client/framework/test/browser_toolbox_split_console.js
+@@ -11,73 +11,73 @@
+ 
+ let gToolbox = null;
+ let panelWin = null;
+ 
+ const URL = "data:text/html;charset=utf8,test split console key delegation";
+ 
+ // Force the old debugger UI since it's directly used (see Bug 1301705)
+ Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+-registerCleanupFunction(function* () {
++registerCleanupFunction(function() {
+   Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+ });
+ 
+-add_task(function* () {
+-  let tab = yield addTab(URL);
++add_task(async function () {
++  let tab = await addTab(URL);
+   let target = TargetFactory.forTab(tab);
+-  gToolbox = yield gDevTools.showToolbox(target, "jsdebugger");
++  gToolbox = await gDevTools.showToolbox(target, "jsdebugger");
+   panelWin = gToolbox.getPanel("jsdebugger").panelWin;
+ 
+-  yield gToolbox.openSplitConsole();
+-  yield testIsSplitConsoleFocused();
+-  yield testUseKeyWithSplitConsole();
+-  yield testUseKeyWithSplitConsoleWrongTool();
++  await gToolbox.openSplitConsole();
++  await testIsSplitConsoleFocused();
++  await testUseKeyWithSplitConsole();
++  await testUseKeyWithSplitConsoleWrongTool();
+ 
+-  yield cleanup();
++  await cleanup();
+ });
+ 
+-function* testIsSplitConsoleFocused() {
+-  yield gToolbox.openSplitConsole();
++async function testIsSplitConsoleFocused() {
++  await gToolbox.openSplitConsole();
+   // The newly opened split console should have focus
+   ok(gToolbox.isSplitConsoleFocused(), "Split console is focused");
+   panelWin.focus();
+   ok(!gToolbox.isSplitConsoleFocused(), "Split console is no longer focused");
+ }
+ 
+ // A key bound to the selected tool should trigger it's command
+-function* testUseKeyWithSplitConsole() {
++function testUseKeyWithSplitConsole() {
+   let commandCalled = false;
+ 
+   info("useKeyWithSplitConsole on debugger while debugger is focused");
+   gToolbox.useKeyWithSplitConsole("F3", () => {
+     commandCalled = true;
+   }, "jsdebugger");
+ 
+   info("synthesizeKey with the console focused");
+   let consoleInput = gToolbox.getPanel("webconsole").hud.jsterm.inputNode;
+   consoleInput.focus();
+   synthesizeKeyShortcut("F3", panelWin);
+ 
+   ok(commandCalled, "Shortcut key should trigger the command");
+ }
+ 
+ // A key bound to a *different* tool should not trigger it's command
+-function* testUseKeyWithSplitConsoleWrongTool() {
++function testUseKeyWithSplitConsoleWrongTool() {
+   let commandCalled = false;
+ 
+   info("useKeyWithSplitConsole on inspector while debugger is focused");
+   gToolbox.useKeyWithSplitConsole("F4", () => {
+     commandCalled = true;
+   }, "inspector");
+ 
+   info("synthesizeKey with the console focused");
+   let consoleInput = gToolbox.getPanel("webconsole").hud.jsterm.inputNode;
+   consoleInput.focus();
+   synthesizeKeyShortcut("F4", panelWin);
+ 
+   ok(!commandCalled, "Shortcut key shouldn't trigger the command");
+ }
+ 
+-function* cleanup() {
+-  yield gToolbox.destroy();
++async function cleanup() {
++  await gToolbox.destroy();
+   gBrowser.removeCurrentTab();
+   gToolbox = panelWin = null;
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_tabsswitch_shortcuts.js b/devtools/client/framework/test/browser_toolbox_tabsswitch_shortcuts.js
+--- a/devtools/client/framework/test/browser_toolbox_tabsswitch_shortcuts.js
++++ b/devtools/client/framework/test/browser_toolbox_tabsswitch_shortcuts.js
+@@ -7,62 +7,62 @@
+ 
+ requestLongerTimeout(2);
+ 
+ var {Toolbox} = require("devtools/client/framework/toolbox");
+ 
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+ 
+-add_task(function* () {
+-  let tab = yield addTab("about:blank");
++add_task(async function () {
++  let tab = await addTab("about:blank");
+   let target = TargetFactory.forTab(tab);
+-  yield target.makeRemote();
++  await target.makeRemote();
+ 
+   let toolIDs = gDevTools.getToolDefinitionArray()
+                          .filter(def => def.isTargetSupported(target))
+                          .map(def => def.id);
+ 
+-  let toolbox = yield gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM);
++  let toolbox = await gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM);
+   let nextShortcut = L10N.getStr("toolbox.nextTool.key");
+   let prevShortcut = L10N.getStr("toolbox.previousTool.key");
+ 
+   // Iterate over all tools, starting from options to netmonitor, in normal
+   // order.
+   for (let i = 1; i < toolIDs.length; i++) {
+-    yield testShortcuts(toolbox, i, nextShortcut, toolIDs);
++    await testShortcuts(toolbox, i, nextShortcut, toolIDs);
+   }
+ 
+   // Iterate again, in the same order, starting from netmonitor (so next one is
+   // 0: options).
+   for (let i = 0; i < toolIDs.length; i++) {
+-    yield testShortcuts(toolbox, i, nextShortcut, toolIDs);
++    await testShortcuts(toolbox, i, nextShortcut, toolIDs);
+   }
+ 
+   // Iterate over all tools in reverse order, starting from netmonitor to
+   // options.
+   for (let i = toolIDs.length - 2; i >= 0; i--) {
+-    yield testShortcuts(toolbox, i, prevShortcut, toolIDs);
++    await testShortcuts(toolbox, i, prevShortcut, toolIDs);
+   }
+ 
+   // Iterate again, in reverse order again, starting from options (so next one
+   // is length-1: netmonitor).
+   for (let i = toolIDs.length - 1; i >= 0; i--) {
+-    yield testShortcuts(toolbox, i, prevShortcut, toolIDs);
++    await testShortcuts(toolbox, i, prevShortcut, toolIDs);
+   }
+ 
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* testShortcuts(toolbox, index, shortcut, toolIDs) {
++async function testShortcuts(toolbox, index, shortcut, toolIDs) {
+   info("Testing shortcut to switch to tool " + index + ":" + toolIDs[index] +
+        " using shortcut " + shortcut);
+ 
+   let onToolSelected = toolbox.once("select");
+   synthesizeKeyShortcut(shortcut);
+-  let id = yield onToolSelected;
++  let id = await onToolSelected;
+ 
+   info("toolbox-select event from " + id);
+ 
+   is(toolIDs.indexOf(id), index,
+      "Correct tool is selected on pressing the shortcut for " + id);
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_target.js b/devtools/client/framework/test/browser_toolbox_target.js
+--- a/devtools/client/framework/test/browser_toolbox_target.js
++++ b/devtools/client/framework/test/browser_toolbox_target.js
+@@ -2,59 +2,59 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ // Test about:devtools-toolbox?target which allows opening a toolbox in an
+ // iframe while defining which document to debug by setting a `target`
+ // attribute refering to the document to debug.
+ 
+-add_task(function *() {
++add_task(async function () {
+   // iframe loads the document to debug
+   let iframe = document.createElement("browser");
+   iframe.setAttribute("type", "content");
+   document.documentElement.appendChild(iframe);
+ 
+   let onLoad = once(iframe, "load", true);
+   iframe.setAttribute("src", "data:text/html,document to debug");
+-  yield onLoad;
++  await onLoad;
+   is(iframe.contentWindow.document.body.innerHTML, "document to debug");
+ 
+   // toolbox loads the toolbox document
+   let toolboxIframe = document.createElement("iframe");
+   document.documentElement.appendChild(toolboxIframe);
+ 
+   // Important step to define which target to debug
+   toolboxIframe.target = iframe;
+ 
+   let onToolboxReady = gDevTools.once("toolbox-ready");
+ 
+   onLoad = once(toolboxIframe, "load", true);
+   toolboxIframe.setAttribute("src", "about:devtools-toolbox?target");
+-  yield onLoad;
++  await onLoad;
+ 
+   // Also wait for toolbox-ready, as toolbox document load isn't enough, there
+   // is plenty of asynchronous steps during toolbox load
+   info("Waiting for toolbox-ready");
+-  let toolbox = yield onToolboxReady;
++  let toolbox = await onToolboxReady;
+ 
+   let onToolboxDestroyed = gDevTools.once("toolbox-destroyed");
+   let onTabActorDetached = once(toolbox.target.client, "tabDetached");
+ 
+   info("Removing the iframes");
+   toolboxIframe.remove();
+ 
+   // And wait for toolbox-destroyed as toolbox unload is also full of
+   // asynchronous operation that outlast unload event
+   info("Waiting for toolbox-destroyed");
+-  yield onToolboxDestroyed;
++  await onToolboxDestroyed;
+   info("Toolbox destroyed");
+ 
+   // Also wait for tabDetached. Toolbox destroys the Target which calls
+   // TabActor.detach(). But Target doesn't wait for detach's end to resolve.
+   // Whereas it is quite important as it is a significant part of toolbox
+   // cleanup. If we do not wait for it and starts removing debugged document,
+   // the actor is still considered as being attached and continues processing
+   // events.
+-  yield onTabActorDetached;
++  await onTabActorDetached;
+ 
+   iframe.remove();
+ });
+diff --git a/devtools/client/framework/test/browser_toolbox_theme_registration.js b/devtools/client/framework/test/browser_toolbox_theme_registration.js
+--- a/devtools/client/framework/test/browser_toolbox_theme_registration.js
++++ b/devtools/client/framework/test/browser_toolbox_theme_registration.js
+@@ -8,22 +8,22 @@
+ 
+ // Test for dynamically registering and unregistering themes
+ const CHROME_URL = "chrome://mochitests/content/browser/devtools/client/framework/test/";
+ const TEST_THEME_NAME = "test-theme";
+ const LIGHT_THEME_NAME = "light";
+ 
+ var toolbox;
+ 
+-add_task(function* themeRegistration() {
+-  let tab = yield addTab("data:text/html,test");
++add_task(async function themeRegistration() {
++  let tab = await addTab("data:text/html,test");
+   let target = TargetFactory.forTab(tab);
+-  toolbox = yield gDevTools.showToolbox(target, "options");
++  toolbox = await gDevTools.showToolbox(target, "options");
+ 
+-  let themeId = yield new Promise(resolve => {
++  let themeId = await new Promise(resolve => {
+     gDevTools.once("theme-registered", (e, registeredThemeId) => {
+       resolve(registeredThemeId);
+     });
+ 
+     gDevTools.registerTheme({
+       id: TEST_THEME_NAME,
+       label: "Test theme",
+       stylesheets: [CHROME_URL + "doc_theme.css"],
+@@ -31,17 +31,17 @@ add_task(function* themeRegistration() {
+     });
+   });
+ 
+   is(themeId, TEST_THEME_NAME, "theme-registered event handler sent theme id");
+ 
+   ok(gDevTools.getThemeDefinitionMap().has(themeId), "theme added to map");
+ });
+ 
+-add_task(function* themeInOptionsPanel() {
++add_task(async function themeInOptionsPanel() {
+   let panelWin = toolbox.getCurrentPanel().panelWin;
+   let doc = panelWin.frameElement.contentDocument;
+   let themeBox = doc.getElementById("devtools-theme-box");
+   let testThemeOption = themeBox.querySelector(
+     `input[type=radio][value=${TEST_THEME_NAME}]`);
+   let eventsRecorded = [];
+ 
+   function onThemeChanged(event, theme) {
+@@ -58,74 +58,74 @@ add_task(function* themeInOptionsPanel()
+   isnot(color, "rgb(255, 0, 0)", "style unapplied");
+ 
+   let onThemeSwithComplete = once(panelWin, "theme-switch-complete");
+ 
+   // Select test theme.
+   testThemeOption.click();
+ 
+   info("Waiting for theme to finish loading");
+-  yield onThemeSwithComplete;
++  await onThemeSwithComplete;
+ 
+   is(gDevTools.getTheme(), TEST_THEME_NAME, "getTheme returns the expected theme");
+   is(eventsRecorded.pop(), TEST_THEME_NAME, "theme-changed fired with the expected theme");
+ 
+   color = panelWin.getComputedStyle(themeBox).color;
+   is(color, "rgb(255, 0, 0)", "style applied");
+ 
+   onThemeSwithComplete = once(panelWin, "theme-switch-complete");
+ 
+   // Select light theme
+   lightThemeOption.click();
+ 
+   info("Waiting for theme to finish loading");
+-  yield onThemeSwithComplete;
++  await onThemeSwithComplete;
+ 
+   is(gDevTools.getTheme(), LIGHT_THEME_NAME, "getTheme returns the expected theme");
+   is(eventsRecorded.pop(), LIGHT_THEME_NAME, "theme-changed fired with the expected theme");
+ 
+   color = panelWin.getComputedStyle(themeBox).color;
+   isnot(color, "rgb(255, 0, 0)", "style unapplied");
+ 
+   onThemeSwithComplete = once(panelWin, "theme-switch-complete");
+   // Select test theme again.
+   testThemeOption.click();
+-  yield onThemeSwithComplete;
++  await onThemeSwithComplete;
+   is(gDevTools.getTheme(), TEST_THEME_NAME, "getTheme returns the expected theme");
+   is(eventsRecorded.pop(), TEST_THEME_NAME, "theme-changed fired with the expected theme");
+ 
+   gDevTools.off("theme-changed", onThemeChanged);
+ });
+ 
+-add_task(function* themeUnregistration() {
++add_task(async function themeUnregistration() {
+   let panelWin = toolbox.getCurrentPanel().panelWin;
+   let onUnRegisteredTheme = once(gDevTools, "theme-unregistered");
+   let onThemeSwitchComplete = once(panelWin, "theme-switch-complete");
+   let eventsRecorded = [];
+ 
+   function onThemeChanged(event, theme) {
+     eventsRecorded.push(theme);
+   }
+   gDevTools.on("theme-changed", onThemeChanged);
+ 
+   gDevTools.unregisterTheme(TEST_THEME_NAME);
+-  yield onUnRegisteredTheme;
+-  yield onThemeSwitchComplete;
++  await onUnRegisteredTheme;
++  await onThemeSwitchComplete;
+ 
+   is(gDevTools.getTheme(), LIGHT_THEME_NAME, "getTheme returns the expected theme");
+   is(eventsRecorded.pop(), LIGHT_THEME_NAME, "theme-changed fired with the expected theme");
+   ok(!gDevTools.getThemeDefinitionMap().has(TEST_THEME_NAME),
+     "theme removed from map");
+ 
+   let doc = panelWin.frameElement.contentDocument;
+   let themeBox = doc.getElementById("devtools-theme-box");
+ 
+   // The default light theme must be selected now.
+   is(themeBox.querySelector(`#devtools-theme-box [value=${LIGHT_THEME_NAME}]`).checked, true,
+     `${LIGHT_THEME_NAME} theme must be selected`);
+ 
+   gDevTools.off("theme-changed", onThemeChanged);
+ });
+ 
+-add_task(function* cleanup() {
+-  yield toolbox.destroy();
++add_task(async function cleanup() {
++  await toolbox.destroy();
+   toolbox = null;
+ });
+diff --git a/devtools/client/framework/test/browser_toolbox_toggle.js b/devtools/client/framework/test/browser_toolbox_toggle.js
+--- a/devtools/client/framework/test/browser_toolbox_toggle.js
++++ b/devtools/client/framework/test/browser_toolbox_toggle.js
+@@ -7,102 +7,102 @@
+ 
+ // Test toggling the toolbox with ACCEL+SHIFT+I / ACCEL+ALT+I and F12 in docked
+ // and detached (window) modes.
+ 
+ const URL = "data:text/html;charset=utf-8,Toggling devtools using shortcuts";
+ 
+ var {Toolbox} = require("devtools/client/framework/toolbox");
+ 
+-add_task(function* () {
++add_task(async function () {
+   // Make sure this test starts with the selectedTool pref cleared. Previous
+   // tests select various tools, and that sets this pref.
+   Services.prefs.clearUserPref("devtools.toolbox.selectedTool");
+ 
+   // Test with ACCEL+SHIFT+I / ACCEL+ALT+I (MacOSX) ; modifiers should match :
+   // - toolbox-key-toggle in devtools/client/framework/toolbox-window.xul
+   // - key_devToolboxMenuItem in browser/base/content/browser.xul
+   info("Test toggle using CTRL+SHIFT+I/CMD+ALT+I");
+-  yield testToggle("I", {
++  await testToggle("I", {
+     accelKey: true,
+     shiftKey: !navigator.userAgent.match(/Mac/),
+     altKey: navigator.userAgent.match(/Mac/)
+   });
+ 
+   // Test with F12 ; no modifiers
+   info("Test toggle using F12");
+-  yield testToggle("VK_F12", {});
++  await testToggle("VK_F12", {});
+ });
+ 
+-function* testToggle(key, modifiers) {
+-  let tab = yield addTab(URL + " ; key : '" + key + "'");
+-  yield gDevTools.showToolbox(TargetFactory.forTab(tab));
++async function testToggle(key, modifiers) {
++  let tab = await addTab(URL + " ; key : '" + key + "'");
++  await gDevTools.showToolbox(TargetFactory.forTab(tab));
+ 
+-  yield testToggleDockedToolbox(tab, key, modifiers);
+-  yield testToggleDetachedToolbox(tab, key, modifiers);
++  await testToggleDockedToolbox(tab, key, modifiers);
++  await testToggleDetachedToolbox(tab, key, modifiers);
+ 
+-  yield cleanup();
++  await cleanup();
+ }
+ 
+-function* testToggleDockedToolbox(tab, key, modifiers) {
++async function testToggleDockedToolbox(tab, key, modifiers) {
+   let toolbox = getToolboxForTab(tab);
+ 
+   isnot(toolbox.hostType, Toolbox.HostType.WINDOW,
+     "Toolbox is docked in the main window");
+ 
+   info("verify docked toolbox is destroyed when using toggle key");
+   let onToolboxDestroyed = once(gDevTools, "toolbox-destroyed");
+   EventUtils.synthesizeKey(key, modifiers);
+-  yield onToolboxDestroyed;
++  await onToolboxDestroyed;
+   ok(true, "Docked toolbox is destroyed when using a toggle key");
+ 
+   info("verify new toolbox is created when using toggle key");
+   let onToolboxReady = once(gDevTools, "toolbox-ready");
+   EventUtils.synthesizeKey(key, modifiers);
+-  yield onToolboxReady;
++  await onToolboxReady;
+   ok(true, "Toolbox is created by using when toggle key");
+ }
+ 
+-function* testToggleDetachedToolbox(tab, key, modifiers) {
++async function testToggleDetachedToolbox(tab, key, modifiers) {
+   let toolbox = getToolboxForTab(tab);
+ 
+   info("change the toolbox hostType to WINDOW");
+ 
+-  yield toolbox.switchHost(Toolbox.HostType.WINDOW);
++  await toolbox.switchHost(Toolbox.HostType.WINDOW);
+   is(toolbox.hostType, Toolbox.HostType.WINDOW,
+     "Toolbox opened on separate window");
+ 
+   info("Wait for focus on the toolbox window");
+-  yield new Promise(res => waitForFocus(res, toolbox.win));
++  await new Promise(res => waitForFocus(res, toolbox.win));
+ 
+   info("Focus main window to put the toolbox window in the background");
+ 
+   let onMainWindowFocus = once(window, "focus");
+   window.focus();
+-  yield onMainWindowFocus;
++  await onMainWindowFocus;
+   ok(true, "Main window focused");
+ 
+   info("Verify windowed toolbox is focused instead of closed when using " +
+     "toggle key from the main window");
+   let toolboxWindow = toolbox.win.top;
+   let onToolboxWindowFocus = once(toolboxWindow, "focus", true);
+   EventUtils.synthesizeKey(key, modifiers);
+-  yield onToolboxWindowFocus;
++  await onToolboxWindowFocus;
+   ok(true, "Toolbox focused and not destroyed");
+ 
+   info("Verify windowed toolbox is destroyed when using toggle key from its " +
+     "own window");
+ 
+   let onToolboxDestroyed = once(gDevTools, "toolbox-destroyed");
+   EventUtils.synthesizeKey(key, modifiers, toolboxWindow);
+-  yield onToolboxDestroyed;
++  await onToolboxDestroyed;
+   ok(true, "Toolbox destroyed");
+ }
+ 
+ function getToolboxForTab(tab) {
+   return gDevTools.getToolbox(TargetFactory.forTab(tab));
+ }
+ 
+-function* cleanup() {
++function cleanup() {
+   Services.prefs.setCharPref("devtools.toolbox.host",
+     Toolbox.HostType.BOTTOM);
+   gBrowser.removeCurrentTab();
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_tool_ready.js b/devtools/client/framework/test/browser_toolbox_tool_ready.js
+--- a/devtools/client/framework/test/browser_toolbox_tool_ready.js
++++ b/devtools/client/framework/test/browser_toolbox_tool_ready.js
+@@ -3,42 +3,42 @@
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ requestLongerTimeout(5);
+ 
+ function performChecks(target) {
+-  return Task.spawn(function* () {
++  return (async function () {
+     let toolIds = gDevTools.getToolDefinitionArray()
+                            .filter(def => def.isTargetSupported(target))
+                            .map(def => def.id);
+ 
+     let toolbox;
+     for (let index = 0; index < toolIds.length; index++) {
+       let toolId = toolIds[index];
+ 
+       info("About to open " + index + "/" + toolId);
+-      toolbox = yield gDevTools.showToolbox(target, toolId);
++      toolbox = await gDevTools.showToolbox(target, toolId);
+       ok(toolbox, "toolbox exists for " + toolId);
+       is(toolbox.currentToolId, toolId, "currentToolId should be " + toolId);
+ 
+       let panel = toolbox.getCurrentPanel();
+       ok(panel.isReady, toolId + " panel should be ready");
+     }
+ 
+-    yield toolbox.destroy();
+-  });
++    await toolbox.destroy();
++  })();
+ }
+ 
+ function test() {
+-  Task.spawn(function* () {
++  Task.spawn(async function () {
+     toggleAllTools(true);
+-    let tab = yield addTab("about:blank");
++    let tab = await addTab("about:blank");
+     let target = TargetFactory.forTab(tab);
+-    yield target.makeRemote();
+-    yield performChecks(target);
++    await target.makeRemote();
++    await performChecks(target);
+     gBrowser.removeCurrentTab();
+     toggleAllTools(false);
+     finish();
+   }, console.error);
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+--- a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
++++ b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+@@ -33,36 +33,36 @@ requestLongerTimeout(2);
+  *
+  * In remote debugging, we do not destroy the DebuggerClient on toolbox close
+  * because it can still used for other targets.
+  * Thus, the same client gets reused across multiple toolboxes,
+  * which leads to the tools failing if they don't destroy their fronts.
+  */
+ 
+ function runTools(target) {
+-  return Task.spawn(function* () {
++  return (async function () {
+     let toolIds = gDevTools.getToolDefinitionArray()
+                            .filter(def => def.isTargetSupported(target))
+                            .map(def => def.id);
+ 
+     let toolbox;
+     for (let index = 0; index < toolIds.length; index++) {
+       let toolId = toolIds[index];
+ 
+       info("About to open " + index + "/" + toolId);
+-      toolbox = yield gDevTools.showToolbox(target, toolId, "window");
++      toolbox = await gDevTools.showToolbox(target, toolId, "window");
+       ok(toolbox, "toolbox exists for " + toolId);
+       is(toolbox.currentToolId, toolId, "currentToolId should be " + toolId);
+ 
+       let panel = toolbox.getCurrentPanel();
+       ok(panel.isReady, toolId + " panel should be ready");
+     }
+ 
+-    yield toolbox.destroy();
+-  });
++    await toolbox.destroy();
++  })();
+ }
+ 
+ function getClient() {
+   let deferred = defer();
+ 
+   DebuggerServer.init();
+   DebuggerServer.registerAllActors();
+ 
+@@ -83,23 +83,23 @@ function getTarget(client) {
+     });
+     deferred.resolve(target);
+   });
+ 
+   return deferred.promise;
+ }
+ 
+ function test() {
+-  Task.spawn(function* () {
++  Task.spawn(async function () {
+     toggleAllTools(true);
+-    yield addTab("about:blank");
++    await addTab("about:blank");
+ 
+-    let client = yield getClient();
+-    let target = yield getTarget(client);
+-    yield runTools(target);
++    let client = await getClient();
++    let target = await getTarget(client);
++    await runTools(target);
+ 
+     // Actor fronts should be destroyed now that the toolbox has closed, but
+     // look for any that remain.
+     for (let pool of client.__pools) {
+       if (!pool.__poolMap) {
+         continue;
+       }
+       for (let actor of pool.__poolMap.keys()) {
+diff --git a/devtools/client/framework/test/browser_toolbox_toolbar_overflow.js b/devtools/client/framework/test/browser_toolbox_toolbar_overflow.js
+--- a/devtools/client/framework/test/browser_toolbox_toolbar_overflow.js
++++ b/devtools/client/framework/test/browser_toolbox_toolbar_overflow.js
+@@ -5,83 +5,83 @@
+ 
+ /* import-globals-from shared-head.js */
+ "use strict";
+ 
+ // Test that a button to access tools hidden by toolbar overflow is displayed when the
+ // toolbar starts to present an overflow.
+ let { Toolbox } = require("devtools/client/framework/toolbox");
+ 
+-add_task(function* () {
+-  let tab = yield addTab("about:blank");
++add_task(async function () {
++  let tab = await addTab("about:blank");
+ 
+   info("Open devtools on the Inspector in a separate window");
+-  let toolbox = yield openToolboxForTab(tab, "inspector", Toolbox.HostType.WINDOW);
++  let toolbox = await openToolboxForTab(tab, "inspector", Toolbox.HostType.WINDOW);
+ 
+   let hostWindow = toolbox.win.parent;
+   let originalWidth = hostWindow.outerWidth;
+   let originalHeight = hostWindow.outerHeight;
+ 
+   info("Resize devtools window to a width that should not trigger any overflow");
+   let onResize = once(hostWindow, "resize");
+   hostWindow.resizeTo(640, 300);
+-  yield onResize;
++  await onResize;
+ 
+   let allToolsButton = toolbox.doc.querySelector(".all-tools-menu");
+   ok(!allToolsButton, "The all tools button is not displayed");
+ 
+   info("Resize devtools window to a width that should trigger an overflow");
+   onResize = once(hostWindow, "resize");
+   hostWindow.resizeTo(300, 300);
+-  yield onResize;
++  await onResize;
+ 
+   info("Wait until the all tools button is available");
+-  yield waitUntil(() => toolbox.doc.querySelector(".all-tools-menu"));
++  await waitUntil(() => toolbox.doc.querySelector(".all-tools-menu"));
+ 
+   allToolsButton = toolbox.doc.querySelector(".all-tools-menu");
+   ok(allToolsButton, "The all tools button is displayed");
+ 
+   info("Open the all-tools-menupopup and verify that the inspector button is checked");
+-  let menuPopup = yield openAllToolsMenu(toolbox);
++  let menuPopup = await openAllToolsMenu(toolbox);
+ 
+   let inspectorButton = toolbox.doc.querySelector("#all-tools-menupopup-inspector");
+   ok(inspectorButton, "The inspector button is available");
+   ok(inspectorButton.getAttribute("checked"), "The inspector button is checked");
+ 
+   let consoleButton = toolbox.doc.querySelector("#all-tools-menupopup-webconsole");
+   ok(consoleButton, "The console button is available");
+   ok(!consoleButton.getAttribute("checked"), "The console button is not checked");
+ 
+   info("Switch to the webconsole using the all-tools-menupopup popup");
+   let onSelected = toolbox.once("webconsole-selected");
+   consoleButton.click();
+-  yield onSelected;
++  await onSelected;
+ 
+   info("Closing the all-tools-menupopup popup");
+   let onPopupHidden = once(menuPopup, "popuphidden");
+   menuPopup.hidePopup();
+-  yield onPopupHidden;
++  await onPopupHidden;
+ 
+   info("Re-open the all-tools-menupopup and verify that the console button is checked");
+-  menuPopup = yield openAllToolsMenu(toolbox);
++  menuPopup = await openAllToolsMenu(toolbox);
+ 
+   inspectorButton = toolbox.doc.querySelector("#all-tools-menupopup-inspector");
+   ok(!inspectorButton.getAttribute("checked"), "The inspector button is not checked");
+ 
+   consoleButton = toolbox.doc.querySelector("#all-tools-menupopup-webconsole");
+   ok(consoleButton.getAttribute("checked"), "The console button is checked");
+ 
+   info("Restore the original window size");
+   hostWindow.resizeTo(originalWidth, originalHeight);
+ });
+ 
+-function* openAllToolsMenu(toolbox) {
++async function openAllToolsMenu(toolbox) {
+   let allToolsButton = toolbox.doc.querySelector(".all-tools-menu");
+   EventUtils.synthesizeMouseAtCenter(allToolsButton, {}, toolbox.win);
+ 
+   let menuPopup = toolbox.doc.querySelector("#all-tools-menupopup");
+   ok(menuPopup, "all-tools-menupopup is available");
+ 
+   info("Waiting for the menu popup to be displayed");
+-  yield waitUntil(() => menuPopup && menuPopup.state === "open");
++  await waitUntil(() => menuPopup && menuPopup.state === "open");
+ 
+   return menuPopup;
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_view_source_01.js b/devtools/client/framework/test/browser_toolbox_view_source_01.js
+--- a/devtools/client/framework/test/browser_toolbox_view_source_01.js
++++ b/devtools/client/framework/test/browser_toolbox_view_source_01.js
+@@ -8,39 +8,39 @@
+  * yet opened.
+  */
+ 
+ var URL = `${URL_ROOT}doc_viewsource.html`;
+ var JS_URL = `${URL_ROOT}code_math.js`;
+ 
+ // Force the old debugger UI since it's directly used (see Bug 1301705)
+ Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+-registerCleanupFunction(function* () {
++registerCleanupFunction(function() {
+   Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+ });
+ 
+-function* viewSource() {
+-  let toolbox = yield openNewTabAndToolbox(URL);
++async function viewSource() {
++  let toolbox = await openNewTabAndToolbox(URL);
+ 
+-  yield toolbox.viewSourceInDebugger(JS_URL, 2);
++  await toolbox.viewSourceInDebugger(JS_URL, 2);
+ 
+   let debuggerPanel = toolbox.getPanel("jsdebugger");
+   ok(debuggerPanel, "The debugger panel was opened.");
+   is(toolbox.currentToolId, "jsdebugger", "The debugger panel was selected.");
+ 
+   let { DebuggerView } = debuggerPanel.panelWin;
+   let Sources = DebuggerView.Sources;
+ 
+   is(Sources.selectedValue, getSourceActor(Sources, JS_URL),
+     "The correct source is shown in the debugger.");
+   is(DebuggerView.editor.getCursor().line + 1, 2,
+     "The correct line is highlighted in the debugger's source editor.");
+ 
+-  yield closeToolboxAndTab(toolbox);
++  await closeToolboxAndTab(toolbox);
+   finish();
+ }
+ 
+ function test() {
+-  Task.spawn(viewSource).then(finish, (aError) => {
++  viewSource().then(finish, (aError) => {
+     ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+     finish();
+   });
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_view_source_02.js b/devtools/client/framework/test/browser_toolbox_view_source_02.js
+--- a/devtools/client/framework/test/browser_toolbox_view_source_02.js
++++ b/devtools/client/framework/test/browser_toolbox_view_source_02.js
+@@ -7,48 +7,48 @@
+  * Tests that Toolbox#viewSourceInDebugger works when debugger is already loaded.
+  */
+ 
+ var URL = `${URL_ROOT}doc_viewsource.html`;
+ var JS_URL = `${URL_ROOT}code_math.js`;
+ 
+ // Force the old debugger UI since it's directly used (see Bug 1301705)
+ Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+-registerCleanupFunction(function* () {
++registerCleanupFunction(function() {
+   Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+ });
+ 
+-function* viewSource() {
+-  let toolbox = yield openNewTabAndToolbox(URL);
+-  let { panelWin: debuggerWin } = yield toolbox.selectTool("jsdebugger");
++async function viewSource() {
++  let toolbox = await openNewTabAndToolbox(URL);
++  let { panelWin: debuggerWin } = await toolbox.selectTool("jsdebugger");
+   let debuggerEvents = debuggerWin.EVENTS;
+   let { DebuggerView } = debuggerWin;
+   let Sources = DebuggerView.Sources;
+ 
+-  yield debuggerWin.once(debuggerEvents.SOURCE_SHOWN);
++  await debuggerWin.once(debuggerEvents.SOURCE_SHOWN);
+   ok("A source was shown in the debugger.");
+ 
+   is(Sources.selectedValue, getSourceActor(Sources, JS_URL),
+     "The correct source is initially shown in the debugger.");
+   is(DebuggerView.editor.getCursor().line, 0,
+     "The correct line is initially highlighted in the debugger's source editor.");
+ 
+-  yield toolbox.viewSourceInDebugger(JS_URL, 2);
++  await toolbox.viewSourceInDebugger(JS_URL, 2);
+ 
+   let debuggerPanel = toolbox.getPanel("jsdebugger");
+   ok(debuggerPanel, "The debugger panel was opened.");
+   is(toolbox.currentToolId, "jsdebugger", "The debugger panel was selected.");
+ 
+   is(Sources.selectedValue, getSourceActor(Sources, JS_URL),
+     "The correct source is shown in the debugger.");
+   is(DebuggerView.editor.getCursor().line + 1, 2,
+     "The correct line is highlighted in the debugger's source editor.");
+ 
+-  yield closeToolboxAndTab(toolbox);
++  await closeToolboxAndTab(toolbox);
+   finish();
+ }
+ 
+ function test() {
+-  Task.spawn(viewSource).then(finish, (aError) => {
++  viewSource().then(finish, (aError) => {
+     ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+     finish();
+   });
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_view_source_03.js b/devtools/client/framework/test/browser_toolbox_view_source_03.js
+--- a/devtools/client/framework/test/browser_toolbox_view_source_03.js
++++ b/devtools/client/framework/test/browser_toolbox_view_source_03.js
+@@ -6,35 +6,35 @@
+ /**
+  * Tests that Toolbox#viewSourceInStyleEditor works when style editor is not
+  * yet opened.
+  */
+ 
+ var URL = `${URL_ROOT}doc_viewsource.html`;
+ var CSS_URL = `${URL_ROOT}doc_theme.css`;
+ 
+-function* viewSource() {
+-  let toolbox = yield openNewTabAndToolbox(URL);
++async function viewSource() {
++  let toolbox = await openNewTabAndToolbox(URL);
+ 
+-  let fileFound = yield toolbox.viewSourceInStyleEditor(CSS_URL, 2);
++  let fileFound = await toolbox.viewSourceInStyleEditor(CSS_URL, 2);
+   ok(fileFound, "viewSourceInStyleEditor should resolve to true if source found.");
+ 
+   let stylePanel = toolbox.getPanel("styleeditor");
+   ok(stylePanel, "The style editor panel was opened.");
+   is(toolbox.currentToolId, "styleeditor", "The style editor panel was selected.");
+ 
+   let { UI } = stylePanel;
+ 
+   is(UI.selectedEditor.styleSheet.href, CSS_URL,
+     "The correct source is shown in the style editor.");
+   is(UI.selectedEditor.sourceEditor.getCursor().line + 1, 2,
+     "The correct line is highlighted in the style editor's source editor.");
+ 
+-  yield closeToolboxAndTab(toolbox);
++  await closeToolboxAndTab(toolbox);
+   finish();
+ }
+ 
+ function test() {
+-  Task.spawn(viewSource).then(finish, (aError) => {
++  viewSource().then(finish, (aError) => {
+     ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+     finish();
+   });
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_view_source_04.js b/devtools/client/framework/test/browser_toolbox_view_source_04.js
+--- a/devtools/client/framework/test/browser_toolbox_view_source_04.js
++++ b/devtools/client/framework/test/browser_toolbox_view_source_04.js
+@@ -4,36 +4,36 @@
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that Toolbox#viewSourceInScratchpad works.
+  */
+ 
+ var URL = `${URL_ROOT}doc_viewsource.html`;
+ 
+-function* viewSource() {
+-  let toolbox = yield openNewTabAndToolbox(URL);
+-  let win = yield openScratchpadWindow();
++async function viewSource() {
++  let toolbox = await openNewTabAndToolbox(URL);
++  let win = await openScratchpadWindow();
+   let { Scratchpad: scratchpad } = win;
+ 
+   // Brahm's Cello Sonata No.1, Op.38 now in the scratchpad
+   scratchpad.setText("E G B C B\nA B A G A B\nG E");
+   let scratchpadURL = scratchpad.uniqueName;
+ 
+   // Now select another tool for focus
+-  yield toolbox.selectTool("webconsole");
++  await toolbox.selectTool("webconsole");
+ 
+-  yield toolbox.viewSourceInScratchpad(scratchpadURL, 2);
++  await toolbox.viewSourceInScratchpad(scratchpadURL, 2);
+ 
+   is(scratchpad.editor.getCursor().line, 2,
+     "The correct line is highlighted in scratchpad's editor.");
+ 
+   win.close();
+-  yield closeToolboxAndTab(toolbox);
++  await closeToolboxAndTab(toolbox);
+   finish();
+ }
+ 
+ function test() {
+-  Task.spawn(viewSource).then(finish, (aError) => {
++  viewSource().then(finish, (aError) => {
+     ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+     finish();
+   });
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js b/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js
+--- a/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js
++++ b/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js
+@@ -14,49 +14,49 @@
+  */
+ 
+ var {Toolbox} = require("devtools/client/framework/toolbox");
+ const URL = URL_ROOT + "browser_toolbox_window_title_frame_select_page.html";
+ const IFRAME_URL = URL_ROOT + "browser_toolbox_window_title_changes_page.html";
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+ 
+-add_task(function* () {
++add_task(async function () {
+   Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
+ 
+-  yield addTab(URL);
++  await addTab(URL);
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  let toolbox = yield gDevTools.showToolbox(target, null,
++  let toolbox = await gDevTools.showToolbox(target, null,
+     Toolbox.HostType.BOTTOM);
+ 
+   let onTitleChanged = waitForTitleChange(toolbox);
+-  yield toolbox.selectTool("inspector");
+-  yield onTitleChanged;
++  await toolbox.selectTool("inspector");
++  await onTitleChanged;
+ 
+-  yield toolbox.switchHost(Toolbox.HostType.WINDOW);
++  await toolbox.switchHost(Toolbox.HostType.WINDOW);
+   // Wait for title change event *after* switch host, in order to listen
+   // for the event on the WINDOW host window, which only exists after switchHost
+-  yield waitForTitleChange(toolbox);
++  await waitForTitleChange(toolbox);
+ 
+   is(getTitle(), `Developer Tools - Page title - ${URL}`,
+     "Devtools title correct after switching to detached window host");
+ 
+   // Wait for tick to avoid unexpected 'popuphidden' event, which
+   // blocks the frame popup menu opened below. See also bug 1276873
+-  yield waitForTick();
++  await waitForTick();
+ 
+   let btn = toolbox.doc.getElementById("command-button-frames");
+ 
+-  yield testShortcutToOpenFrames(btn, toolbox);
++  await testShortcutToOpenFrames(btn, toolbox);
+ 
+   // Open frame menu and wait till it's available on the screen.
+   // Also check 'open' attribute on the command button.
+   ok(!btn.classList.contains("checked"), "The checked class must not be present");
+-  let menu = yield toolbox.showFramesMenu({target: btn});
+-  yield once(menu, "open");
++  let menu = await toolbox.showFramesMenu({target: btn});
++  await once(menu, "open");
+ 
+   ok(btn.classList.contains("checked"), "The checked class must be set");
+ 
+   // Verify that the frame list menu is populated
+   let frames = menu.items;
+   is(frames.length, 2, "We have both frames in the list");
+ 
+   let topFrameBtn = frames.filter(b => b.label == URL)[0];
+@@ -70,52 +70,52 @@ add_task(function* () {
+   onTitleChanged = waitForTitleChange(toolbox);
+ 
+   // Only select the iframe after we are able to select an element from the top
+   // level document.
+   let newRoot = toolbox.getPanel("inspector").once("new-root");
+   info("Select the iframe");
+   iframeBtn.click();
+ 
+-  yield willNavigate;
+-  yield newRoot;
+-  yield onTitleChanged;
++  await willNavigate;
++  await newRoot;
++  await onTitleChanged;
+ 
+   info("Navigation to the iframe is done, the inspector should be back up");
+   is(getTitle(), `Developer Tools - Page title - ${URL}`,
+     "Devtools title was not updated after changing inspected frame");
+ 
+   info("Cleanup toolbox and test preferences.");
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   toolbox = null;
+   gBrowser.removeCurrentTab();
+   Services.prefs.clearUserPref("devtools.toolbox.host");
+   Services.prefs.clearUserPref("devtools.toolbox.selectedTool");
+   Services.prefs.clearUserPref("devtools.toolbox.sideEnabled");
+   Services.prefs.clearUserPref("devtools.command-button-frames.enabled");
+   finish();
+ });
+ 
+ function getTitle() {
+   return Services.wm.getMostRecentWindow("devtools:toolbox").document.title;
+ }
+ 
+-function* testShortcutToOpenFrames(btn, toolbox) {
++async function testShortcutToOpenFrames(btn, toolbox) {
+   info("Tests if shortcut Alt+Down opens the frames");
+   // focus the button so that keyPress can be performed
+   btn.focus();
+   // perform keyPress - Alt+Down
+   let shortcut = L10N.getStr("toolbox.showFrames.key");
+   synthesizeKeyShortcut(shortcut, toolbox.win);
+ 
+   // wait for 200 ms for UI to render
+-  yield wait(200);
++  await wait(200);
+ 
+   // btn should now have the checked class set
+   ok(btn.classList.contains("checked"), "The checked class must be set");
+ 
+   // pressing Esc should hide the menu again
+   synthesizeKeyShortcut("Esc", toolbox.win);
+-  yield wait(200);
++  await wait(200);
+ 
+   // btn shouldn't have the checked class set
+   ok(!btn.classList.contains("checked"), "The checked class must not be set");
+ }
+diff --git a/devtools/client/framework/test/head.js b/devtools/client/framework/test/head.js
+--- a/devtools/client/framework/test/head.js
++++ b/devtools/client/framework/test/head.js
+@@ -50,21 +50,21 @@ function getSourceActor(aSources, aURL) 
+ }
+ 
+ /**
+  * Open a Scratchpad window.
+  *
+  * @return nsIDOMWindow
+  *         The new window object that holds Scratchpad.
+  */
+-function* openScratchpadWindow() {
++async function openScratchpadWindow() {
+   let { promise: p, resolve } = defer();
+   let win = ScratchpadManager.openScratchpad();
+ 
+-  yield once(win, "load");
++  await once(win, "load");
+ 
+   win.Scratchpad.addObserver({
+     onReady: function () {
+       win.Scratchpad.removeObserver(this);
+       resolve(win);
+     }
+   });
+   return p;
+diff --git a/devtools/client/framework/test/helper_disable_cache.js b/devtools/client/framework/test/helper_disable_cache.js
+--- a/devtools/client/framework/test/helper_disable_cache.js
++++ b/devtools/client/framework/test/helper_disable_cache.js
+@@ -24,71 +24,71 @@ var tabs = [
+     startToolbox: false
+   },
+   {
+     title: "Tab 3",
+     desc: "No toolbox",
+     startToolbox: false
+   }];
+ 
+-function* initTab(tabX, startToolbox) {
+-  tabX.tab = yield addTab(TEST_URI);
++async function initTab(tabX, startToolbox) {
++  tabX.tab = await addTab(TEST_URI);
+   tabX.target = TargetFactory.forTab(tabX.tab);
+ 
+   if (startToolbox) {
+-    tabX.toolbox = yield gDevTools.showToolbox(tabX.target, "options");
++    tabX.toolbox = await gDevTools.showToolbox(tabX.target, "options");
+   }
+ }
+ 
+-function* checkCacheStateForAllTabs(states) {
++async function checkCacheStateForAllTabs(states) {
+   for (let i = 0; i < tabs.length; i++) {
+     let tab = tabs[i];
+-    yield checkCacheEnabled(tab, states[i]);
++    await checkCacheEnabled(tab, states[i]);
+   }
+ }
+ 
+-function* checkCacheEnabled(tabX, expected) {
++async function checkCacheEnabled(tabX, expected) {
+   gBrowser.selectedTab = tabX.tab;
+ 
+-  yield reloadTab(tabX);
++  await reloadTab(tabX);
+ 
+-  let oldGuid = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
++  let oldGuid = await ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+     let doc = content.document;
+     let h1 = doc.querySelector("h1");
+     return h1.textContent;
+   });
+ 
+-  yield reloadTab(tabX);
++  await reloadTab(tabX);
+ 
+-  let guid = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
++  let guid = await ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+     let doc = content.document;
+     let h1 = doc.querySelector("h1");
+     return h1.textContent;
+   });
+ 
+   if (expected) {
+     is(guid, oldGuid, tabX.title + " cache is enabled");
+   } else {
+     isnot(guid, oldGuid, tabX.title + " cache is not enabled");
+   }
+ }
+ 
+-function* setDisableCacheCheckboxChecked(tabX, state) {
++async function setDisableCacheCheckboxChecked(tabX, state) {
+   gBrowser.selectedTab = tabX.tab;
+ 
+   let panel = tabX.toolbox.getCurrentPanel();
+   let cbx = panel.panelDoc.getElementById("devtools-disable-cache");
+ 
+   if (cbx.checked !== state) {
+     info("Setting disable cache checkbox to " + state + " for " + tabX.title);
+     cbx.click();
+ 
+     // We need to wait for all checkboxes to be updated and the docshells to
+     // apply the new cache settings.
+-    yield waitForTick();
++    await waitForTick();
+   }
+ }
+ 
+ function reloadTab(tabX) {
+   let def = defer();
+   let browser = gBrowser.selectedBrowser;
+ 
+   BrowserTestUtils.browserLoaded(browser).then(function () {
+@@ -98,31 +98,31 @@ function reloadTab(tabX) {
+ 
+   info("Reloading tab " + tabX.title);
+   let mm = loadFrameScriptUtils();
+   mm.sendAsyncMessage("devtools:test:reload");
+ 
+   return def.promise;
+ }
+ 
+-function* destroyTab(tabX) {
++async function destroyTab(tabX) {
+   let toolbox = gDevTools.getToolbox(tabX.target);
+ 
+   let onceDestroyed = promise.resolve();
+   if (toolbox) {
+     onceDestroyed = gDevTools.once("toolbox-destroyed");
+   }
+ 
+   info("Removing tab " + tabX.title);
+   gBrowser.removeTab(tabX.tab);
+   info("Removed tab " + tabX.title);
+ 
+   info("Waiting for toolbox-destroyed");
+-  yield onceDestroyed;
++  await onceDestroyed;
+ }
+ 
+-function* finishUp() {
++async function finishUp() {
+   for (let tab of tabs) {
+-    yield destroyTab(tab);
++    await destroyTab(tab);
+   }
+ 
+   tabs = null;
+ }
+diff --git a/devtools/client/framework/test/test_browser_toolbox_debugger.js b/devtools/client/framework/test/test_browser_toolbox_debugger.js
+--- a/devtools/client/framework/test/test_browser_toolbox_debugger.js
++++ b/devtools/client/framework/test/test_browser_toolbox_debugger.js
+@@ -1,23 +1,23 @@
+ /* global toolbox */
+ 
+ info(`START: ${new Error().lineNumber}`);
+ 
+-Task.spawn(function* () {
++(async function () {
+   Services.prefs.clearUserPref("devtools.debugger.tabs")
+   Services.prefs.clearUserPref("devtools.debugger.pending-selected-location")
+ 
+   info("Waiting for debugger load");
+-  yield toolbox.selectTool("jsdebugger");
++  await toolbox.selectTool("jsdebugger");
+   let dbg = createDebuggerContext(toolbox);
+   let window = dbg.win;
+   let document = window.document;
+ 
+-  yield waitForSources(dbg, testUrl);
++  await waitForSources(dbg, testUrl);
+ //  yield waitForSourceCount(dbg, 6);
+ 
+   info("Loaded, selecting the test script to debug");
+   // First expand the domain
+   let domain = [...document.querySelectorAll(".tree-node")].find(node => {
+     return node.textContent.trim() == "mozilla.org";
+   });
+   let arrow = domain.querySelector(".arrow");
+@@ -27,28 +27,28 @@ Task.spawn(function* () {
+ 
+   let script = [...document.querySelectorAll(".tree-node")].find(node => {
+     return node.textContent.includes(fileName);
+   });
+   script = script.querySelector(".node");
+   script.click();
+ 
+   let onPaused = waitForPaused(dbg);
+-  yield addBreakpoint(dbg, fileName, 2);
++  await addBreakpoint(dbg, fileName, 2);
+ 
+-  yield onPaused;
++  await onPaused;
+ 
+   assertPausedLocation(dbg, fileName, 2);
+ 
+-  yield stepIn(dbg);
++  await stepIn(dbg);
+ 
+   assertPausedLocation(dbg, fileName, 3);
+ 
+   // Remove the breakpoint before resuming in order to prevent hitting the breakpoint
+   // again during test closing.
+   let source = findSource(dbg, fileName);
+-  yield removeBreakpoint(dbg, source.id, 2);
++  await removeBreakpoint(dbg, source.id, 2);
+ 
+-  yield resume(dbg);
++  await resume(dbg);
+ 
+   info("Close the browser toolbox");
+   toolbox.destroy();
+-});
++})();
+diff --git a/devtools/client/framework/toolbox-highlighter-utils.js b/devtools/client/framework/toolbox-highlighter-utils.js
+--- a/devtools/client/framework/toolbox-highlighter-utils.js
++++ b/devtools/client/framework/toolbox-highlighter-utils.js
+@@ -1,16 +1,15 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ const promise = require("promise");
+-const {Task} = require("devtools/shared/task");
+ const flags = require("devtools/shared/flags");
+ 
+ /**
+  * Client-side highlighter shared module.
+  * To be used by toolbox panels that need to highlight DOM elements.
+  *
+  * Highlighting and selecting elements is common enough that it needs to be at
+  * toolbox level, accessible by any panel that needs it.
+@@ -74,23 +73,23 @@ exports.getHighlighterUtils = function(t
+    * Make a function that initializes the inspector before it runs.
+    * Since the init of the inspector is asynchronous, the return value will be
+    * produced by Task.async and the argument should be a generator
+    * @param {Function*} generator A generator function
+    * @return {Function} A function
+    */
+   let isInspectorInitialized = false;
+   let requireInspector = generator => {
+-    return Task.async(function* (...args) {
++    return async function(...args) {
+       if (!isInspectorInitialized) {
+-        yield toolbox.initInspector();
++        await toolbox.initInspector();
+         isInspectorInitialized = true;
+       }
+-      return yield generator.apply(null, args);
+-    });
++      return generator.apply(null, args);
++    };
+   };
+ 
+   /**
+    * Start/stop the element picker on the debuggee target.
+    * @param {Boolean} doFocus - Optionally focus the content area once the picker is
+    *                            activated.
+    * @return A promise that resolves when done
+    */
+@@ -107,81 +106,82 @@ exports.getHighlighterUtils = function(t
+    * on the target page to highlight the hovered/picked element.
+    * Depending on the server-side capabilities, this may fire events when nodes
+    * are hovered.
+    * @param {Boolean} doFocus - Optionally focus the content area once the picker is
+    *                            activated.
+    * @return A promise that resolves when the picker has started or immediately
+    * if it is already started
+    */
+-  let startPicker = exported.startPicker = requireInspector(function* (doFocus = false) {
+-    if (isPicking) {
+-      return;
+-    }
+-    isPicking = true;
++  let startPicker = exported.startPicker =
++    requireInspector(async function(doFocus = false) {
++      if (isPicking) {
++        return;
++      }
++      isPicking = true;
+ 
+-    toolbox.pickerButton.isChecked = true;
+-    yield toolbox.selectTool("inspector");
+-    toolbox.on("select", cancelPicker);
++      toolbox.pickerButton.isChecked = true;
++      await toolbox.selectTool("inspector");
++      toolbox.on("select", cancelPicker);
+ 
+-    if (isRemoteHighlightable()) {
+-      toolbox.walker.on("picker-node-hovered", onPickerNodeHovered);
+-      toolbox.walker.on("picker-node-picked", onPickerNodePicked);
+-      toolbox.walker.on("picker-node-previewed", onPickerNodePreviewed);
+-      toolbox.walker.on("picker-node-canceled", onPickerNodeCanceled);
++      if (isRemoteHighlightable()) {
++        toolbox.walker.on("picker-node-hovered", onPickerNodeHovered);
++        toolbox.walker.on("picker-node-picked", onPickerNodePicked);
++        toolbox.walker.on("picker-node-previewed", onPickerNodePreviewed);
++        toolbox.walker.on("picker-node-canceled", onPickerNodeCanceled);
+ 
+-      yield toolbox.highlighter.pick(doFocus);
+-      toolbox.emit("picker-started");
+-    } else {
+-      // If the target doesn't have the highlighter actor, we can use the
+-      // walker's pick method instead, knowing that it only responds when a node
+-      // is picked (instead of emitting events)
+-      toolbox.emit("picker-started");
+-      let node = yield toolbox.walker.pick();
+-      onPickerNodePicked({node: node});
+-    }
+-  });
++        await toolbox.highlighter.pick(doFocus);
++        toolbox.emit("picker-started");
++      } else {
++        // If the target doesn't have the highlighter actor, we can use the
++        // walker's pick method instead, knowing that it only responds when a node
++        // is picked (instead of emitting events)
++        toolbox.emit("picker-started");
++        let node = await toolbox.walker.pick();
++        onPickerNodePicked({node: node});
++      }
++    });
+ 
+   /**
+    * Stop the element picker. Note that the picker is automatically stopped when
+    * an element is picked
+    * @return A promise that resolves when the picker has stopped or immediately
+    * if it is already stopped
+    */
+-  let stopPicker = exported.stopPicker = requireInspector(function* () {
++  let stopPicker = exported.stopPicker = requireInspector(async function() {
+     if (!isPicking) {
+       return;
+     }
+     isPicking = false;
+ 
+     toolbox.pickerButton.isChecked = false;
+ 
+     if (isRemoteHighlightable()) {
+-      yield toolbox.highlighter.cancelPick();
++      await toolbox.highlighter.cancelPick();
+       toolbox.walker.off("picker-node-hovered", onPickerNodeHovered);
+       toolbox.walker.off("picker-node-picked", onPickerNodePicked);
+       toolbox.walker.off("picker-node-previewed", onPickerNodePreviewed);
+       toolbox.walker.off("picker-node-canceled", onPickerNodeCanceled);
+     } else {
+       // If the target doesn't have the highlighter actor, use the walker's
+       // cancelPick method instead
+-      yield toolbox.walker.cancelPick();
++      await toolbox.walker.cancelPick();
+     }
+ 
+     toolbox.off("select", cancelPicker);
+     toolbox.emit("picker-stopped");
+   });
+ 
+   /**
+    * Stop the picker, but also emit an event that the picker was canceled.
+    */
+-  let cancelPicker = exported.cancelPicker = Task.async(function* () {
+-    yield stopPicker();
++  let cancelPicker = exported.cancelPicker = async function() {
++    await stopPicker();
+     toolbox.emit("picker-canceled");
+-  });
++  };
+ 
+   /**
+    * When a node is hovered by the mouse when the highlighter is in picker mode
+    * @param {Object} data Information about the node being hovered
+    */
+   function onPickerNodeHovered(data) {
+     toolbox.emit("picker-node-hovered", data.node);
+   }
+@@ -217,100 +217,101 @@ exports.getHighlighterUtils = function(t
+    * Show the box model highlighter on a node in the content page.
+    * The node needs to be a NodeFront, as defined by the inspector actor
+    * @see devtools/server/actors/inspector/inspector.js
+    * @param {NodeFront} nodeFront The node to highlight
+    * @param {Object} options
+    * @return A promise that resolves when the node has been highlighted
+    */
+   let highlightNodeFront = exported.highlightNodeFront = requireInspector(
+-  function* (nodeFront, options = {}) {
++  async function(nodeFront, options = {}) {
+     if (!nodeFront) {
+       return;
+     }
+ 
+     isNodeFrontHighlighted = true;
+     if (isRemoteHighlightable()) {
+-      yield toolbox.highlighter.showBoxModel(nodeFront, options);
++      await toolbox.highlighter.showBoxModel(nodeFront, options);
+     } else {
+       // If the target doesn't have the highlighter actor, revert to the
+       // walker's highlight method, which draws a simple outline
+-      yield toolbox.walker.highlight(nodeFront);
++      await toolbox.walker.highlight(nodeFront);
+     }
+ 
+     toolbox.emit("node-highlight", nodeFront);
+   });
+ 
+   /**
+    * This is a convenience method in case you don't have a nodeFront but a
+    * valueGrip. This is often the case with VariablesView properties.
+    * This method will simply translate the grip into a nodeFront and call
+    * highlightNodeFront, so it has the same signature.
+    * @see highlightNodeFront
+    */
+-  exported.highlightDomValueGrip = requireInspector(function* (valueGrip, options = {}) {
+-    let nodeFront = yield gripToNodeFront(valueGrip);
+-    if (nodeFront) {
+-      yield highlightNodeFront(nodeFront, options);
+-    } else {
+-      throw new Error("The ValueGrip passed could not be translated to a NodeFront");
+-    }
+-  });
++  exported.highlightDomValueGrip =
++    requireInspector(async function(valueGrip, options = {}) {
++      let nodeFront = await gripToNodeFront(valueGrip);
++      if (nodeFront) {
++        await highlightNodeFront(nodeFront, options);
++      } else {
++        throw new Error("The ValueGrip passed could not be translated to a NodeFront");
++      }
++    });
+ 
+   /**
+    * Translate a debugger value grip into a node front usable by the inspector
+    * @param {ValueGrip}
+    * @return a promise that resolves to the node front when done
+    */
+   let gripToNodeFront = exported.gripToNodeFront = requireInspector(
+-  function* (grip) {
+-    return yield toolbox.walker.getNodeActorFromObjectActor(grip.actor);
++  async function(grip) {
++    return toolbox.walker.getNodeActorFromObjectActor(grip.actor);
+   });
+ 
+   /**
+    * Hide the highlighter.
+    * @param {Boolean} forceHide Only really matters in test mode (when
+    * flags.testing is true). In test mode, hovering over several nodes
+    * in the markup view doesn't hide/show the highlighter to ease testing. The
+    * highlighter stays visible at all times, except when the mouse leaves the
+    * markup view, which is when this param is passed to true
+    * @return a promise that resolves when the highlighter is hidden
+    */
+-  exported.unhighlight = Task.async(function* (forceHide = false) {
++  exported.unhighlight = async function(forceHide = false) {
+     forceHide = forceHide || !flags.testing;
+ 
+     // Note that if isRemoteHighlightable is true, there's no need to hide the
+     // highlighter as the walker uses setTimeout to hide it after some time
+     if (isNodeFrontHighlighted && forceHide && toolbox.highlighter &&
+         isRemoteHighlightable()) {
+       isNodeFrontHighlighted = false;
+-      yield toolbox.highlighter.hideBoxModel();
++      await toolbox.highlighter.hideBoxModel();
+     }
+ 
+     // unhighlight is called when destroying the toolbox, which means that by
+     // now, the toolbox reference might have been nullified already.
+     if (toolbox) {
+       toolbox.emit("node-unhighlight");
+     }
+-  });
++  };
+ 
+   /**
+    * If the main, box-model, highlighter isn't enough, or if multiple
+    * highlighters are needed in parallel, this method can be used to return a
+    * new instance of a highlighter actor, given a type.
+    * The type of the highlighter passed must be known by the server.
+    * The highlighter actor returned will have the show(nodeFront) and hide()
+    * methods and needs to be released by the consumer when not needed anymore.
+    * @return a promise that resolves to the highlighter
+    */
+-  exported.getHighlighterByType = requireInspector(function* (typeName) {
++  exported.getHighlighterByType = requireInspector(async function(typeName) {
+     let highlighter = null;
+ 
+     if (supportsCustomHighlighters()) {
+-      highlighter = yield toolbox.inspector.getHighlighterByType(typeName);
++      highlighter = await toolbox.inspector.getHighlighterByType(typeName);
+     }
+ 
+     return highlighter || promise.reject("The target doesn't support " +
+         `creating highlighters by types or ${typeName} is unknown`);
+   });
+ 
+   // Return the public API
+   return exported;
+diff --git a/devtools/client/framework/toolbox-host-manager.js b/devtools/client/framework/toolbox-host-manager.js
+--- a/devtools/client/framework/toolbox-host-manager.js
++++ b/devtools/client/framework/toolbox-host-manager.js
+@@ -3,17 +3,16 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ const Services = require("Services");
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+-const {Task} = require("devtools/shared/task");
+ 
+ loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
+ loader.lazyRequireGetter(this, "Hosts", "devtools/client/framework/toolbox-hosts", true);
+ 
+ /**
+  * Implement a wrapper on the chrome side to setup a Toolbox within Firefox UI.
+  *
+  * This component handles iframe creation within Firefox, in which we are loading
+@@ -59,18 +58,18 @@ function ToolboxHostManager(target, host
+   }
+   this.onHostMinimized = this.onHostMinimized.bind(this);
+   this.onHostMaximized = this.onHostMaximized.bind(this);
+   this.host = this.createHost(hostType, hostOptions);
+   this.hostType = hostType;
+ }
+ 
+ ToolboxHostManager.prototype = {
+-  create: Task.async(function* (toolId) {
+-    yield this.host.create();
++  async create(toolId) {
++    await this.host.create();
+ 
+     this.host.frame.setAttribute("aria-label", L10N.getStr("toolbox.label"));
+     this.host.frame.ownerDocument.defaultView.addEventListener("message", this);
+     // We have to listen on capture as no event fires on bubble
+     this.host.frame.addEventListener("unload", this, true);
+ 
+     let toolbox = new Toolbox(this.target, toolId, this.host.type,
+                               this.host.frame.contentWindow, this.frameId);
+@@ -78,17 +77,17 @@ ToolboxHostManager.prototype = {
+     // Prevent reloading the toolbox when loading the tools in a tab
+     // (e.g. from about:debugging)
+     let location = this.host.frame.contentWindow.location;
+     if (!location.href.startsWith("about:devtools-toolbox")) {
+       this.host.frame.setAttribute("src", "about:devtools-toolbox");
+     }
+ 
+     return toolbox;
+-  }),
++  },
+ 
+   handleEvent(event) {
+     switch (event.type) {
+       case "message":
+         this.onMessage(event);
+         break;
+       case "unload":
+         // On unload, host iframe already lost its contentWindow attribute, so
+@@ -181,17 +180,17 @@ ToolboxHostManager.prototype = {
+   },
+ 
+   onHostMaximized() {
+     this.postMessage({
+       name: "host-maximized"
+     });
+   },
+ 
+-  switchHost: Task.async(function* (hostType) {
++  async switchHost(hostType) {
+     if (hostType == "previous") {
+       // Switch to the last used host for the toolbox UI.
+       // This is determined by the devtools.toolbox.previousHost pref.
+       hostType = Services.prefs.getCharPref(PREVIOUS_HOST);
+ 
+       // Handle the case where the previous host happens to match the current
+       // host. If so, switch to bottom if it's not already used, and side if not.
+       if (hostType === this.hostType) {
+@@ -199,17 +198,17 @@ ToolboxHostManager.prototype = {
+           hostType = Toolbox.HostType.SIDE;
+         } else {
+           hostType = Toolbox.HostType.BOTTOM;
+         }
+       }
+     }
+     let iframe = this.host.frame;
+     let newHost = this.createHost(hostType);
+-    let newIframe = yield newHost.create();
++    let newIframe = await newHost.create();
+     // change toolbox document's parent to the new host
+     newIframe.swapFrameLoaders(iframe);
+ 
+     this.destroyHost();
+ 
+     if (this.hostType != Toolbox.HostType.CUSTOM) {
+       Services.prefs.setCharPref(PREVIOUS_HOST, this.hostType);
+     }
+@@ -224,17 +223,17 @@ ToolboxHostManager.prototype = {
+       Services.prefs.setCharPref(LAST_HOST, hostType);
+     }
+ 
+     // Tell the toolbox the host changed
+     this.postMessage({
+       name: "switched-host",
+       hostType
+     });
+-  }),
++  },
+ 
+   /**
+    * Destroy the current host, and remove event listeners from its frame.
+    *
+    * @return {promise} to be resolved when the host is destroyed.
+    */
+   destroyHost() {
+     // When Firefox toplevel is closed, the frame may already be detached and
+diff --git a/devtools/client/framework/toolbox-init.js b/devtools/client/framework/toolbox-init.js
+--- a/devtools/client/framework/toolbox-init.js
++++ b/devtools/client/framework/toolbox-init.js
+@@ -15,17 +15,16 @@ let url = new window.URL(href);
+ if (url.search.length > 1) {
+   const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+   const { gDevTools } = require("devtools/client/framework/devtools");
+   const { targetFromURL } = require("devtools/client/framework/target-from-url");
+   const { Toolbox } = require("devtools/client/framework/toolbox");
+   const { TargetFactory } = require("devtools/client/framework/target");
+   const { DebuggerServer } = require("devtools/server/main");
+   const { DebuggerClient } = require("devtools/shared/client/debugger-client");
+-  const { Task } = require("devtools/shared/task");
+ 
+   // `host` is the frame element loading the toolbox.
+   let host = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIDOMWindowUtils)
+                    .containerElement;
+ 
+   // If there's no containerElement (which happens when loading about:devtools-toolbox as
+   // a top level document), use the current window.
+@@ -42,17 +41,17 @@ if (url.search.length > 1) {
+       // and there is no outer document in this case.
+       addEventListener() {},
+     };
+   }
+ 
+   // Specify the default tool to open
+   let tool = url.searchParams.get("tool");
+ 
+-  Task.spawn(function* () {
++  (async function() {
+     let target;
+     if (url.searchParams.has("target")) {
+       // Attach toolbox to a given browser iframe (<xul:browser> or <html:iframe
+       // mozbrowser>) whose reference is set on the host iframe.
+ 
+       // `iframe` is the targeted document to debug
+       let iframe = host.wrappedJSObject ? host.wrappedJSObject.target
+                                         : host.target;
+@@ -67,22 +66,22 @@ if (url.search.length > 1) {
+       // Fake a xul:tab object as we don't have one.
+       // linkedBrowser is the only one attribute being queried by client.getTab
+       let tab = { linkedBrowser: iframe };
+ 
+       DebuggerServer.init();
+       DebuggerServer.registerAllActors();
+       let client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+-      yield client.connect();
++      await client.connect();
+       // Creates a target for a given browser iframe.
+-      let response = yield client.getTab({ tab });
++      let response = await client.getTab({ tab });
+       let form = response.tab;
+-      target = yield TargetFactory.forRemoteTab({client, form, chrome: false});
++      target = await TargetFactory.forRemoteTab({client, form, chrome: false});
+     } else {
+-      target = yield targetFromURL(url);
++      target = await targetFromURL(url);
+     }
+     let options = { customIframe: host };
+-    yield gDevTools.showToolbox(target, tool, Toolbox.HostType.CUSTOM, options);
+-  }).catch(error => {
++    await gDevTools.showToolbox(target, tool, Toolbox.HostType.CUSTOM, options);
++  })().catch(error => {
+     console.error("Exception while loading the toolbox", error);
+   });
+ }
+diff --git a/devtools/client/framework/toolbox-options.js b/devtools/client/framework/toolbox-options.js
+--- a/devtools/client/framework/toolbox-options.js
++++ b/devtools/client/framework/toolbox-options.js
+@@ -1,17 +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/. */
+ 
+ "use strict";
+ 
+ const Services = require("Services");
+ const defer = require("devtools/shared/defer");
+-const {Task} = require("devtools/shared/task");
+ const {gDevTools} = require("devtools/client/framework/devtools");
+ 
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+ 
+ loader.lazyRequireGetter(this, "system", "devtools/shared/system");
+ 
+ exports.OptionsPanel = OptionsPanel;
+@@ -76,31 +75,31 @@ function OptionsPanel(iframeWindow, tool
+ }
+ 
+ OptionsPanel.prototype = {
+ 
+   get target() {
+     return this.toolbox.target;
+   },
+ 
+-  open: Task.async(function* () {
++  async open() {
+     // For local debugging we need to make the target remote.
+     if (!this.target.isRemote) {
+-      yield this.target.makeRemote();
++      await this.target.makeRemote();
+     }
+ 
+     this.setupToolsList();
+     this.setupToolbarButtonsList();
+     this.setupThemeList();
+     this.setupNightlyOptions();
+-    yield this.populatePreferences();
++    await this.populatePreferences();
+     this.isReady = true;
+     this.emit("ready");
+     return this;
+-  }),
++  },
+ 
+   _addListeners: function() {
+     Services.prefs.addObserver("devtools.cache.disabled", this._prefChanged);
+     Services.prefs.addObserver("devtools.theme", this._prefChanged);
+     Services.prefs.addObserver("devtools.source-map.client-service.enabled",
+                                this._prefChanged);
+     gDevTools.on("theme-registered", this._themeRegistered);
+     gDevTools.on("theme-unregistered", this._themeUnregistered);
+@@ -135,19 +134,19 @@ OptionsPanel.prototype = {
+     let themeBox = this.panelDoc.getElementById("devtools-theme-box");
+     let themeInput = themeBox.querySelector(`[value=${theme.id}]`);
+ 
+     if (themeInput) {
+       themeInput.parentNode.remove();
+     }
+   },
+ 
+-  setupToolbarButtonsList: Task.async(function* () {
++  async setupToolbarButtonsList() {
+     // Ensure the toolbox is open, and the buttons are all set up.
+-    yield this.toolbox.isOpen;
++    await this.toolbox.isOpen;
+ 
+     let enabledToolbarButtonsBox = this.panelDoc.getElementById(
+       "enabled-toolbox-buttons-box");
+ 
+     let toolbarButtons = this.toolbox.toolbarButtons;
+ 
+     if (!toolbarButtons) {
+       console.warn("The command buttons weren't initiated yet.");
+@@ -182,17 +181,17 @@ OptionsPanel.prototype = {
+ 
+     for (let button of toolbarButtons) {
+       if (!button.isTargetSupported(this.toolbox.target)) {
+         continue;
+       }
+ 
+       enabledToolbarButtonsBox.appendChild(createCommandCheckbox(button));
+     }
+-  }),
++  },
+ 
+   setupToolsList: function() {
+     let defaultToolsBox = this.panelDoc.getElementById("default-tools-box");
+     let additionalToolsBox = this.panelDoc.getElementById(
+       "additional-tools-box");
+     let toolsNotSupportedLabel = this.panelDoc.getElementById(
+       "tools-not-supported-label");
+     let atleastOneToolNotSupported = false;
+@@ -357,17 +356,17 @@ OptionsPanel.prototype = {
+ 
+     for (let prefDefinition of prefDefinitions) {
+       let parent = this.panelDoc.getElementById(prefDefinition.parentId);
+       parent.appendChild(createPreferenceOption(prefDefinition));
+       parent.removeAttribute("hidden");
+     }
+   },
+ 
+-  populatePreferences: Task.async(function* () {
++  async populatePreferences() {
+     let prefCheckboxes = this.panelDoc.querySelectorAll(
+       "input[type=checkbox][data-pref]");
+     for (let prefCheckbox of prefCheckboxes) {
+       if (GetPref(prefCheckbox.getAttribute("data-pref"))) {
+         prefCheckbox.setAttribute("checked", true);
+       }
+       prefCheckbox.addEventListener("change", function(e) {
+         let checkbox = e.target;
+@@ -408,25 +407,25 @@ OptionsPanel.prototype = {
+       prefSelect.addEventListener("change", function(e) {
+         let select = e.target;
+         SetPref(select.getAttribute("data-pref"),
+           select.options[select.selectedIndex].value);
+       });
+     }
+ 
+     if (this.target.activeTab) {
+-      let [ response ] = yield this.target.client.attachTab(this.target.activeTab._actor);
++      let [ response ] = await this.target.client.attachTab(this.target.activeTab._actor);
+       this._origJavascriptEnabled = !response.javascriptEnabled;
+       this.disableJSNode.checked = this._origJavascriptEnabled;
+       this.disableJSNode.addEventListener("click", this._disableJSClicked);
+     } else {
+       // Hide the checkbox and label
+       this.disableJSNode.parentNode.style.display = "none";
+     }
+-  }),
++  },
+ 
+   updateCurrentTheme: function() {
+     let currentTheme = GetPref("devtools.theme");
+     let themeBox = this.panelDoc.getElementById("devtools-theme-box");
+     let themeRadioInput = themeBox.querySelector(`[value=${currentTheme}]`);
+ 
+     if (themeRadioInput) {
+       themeRadioInput.checked = true;
+diff --git a/devtools/client/framework/toolbox.js b/devtools/client/framework/toolbox.js
+--- a/devtools/client/framework/toolbox.js
++++ b/devtools/client/framework/toolbox.js
+@@ -15,17 +15,16 @@ const SCREENSIZE_HISTOGRAM = "DEVTOOLS_S
+ const CURRENT_THEME_SCALAR = "devtools.current_theme";
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ 
+ var {Ci, Cc} = require("chrome");
+ var promise = require("promise");
+ var defer = require("devtools/shared/defer");
+ var Services = require("Services");
+ var ChromeUtils = require("ChromeUtils");
+-var {Task} = require("devtools/shared/task");
+ var {gDevTools} = require("devtools/client/framework/devtools");
+ var EventEmitter = require("devtools/shared/old-event-emitter");
+ var Telemetry = require("devtools/client/shared/telemetry");
+ var { attachThread, detachThread } = require("./attach-thread");
+ var Menu = require("devtools/client/framework/menu");
+ var MenuItem = require("devtools/client/framework/menu-item");
+ var { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
+ const { KeyCodes } = require("devtools/client/shared/keycodes");
+@@ -421,17 +420,17 @@ Toolbox.prototype = {
+     return focusedWin && focusedWin ===
+       this.doc.querySelector("#toolbox-panel-iframe-webconsole").contentWindow;
+   },
+ 
+   /**
+    * Open the toolbox
+    */
+   open: function() {
+-    return Task.spawn(function* () {
++    return (async function() {
+       this.browserRequire = BrowserLoader({
+         window: this.doc.defaultView,
+         useOnlyShared: true
+       }).require;
+ 
+       if (this.win.location.href.startsWith(this._URL)) {
+         // Update the URL so that onceDOMReady watch for the right url.
+         this._URL = this.win.location.href;
+@@ -442,29 +441,29 @@ Toolbox.prototype = {
+       domHelper.onceDOMReady(() => {
+         domReady.resolve();
+       }, this._URL);
+ 
+       // Optimization: fire up a few other things before waiting on
+       // the iframe being ready (makes startup faster)
+ 
+       // Load the toolbox-level actor fronts and utilities now
+-      yield this._target.makeRemote();
++      await this._target.makeRemote();
+ 
+       // Start tracking network activity on toolbox open for targets such as tabs.
+       // (Workers and potentially others don't manage the console client in the target.)
+       if (this._target.activeConsole) {
+-        yield this._target.activeConsole.startListeners([
++        await this._target.activeConsole.startListeners([
+           "NetworkActivity",
+         ]);
+       }
+ 
+       // Attach the thread
+-      this._threadClient = yield attachThread(this);
+-      yield domReady.promise;
++      this._threadClient = await attachThread(this);
++      await domReady.promise;
+ 
+       this.isReady = true;
+ 
+       let framesPromise = this._listFrames();
+ 
+       Services.prefs.addObserver("devtools.cache.disabled", this._applyCacheSettings);
+       Services.prefs.addObserver("devtools.serviceWorkers.testing.enabled",
+                                  this._applyServiceWorkersTestingSettings);
+@@ -533,45 +532,45 @@ Toolbox.prototype = {
+         // requestIdleCallback allows releasing it to allow user events to be processed.
+         // Use 16ms maximum delay to allow one frame to be rendered at 60FPS
+         // (1000ms/60FPS=16ms)
+         this.win.requestIdleCallback(() => {
+           this.component.setCanRender();
+         }, {timeout: 16});
+       });
+ 
+-      yield this.selectTool(this._defaultToolId);
++      await this.selectTool(this._defaultToolId);
+ 
+       // Wait until the original tool is selected so that the split
+       // console input will receive focus.
+       let splitConsolePromise = promise.resolve();
+       if (Services.prefs.getBoolPref(SPLITCONSOLE_ENABLED_PREF)) {
+         splitConsolePromise = this.openSplitConsole();
+       }
+ 
+-      yield promise.all([
++      await promise.all([
+         splitConsolePromise,
+         buttonsPromise,
+         framesPromise
+       ]);
+ 
+       // Lazily connect to the profiler here and don't wait for it to complete,
+       // used to intercept console.profile calls before the performance tools are open.
+       let performanceFrontConnection = this.initPerformance();
+ 
+       // If in testing environment, wait for performance connection to finish,
+       // so we don't have to explicitly wait for this in tests; ideally, all tests
+       // will handle this on their own, but each have their own tear down function.
+       if (flags.testing) {
+-        yield performanceFrontConnection;
++        await performanceFrontConnection;
+       }
+ 
+       this.emit("ready");
+       this._isOpenDeferred.resolve();
+-    }.bind(this)).catch(console.error.bind(console));
++    }.bind(this))().catch(console.error.bind(console));
+   },
+ 
+   /**
+    * loading React modules when needed (to avoid performance penalties
+    * during Firefox start up time).
+    */
+   get React() {
+     return this.browserRequire("devtools/client/shared/vendor/react");
+@@ -1253,31 +1252,31 @@ Toolbox.prototype = {
+ 
+     event.preventDefault();
+     event.stopPropagation();
+   },
+ 
+   /**
+    * Add buttons to the UI as specified in devtools/client/definitions.js
+    */
+-  _buildButtons: Task.async(function* () {
++  async _buildButtons() {
+     // Beyond the normal preference filtering
+     this.toolbarButtons = [
+       this._buildPickerButton(),
+       this._buildFrameButton(),
+-      yield this._buildNoAutoHideButton()
++      await this._buildNoAutoHideButton()
+     ];
+ 
+     ToolboxButtons.forEach(definition => {
+       let button = this._createButtonState(definition);
+       this.toolbarButtons.push(button);
+     });
+ 
+     this.component.setToolboxButtons(this.toolbarButtons);
+-  }),
++  },
+ 
+   /**
+    * Button to select a frame for the inspector to target.
+    */
+   _buildFrameButton() {
+     this.frameButton = this._createButtonState({
+       id: "command-button-frames",
+       description: L10N.getStr("toolbox.frames.tooltip"),
+@@ -1290,30 +1289,30 @@ Toolbox.prototype = {
+ 
+     return this.frameButton;
+   },
+ 
+   /**
+    * Button that disables/enables auto-hiding XUL pop-ups. When enabled, XUL
+    * pop-ups will not automatically close when they lose focus.
+    */
+-  _buildNoAutoHideButton: Task.async(function* () {
++  async _buildNoAutoHideButton() {
+     this.autohideButton = this._createButtonState({
+       id: "command-button-noautohide",
+       description: L10N.getStr("toolbox.noautohide.tooltip"),
+       onClick: this._toggleNoAutohide,
+       isTargetSupported: target => target.chrome
+     });
+ 
+     this._isDisableAutohideEnabled().then(enabled => {
+       this.autohideButton.isChecked = enabled;
+     });
+ 
+     return this.autohideButton;
+-  }),
++  },
+ 
+   /**
+    * Toggle the picker, but also decide whether or not the highlighter should
+    * focus the window. This is only desirable when the toolbox is mounted to the
+    * window. When devtools is free floating, then the target window should not
+    * pop in front of the viewer when the picker is clicked.
+    *
+    * Note: Toggle picker can be overwritten by panel other than the inspector to
+@@ -2061,35 +2060,35 @@ Toolbox.prototype = {
+   },
+ 
+   /**
+    * Highlights the tool's tab if it is not the currently selected tool.
+    *
+    * @param {string} id
+    *        The id of the tool to highlight
+    */
+-  highlightTool: Task.async(function* (id) {
++  async highlightTool(id) {
+     if (!this.component) {
+-      yield this.isOpen;
++      await this.isOpen;
+     }
+     this.component.highlightTool(id);
+-  }),
++  },
+ 
+   /**
+    * De-highlights the tool's tab.
+    *
+    * @param {string} id
+    *        The id of the tool to unhighlight
+    */
+-  unhighlightTool: Task.async(function* (id) {
++  async unhighlightTool(id) {
+     if (!this.component) {
+-      yield this.isOpen;
++      await this.isOpen;
+     }
+     this.component.unhighlightTool(id);
+-  }),
++  },
+ 
+   /**
+    * Raise the toolbox host.
+    */
+   raise: function() {
+     this.postMessage({
+       name: "raise-host"
+     });
+@@ -2134,36 +2133,36 @@ Toolbox.prototype = {
+       return this.target.root.then(rootForm => {
+         let front = getPreferenceFront(this.target.client, rootForm);
+         this._preferenceFront = front;
+         return front;
+       });
+     });
+   },
+ 
+-  _toggleNoAutohide: Task.async(function* () {
+-    let front = yield this.preferenceFront;
+-    let toggledValue = !(yield this._isDisableAutohideEnabled());
++  async _toggleNoAutohide() {
++    let front = await this.preferenceFront;
++    let toggledValue = !(await this._isDisableAutohideEnabled());
+ 
+     front.setBoolPref(DISABLE_AUTOHIDE_PREF, toggledValue);
+ 
+     this.autohideButton.isChecked = toggledValue;
+     this._autohideHasBeenToggled = true;
+-  }),
+-
+-  _isDisableAutohideEnabled: Task.async(function* () {
++  },
++
++  async _isDisableAutohideEnabled() {
+     // Ensure that the tools are open, and the button is visible.
+-    yield this.isOpen;
++    await this.isOpen;
+     if (!this.autohideButton.isVisible) {
+       return false;
+     }
+ 
+-    let prefFront = yield this.preferenceFront;
+-    return yield prefFront.getBoolPref(DISABLE_AUTOHIDE_PREF);
+-  }),
++    let prefFront = await this.preferenceFront;
++    return prefFront.getBoolPref(DISABLE_AUTOHIDE_PREF);
++  },
+ 
+   _listFrames: function(event) {
+     if (!this._target.activeTab || !this._target.activeTab.traits.frames) {
+       // We are not targetting a regular TabActor
+       // it can be either an addon or browser toolbox actor
+       return promise.resolve();
+     }
+     let packet = {
+@@ -2535,32 +2534,32 @@ Toolbox.prototype = {
+   },
+ 
+   /**
+    * Initialize the inspector/walker/selection/highlighter fronts.
+    * Returns a promise that resolves when the fronts are initialized
+    */
+   initInspector: function() {
+     if (!this._initInspector) {
+-      this._initInspector = Task.spawn(function* () {
++      this._initInspector = (async function() {
+         this._inspector = InspectorFront(this._target.client, this._target.form);
+         let pref = "devtools.inspector.showAllAnonymousContent";
+         let showAllAnonymousContent = Services.prefs.getBoolPref(pref);
+-        this._walker = yield this._inspector.getWalker({ showAllAnonymousContent });
++        this._walker = await this._inspector.getWalker({ showAllAnonymousContent });
+         this._selection = new Selection(this._walker);
+         this._selection.on("new-node-front", this._onNewSelectedNodeFront);
+ 
+         if (this.highlighterUtils.isRemoteHighlightable()) {
+           this.walker.on("highlighter-ready", this._highlighterReady);
+           this.walker.on("highlighter-hide", this._highlighterHidden);
+ 
+           let autohide = !flags.testing;
+-          this._highlighter = yield this._inspector.getHighlighter(autohide);
++          this._highlighter = await this._inspector.getHighlighter(autohide);
+         }
+-      }.bind(this));
++      }.bind(this))();
+     }
+     return this._initInspector;
+   },
+ 
+   _onNewSelectedNodeFront: function(evt) {
+     // Emit a "selection-changed" event when the toolbox.selection has been set
+     // to a new node (or cleared). Currently used in the WebExtensions APIs (to
+     // provide the `devtools.panels.elements.onSelectionChanged` event).
+@@ -2598,59 +2597,59 @@ Toolbox.prototype = {
+    * Destroy the inspector/walker/selection fronts
+    * Returns a promise that resolves when the fronts are destroyed
+    */
+   destroyInspector: function() {
+     if (this._destroyingInspector) {
+       return this._destroyingInspector;
+     }
+ 
+-    this._destroyingInspector = Task.spawn(function* () {
++    this._destroyingInspector = (async function() {
+       if (!this._inspector) {
+         return;
+       }
+ 
+       // Ensure that the inspector isn't still being initiated, otherwise race conditions
+       // in the initialization process can throw errors.
+-      yield this._initInspector;
++      await this._initInspector;
+ 
+       let currentPanel = this.getCurrentPanel();
+       if (currentPanel.stopPicker) {
+-        yield currentPanel.stopPicker();
++        await currentPanel.stopPicker();
+       } else {
+-        yield this.highlighterUtils.stopPicker();
++        await this.highlighterUtils.stopPicker();
+       }
+ 
+-      yield this._inspector.destroy();
++      await this._inspector.destroy();
+       if (this._highlighter) {
+         // Note that if the toolbox is closed, this will work fine, but will fail
+         // in case the browser is closed and will trigger a noSuchActor message.
+         // We ignore the promise that |_hideBoxModel| returns, since we should still
+         // proceed with the rest of destruction if it fails.
+         // FF42+ now does the cleanup from the actor.
+         if (!this.highlighter.traits.autoHideOnDestroy) {
+           this.highlighterUtils.unhighlight();
+         }
+-        yield this._highlighter.destroy();
++        await this._highlighter.destroy();
+       }
+       if (this._selection) {
+         this._selection.off("new-node-front", this._onNewSelectedNodeFront);
+         this._selection.destroy();
+       }
+ 
+       if (this.walker) {
+         this.walker.off("highlighter-ready", this._highlighterReady);
+         this.walker.off("highlighter-hide", this._highlighterHidden);
+       }
+ 
+       this._inspector = null;
+       this._highlighter = null;
+       this._selection = null;
+       this._walker = null;
+-    }.bind(this));
++    }.bind(this))();
+     return this._destroyingInspector;
+   },
+ 
+   /**
+    * Get the toolbox's notification component
+    *
+    * @return The notification box component.
+    */
+@@ -2880,123 +2879,123 @@ Toolbox.prototype = {
+   openTextBoxContextMenu: function(x, y) {
+     this.textBoxContextMenuPopup.openPopupAtScreen(x, y, true);
+   },
+ 
+   /**
+    * Connects to the Gecko Profiler when the developer tools are open. This is
+    * necessary because of the WebConsole's `profile` and `profileEnd` methods.
+    */
+-  initPerformance: Task.async(function* () {
++  async initPerformance() {
+     // If target does not have performance actor (addons), do not
+     // even register the shared performance connection.
+     if (!this.target.hasActor("performance")) {
+       return promise.resolve();
+     }
+ 
+     if (this._performanceFrontConnection) {
+       return this._performanceFrontConnection.promise;
+     }
+ 
+     this._performanceFrontConnection = defer();
+     this._performance = createPerformanceFront(this._target);
+-    yield this.performance.connect();
++    await this.performance.connect();
+ 
+     // Emit an event when connected, but don't wait on startup for this.
+     this.emit("profiler-connected");
+ 
+     this.performance.on("*", this._onPerformanceFrontEvent);
+     this._performanceFrontConnection.resolve(this.performance);
+     return this._performanceFrontConnection.promise;
+-  }),
++  },
+ 
+   /**
+    * Disconnects the underlying Performance actor. If the connection
+    * has not finished initializing, as opening a toolbox does not wait,
+    * the performance connection destroy method will wait for it on its own.
+    */
+-  destroyPerformance: Task.async(function* () {
++  async destroyPerformance() {
+     if (!this.performance) {
+       return;
+     }
+     // If still connecting to performance actor, allow the
+     // actor to resolve its connection before attempting to destroy.
+     if (this._performanceFrontConnection) {
+-      yield this._performanceFrontConnection.promise;
++      await this._performanceFrontConnection.promise;
+     }
+     this.performance.off("*", this._onPerformanceFrontEvent);
+-    yield this.performance.destroy();
++    await this.performance.destroy();
+     this._performance = null;
+-  }),
++  },
+ 
+   /**
+    * Return the style sheets front, creating it if necessary.  If the
+    * style sheets front is not supported by the target, returns null.
+    */
+   initStyleSheetsFront: function() {
+     if (!this._styleSheets && this.target.hasActor("styleSheets")) {
+       this._styleSheets = StyleSheetsFront(this.target.client, this.target.form);
+     }
+     return this._styleSheets;
+   },
+ 
+   /**
+    * Destroy the preferences actor when the toolbox is unloaded.
+    */
+-  destroyPreference: Task.async(function* () {
++  async destroyPreference() {
+     if (!this._preferenceFront) {
+       return;
+     }
+ 
+     // Only reset the autohide pref in the Browser Toolbox if it's been toggled
+     // in the UI (don't reset the pref if it was already set before opening)
+     if (this._autohideHasBeenToggled) {
+-      yield this._preferenceFront.clearUserPref(DISABLE_AUTOHIDE_PREF);
++      await this._preferenceFront.clearUserPref(DISABLE_AUTOHIDE_PREF);
+     }
+ 
+     this._preferenceFront.destroy();
+     this._preferenceFront = null;
+-  }),
++  },
+ 
+   /**
+    * Called when any event comes from the PerformanceFront. If the performance tool is
+    * already loaded when the first event comes in, immediately unbind this handler, as
+    * this is only used to queue up observed recordings before the performance tool can
+    * handle them, which will only occur when `console.profile()` recordings are started
+    * before the tool loads.
+    */
+-  _onPerformanceFrontEvent: Task.async(function* (eventName, recording) {
++  async _onPerformanceFrontEvent(eventName, recording) {
+     if (this.getPanel("performance")) {
+       this.performance.off("*", this._onPerformanceFrontEvent);
+       return;
+     }
+ 
+     this._performanceQueuedRecordings = this._performanceQueuedRecordings || [];
+     let recordings = this._performanceQueuedRecordings;
+ 
+     // Before any console recordings, we'll get a `console-profile-start` event
+     // warning us that a recording will come later (via `recording-started`), so
+     // start to boot up the tool and populate the tool with any other recordings
+     // observed during that time.
+     if (eventName === "console-profile-start" && !this._performanceToolOpenedViaConsole) {
+       this._performanceToolOpenedViaConsole = this.loadTool("performance");
+-      let panel = yield this._performanceToolOpenedViaConsole;
+-      yield panel.open();
++      let panel = await this._performanceToolOpenedViaConsole;
++      await panel.open();
+ 
+       panel.panelWin.PerformanceController.populateWithRecordings(recordings);
+       this.performance.off("*", this._onPerformanceFrontEvent);
+     }
+ 
+     // Otherwise, if it's a recording-started event, we've already started loading
+     // the tool, so just store this recording in our array to be later populated
+     // once the tool loads.
+     if (eventName === "recording-started") {
+       recordings.push(recording);
+     }
+-  }),
++  },
+ 
+   /**
+    * Returns gViewSourceUtils for viewing source.
+    */
+   get gViewSourceUtils() {
+     return this.win.gViewSourceUtils;
+   },
+ 

+ 34 - 0
frg/work-js/mozilla-release/patches/1440321-1g-inspector-61a1.patch

@@ -0,0 +1,34 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  87341d0baeab760b7094b759a0a1cefed67dcce2
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1g. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/inspector/test/shared-head.js b/devtools/client/inspector/test/shared-head.js
+--- a/devtools/client/inspector/test/shared-head.js
++++ b/devtools/client/inspector/test/shared-head.js
+@@ -1,18 +1,20 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ /* eslint no-unused-vars: [2, {"vars": "local"}] */
+-/* globals registerTestActor, getTestActor, Task, openToolboxForTab, gBrowser */
++/* globals registerTestActor, getTestActor, openToolboxForTab, gBrowser */
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
++const { Task } = require("devtools/shared/task");
++
+ var {getInplaceEditorForSpan: inplaceEditor} = require("devtools/client/shared/inplace-editor");
+ 
+ // This file contains functions related to the inspector that are also of interest to
+ // other test directores as well.
+ 
+ /**
+  * Open the toolbox, with the inspector tool visible.
+  * @param {String} hostType Optional hostType, as defined in Toolbox.HostType

+ 834 - 0
frg/work-js/mozilla-release/patches/1440321-1h-jsonview-61a1.patch

@@ -0,0 +1,834 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  a00f2cc9ce9a2dccb1ee1f030026ed3b3c7f3828
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1h. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/jsonview/test/browser_json_refresh.js b/devtools/client/jsonview/test/browser_json_refresh.js
+--- a/devtools/client/jsonview/test/browser_json_refresh.js
++++ b/devtools/client/jsonview/test/browser_json_refresh.js
+@@ -2,27 +2,27 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_JSON_FILE = "simple_json.json";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test JSON refresh started");
+ 
+   // generate file:// URI for JSON file and load in new tab
+   let dir = getChromeDir(getResolvedURI(gTestPath));
+   dir.append(TEST_JSON_FILE);
+   let uri = Services.io.newFileURI(dir);
+-  let tab = yield addJsonViewTab(uri.spec);
++  let tab = await addJsonViewTab(uri.spec);
+ 
+   // perform sanity checks for URI and pricnipals in loadInfo
+-  yield ContentTask.spawn(tab.linkedBrowser, {TEST_JSON_FILE}, function*({TEST_JSON_FILE}) { // eslint-disable-line
++  await ContentTask.spawn(tab.linkedBrowser, {TEST_JSON_FILE}, async function ({TEST_JSON_FILE}) { // eslint-disable-line
+     let channel = content.document.docShell.currentDocumentChannel;
+     let channelURI = channel.URI.spec;
+     ok(channelURI.startsWith("file://") && channelURI.includes(TEST_JSON_FILE),
+        "sanity: correct channel uri");
+     let contentPolicyType = channel.loadInfo.externalContentPolicyType;
+     is(contentPolicyType, Ci.nsIContentPolicy.TYPE_DOCUMENT,
+        "sanity: correct contentPolicyType");
+ 
+@@ -35,20 +35,20 @@ add_task(function* () {
+     ok(principalToInherit.isNullPrincipal, "sanity: correct principalToInherit");
+     ok(content.document.nodePrincipal.isNullPrincipal,
+        "sanity: correct doc.nodePrincipal");
+   });
+ 
+   // reload the tab
+   let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+   tab.linkedBrowser.reload();
+-  yield loaded;
++  await loaded;
+ 
+   // check principals in loadInfo are still correct
+-  yield ContentTask.spawn(tab.linkedBrowser, {TEST_JSON_FILE}, function*({TEST_JSON_FILE}) { // eslint-disable-line
++  await ContentTask.spawn(tab.linkedBrowser, {TEST_JSON_FILE}, async function ({TEST_JSON_FILE}) { // eslint-disable-line
+     let channel = content.document.docShell.currentDocumentChannel;
+     let channelURI = channel.URI.spec;
+     ok(channelURI.startsWith("file://") && channelURI.includes(TEST_JSON_FILE),
+        "reloaded: correct channel uri");
+     let contentPolicyType = channel.loadInfo.externalContentPolicyType;
+     is(contentPolicyType, Ci.nsIContentPolicy.TYPE_DOCUMENT,
+        "reloaded: correct contentPolicyType");
+ 
+diff --git a/devtools/client/jsonview/test/browser_jsonview_bug_1380828.js b/devtools/client/jsonview/test/browser_jsonview_bug_1380828.js
+--- a/devtools/client/jsonview/test/browser_jsonview_bug_1380828.js
++++ b/devtools/client/jsonview/test/browser_jsonview_bug_1380828.js
+@@ -2,31 +2,31 @@
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const VALID_JSON_URL = URL_ROOT + "valid_json.json";
+ const INVALID_JSON_URL = URL_ROOT + "invalid_json.json";
+ const prettyPrintButtonClass = ".textPanelBox .toolbar button.prettyprint";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test 'Pretty Print' button disappears on parsing invalid JSON");
+ 
+-  let count = yield testPrettyPrintButton(INVALID_JSON_URL);
++  let count = await testPrettyPrintButton(INVALID_JSON_URL);
+   is(count, 0, "There must be no pretty-print button for invalid json");
+ });
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test 'Pretty Print' button is present on parsing valid JSON");
+ 
+-  let count = yield testPrettyPrintButton(VALID_JSON_URL);
++  let count = await testPrettyPrintButton(VALID_JSON_URL);
+   is(count, 1, "There must be pretty-print button for valid json");
+ });
+ 
+-function* testPrettyPrintButton(url) {
+-  yield addJsonViewTab(url);
++async function testPrettyPrintButton(url) {
++  await addJsonViewTab(url);
+ 
+-  yield selectJsonViewContentTab("rawdata");
++  await selectJsonViewContentTab("rawdata");
+   info("Switched to Raw Data tab.");
+ 
+-  let count = yield getElementCount(prettyPrintButtonClass);
++  let count = await getElementCount(prettyPrintButtonClass);
+   return count;
+ }
+diff --git a/devtools/client/jsonview/test/browser_jsonview_content_type.js b/devtools/client/jsonview/test/browser_jsonview_content_type.js
+--- a/devtools/client/jsonview/test/browser_jsonview_content_type.js
++++ b/devtools/client/jsonview/test/browser_jsonview_content_type.js
+@@ -21,17 +21,17 @@ let contentTypes = {
+   invalid: [
+     "text/json",
+     "text/hal+json",
+     "application/jsona",
+     "application/whatever+jsona",
+   ],
+ };
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test JSON content types started");
+ 
+   // Prevent saving files to disk.
+   let useDownloadDir = SpecialPowers.getBoolPref("browser.download.useDownloadDir");
+   SpecialPowers.setBoolPref("browser.download.useDownloadDir", false);
+   let { MockFilePicker } = SpecialPowers;
+   MockFilePicker.init(window);
+   MockFilePicker.returnValue = MockFilePicker.returnCancel;
+@@ -41,18 +41,18 @@ add_task(function* () {
+     for (let type of contentTypes[kind]) {
+       // Prevent "Open or Save" dialogs, which would make the test fail.
+       let mimeInfo = mimeSvc.getFromTypeAndExtension(type, null);
+       let exists = handlerSvc.exists(mimeInfo);
+       let {alwaysAskBeforeHandling} = mimeInfo;
+       mimeInfo.alwaysAskBeforeHandling = false;
+       handlerSvc.store(mimeInfo);
+ 
+-      yield testType(isValid, type);
+-      yield testType(isValid, type, ";foo=bar+json");
++      await testType(isValid, type);
++      await testType(isValid, type, ";foo=bar+json");
+ 
+       // Restore old nsIMIMEInfo
+       if (exists) {
+         Object.assign(mimeInfo, {alwaysAskBeforeHandling});
+         handlerSvc.store(mimeInfo);
+       } else {
+         handlerSvc.remove(mimeInfo);
+       }
+diff --git a/devtools/client/jsonview/test/browser_jsonview_copy_headers.js b/devtools/client/jsonview/test/browser_jsonview_copy_headers.js
+--- a/devtools/client/jsonview/test/browser_jsonview_copy_headers.js
++++ b/devtools/client/jsonview/test/browser_jsonview_copy_headers.js
+@@ -2,34 +2,34 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_JSON_URL = URL_ROOT + "valid_json.json";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test valid JSON started");
+ 
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+   // Select the RawData tab
+-  yield selectJsonViewContentTab("headers");
++  await selectJsonViewContentTab("headers");
+ 
+   // Check displayed headers
+-  let count = yield getElementCount(".headersPanelBox .netHeadersGroup");
++  let count = await getElementCount(".headersPanelBox .netHeadersGroup");
+   is(count, 2, "There must be two header groups");
+ 
+-  let text = yield getElementText(".headersPanelBox .netInfoHeadersTable");
++  let text = await getElementText(".headersPanelBox .netInfoHeadersTable");
+   isnot(text, "", "Headers text must not be empty");
+ 
+   let browser = gBrowser.selectedBrowser;
+ 
+   // Verify JSON copy into the clipboard.
+-  yield waitForClipboardPromise(function setup() {
++  await waitForClipboardPromise(function setup() {
+     BrowserTestUtils.synthesizeMouseAtCenter(
+       ".headersPanelBox .toolbar button.copy",
+       {}, browser);
+   }, function validator(value) {
+     return value.indexOf("application/json") > 0;
+   });
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_copy_json.js b/devtools/client/jsonview/test/browser_jsonview_copy_json.js
+--- a/devtools/client/jsonview/test/browser_jsonview_copy_json.js
++++ b/devtools/client/jsonview/test/browser_jsonview_copy_json.js
+@@ -2,30 +2,30 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_JSON_URL = URL_ROOT + "simple_json.json";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test copy JSON started");
+ 
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+-  let countBefore = yield getElementCount(".jsonPanelBox .treeTable .treeRow");
++  let countBefore = await getElementCount(".jsonPanelBox .treeTable .treeRow");
+   ok(countBefore == 1, "There must be one row");
+ 
+-  let text = yield getElementText(".jsonPanelBox .treeTable .treeRow");
++  let text = await getElementText(".jsonPanelBox .treeTable .treeRow");
+   is(text, "name\"value\"", "There must be proper JSON displayed");
+ 
+   // Verify JSON copy into the clipboard.
+   let value = "{\"name\": \"value\"}\n";
+   let browser = gBrowser.selectedBrowser;
+   let selector = ".jsonPanelBox .toolbar button.copy";
+-  yield waitForClipboardPromise(function setup() {
++  await waitForClipboardPromise(function setup() {
+     BrowserTestUtils.synthesizeMouseAtCenter(selector, {}, browser);
+   }, function validator(result) {
+     let str = normalizeNewLines(result);
+     return str == value;
+   });
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_copy_rawdata.js b/devtools/client/jsonview/test/browser_jsonview_copy_rawdata.js
+--- a/devtools/client/jsonview/test/browser_jsonview_copy_rawdata.js
++++ b/devtools/client/jsonview/test/browser_jsonview_copy_rawdata.js
+@@ -5,49 +5,49 @@
+ 
+ "use strict";
+ 
+ const TEST_JSON_URL = URL_ROOT + "simple_json.json";
+ 
+ let jsonText = "{\"name\": \"value\"}\n";
+ let prettyJson = "{\n  \"name\": \"value\"\n}";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test copy raw data started");
+ 
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+   // Select the RawData tab
+-  yield selectJsonViewContentTab("rawdata");
++  await selectJsonViewContentTab("rawdata");
+ 
+   // Check displayed JSON
+-  let text = yield getElementText(".textPanelBox .data");
++  let text = await getElementText(".textPanelBox .data");
+   is(text, jsonText, "Proper JSON must be displayed in DOM");
+ 
+   let browser = gBrowser.selectedBrowser;
+ 
+   // Verify JSON copy into the clipboard.
+-  yield waitForClipboardPromise(function setup() {
++  await waitForClipboardPromise(function setup() {
+     BrowserTestUtils.synthesizeMouseAtCenter(
+       ".textPanelBox .toolbar button.copy",
+       {}, browser);
+   }, jsonText);
+ 
+   // Click 'Pretty Print' button
+-  yield BrowserTestUtils.synthesizeMouseAtCenter(
++  await BrowserTestUtils.synthesizeMouseAtCenter(
+     ".textPanelBox .toolbar button.prettyprint",
+     {}, browser);
+ 
+-  let prettyText = yield getElementText(".textPanelBox .data");
++  let prettyText = await getElementText(".textPanelBox .data");
+   prettyText = normalizeNewLines(prettyText);
+   ok(prettyText.startsWith(prettyJson),
+     "Pretty printed JSON must be displayed");
+ 
+   // Verify JSON copy into the clipboard.
+-  yield waitForClipboardPromise(function setup() {
++  await waitForClipboardPromise(function setup() {
+     BrowserTestUtils.synthesizeMouseAtCenter(
+       ".textPanelBox .toolbar button.copy",
+       {}, browser);
+   }, function validator(value) {
+     let str = normalizeNewLines(value);
+     return str == prettyJson;
+   });
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_csp_json.js b/devtools/client/jsonview/test/browser_jsonview_csp_json.js
+--- a/devtools/client/jsonview/test/browser_jsonview_csp_json.js
++++ b/devtools/client/jsonview/test/browser_jsonview_csp_json.js
+@@ -2,16 +2,16 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_JSON_URL = URL_ROOT + "csp_json.json";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test CSP JSON started");
+ 
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+-  let count = yield getElementCount(".jsonPanelBox .treeTable .treeRow");
++  let count = await getElementCount(".jsonPanelBox .treeTable .treeRow");
+   is(count, 1, "There must be one row");
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_empty_object.js b/devtools/client/jsonview/test/browser_jsonview_empty_object.js
+--- a/devtools/client/jsonview/test/browser_jsonview_empty_object.js
++++ b/devtools/client/jsonview/test/browser_jsonview_empty_object.js
+@@ -1,47 +1,47 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ function testRootObject(objExpr, summary = objExpr) {
+-  return function* () {
++  return async function() {
+     info("Test JSON with root empty object " + objExpr + " started");
+ 
+     let TEST_JSON_URL = "data:application/json," + objExpr;
+-    yield addJsonViewTab(TEST_JSON_URL);
++    await addJsonViewTab(TEST_JSON_URL);
+ 
+-    let objectText = yield getElementText(
++    let objectText = await getElementText(
+       ".jsonPanelBox .panelContent");
+     is(objectText, summary, "The root object " + objExpr + " is visible");
+   };
+ }
+ 
+ function testNestedObject(objExpr, summary = objExpr) {
+-  return function* () {
++  return async function() {
+     info("Test JSON with nested empty object " + objExpr + " started");
+ 
+     let TEST_JSON_URL = "data:application/json,[" + objExpr + "]";
+-    yield addJsonViewTab(TEST_JSON_URL);
++    await addJsonViewTab(TEST_JSON_URL);
+ 
+-    let objectCellCount = yield getElementCount(
++    let objectCellCount = await getElementCount(
+       ".jsonPanelBox .treeTable .objectCell");
+     is(objectCellCount, 1, "There must be one object cell");
+ 
+-    let objectCellText = yield getElementText(
++    let objectCellText = await getElementText(
+       ".jsonPanelBox .treeTable .objectCell");
+     is(objectCellText, summary, objExpr + " has a visible summary");
+ 
+     // Collapse auto-expanded node.
+-    yield clickJsonNode(".jsonPanelBox .treeTable .treeLabel");
++    await clickJsonNode(".jsonPanelBox .treeTable .treeLabel");
+ 
+-    let textAfter = yield getElementText(
++    let textAfter = await getElementText(
+       ".jsonPanelBox .treeTable .objectCell");
+     is(textAfter, summary, objExpr + " still has a visible summary");
+   };
+ }
+ 
+ add_task(testRootObject("null"));
+ add_task(testNestedObject("null"));
+ add_task(testNestedObject("[]"));
+diff --git a/devtools/client/jsonview/test/browser_jsonview_encoding.js b/devtools/client/jsonview/test/browser_jsonview_encoding.js
+--- a/devtools/client/jsonview/test/browser_jsonview_encoding.js
++++ b/devtools/client/jsonview/test/browser_jsonview_encoding.js
+@@ -1,16 +1,16 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test JSON encoding started");
+ 
+   const bom = "%EF%BB%BF"; // UTF-8 BOM
+   const tests = [
+     {
+       input: bom,
+       output: ""
+     }, {
+@@ -44,16 +44,16 @@ add_task(function* () {
+       input: "%F0%9F%9A%80",
+       output: "\uD83D\uDE80" // 🚀
+     }
+   ];
+ 
+   for (let {input, output} of tests) {
+     info("Test decoding of " + JSON.stringify(input) + ".");
+ 
+-    yield addJsonViewTab("data:application/json," + input);
+-    yield selectJsonViewContentTab("rawdata");
++    await addJsonViewTab("data:application/json," + input);
++    await selectJsonViewContentTab("rawdata");
+ 
+     // Check displayed data.
+-    let data = yield getElementText(".textPanelBox .data");
++    let data = await getElementText(".textPanelBox .data");
+     is(data, output, "The right data has been received.");
+   }
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_filter.js b/devtools/client/jsonview/test/browser_jsonview_filter.js
+--- a/devtools/client/jsonview/test/browser_jsonview_filter.js
++++ b/devtools/client/jsonview/test/browser_jsonview_filter.js
+@@ -2,27 +2,27 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_JSON_URL = URL_ROOT + "array_json.json";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test valid JSON started");
+ 
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+-  let count = yield getElementCount(".jsonPanelBox .treeTable .treeRow");
++  let count = await getElementCount(".jsonPanelBox .treeTable .treeRow");
+   is(count, 6, "There must be expected number of rows");
+ 
+   // XXX use proper shortcut to focus the filter box
+   // as soon as bug Bug 1178771 is fixed.
+-  yield sendString("h", ".jsonPanelBox .searchBox");
++  await sendString("h", ".jsonPanelBox .searchBox");
+ 
+   // The filtering is done asynchronously so, we need to wait.
+-  yield waitForFilter();
++  await waitForFilter();
+ 
+-  let hiddenCount = yield getElementCount(
++  let hiddenCount = await getElementCount(
+     ".jsonPanelBox .treeTable .treeRow.hidden");
+   is(hiddenCount, 4, "There must be expected number of hidden rows");
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_ignore_charset.js b/devtools/client/jsonview/test/browser_jsonview_ignore_charset.js
+--- a/devtools/client/jsonview/test/browser_jsonview_ignore_charset.js
++++ b/devtools/client/jsonview/test/browser_jsonview_ignore_charset.js
+@@ -1,20 +1,20 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test ignored charset parameter started");
+ 
+   const encodedChar = "%E2%9D%A4"; // In UTF-8 this is a heavy black heart
+   const result = "\u2764"; // ❤
+   const TEST_JSON_URL = "data:application/json;charset=ANSI," + encodedChar;
+ 
+-  yield addJsonViewTab(TEST_JSON_URL);
+-  yield selectJsonViewContentTab("rawdata");
++  await addJsonViewTab(TEST_JSON_URL);
++  await selectJsonViewContentTab("rawdata");
+ 
+-  let text = yield getElementText(".textPanelBox .data");
++  let text = await getElementText(".textPanelBox .data");
+   is(text, result, "The charset parameter is ignored and UTF-8 is used.");
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_invalid_json.js b/devtools/client/jsonview/test/browser_jsonview_invalid_json.js
+--- a/devtools/client/jsonview/test/browser_jsonview_invalid_json.js
++++ b/devtools/client/jsonview/test/browser_jsonview_invalid_json.js
+@@ -2,19 +2,19 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_JSON_URL = URL_ROOT + "invalid_json.json";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test invalid JSON started");
+ 
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+-  let count = yield getElementCount(".jsonPanelBox .treeTable .treeRow");
++  let count = await getElementCount(".jsonPanelBox .treeTable .treeRow");
+   ok(count == 0, "There must be no row");
+ 
+-  let text = yield getElementText(".jsonPanelBox .jsonParseError");
++  let text = await getElementText(".jsonPanelBox .jsonParseError");
+   ok(text, "There must be an error description");
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_manifest.js b/devtools/client/jsonview/test/browser_jsonview_manifest.js
+--- a/devtools/client/jsonview/test/browser_jsonview_manifest.js
++++ b/devtools/client/jsonview/test/browser_jsonview_manifest.js
+@@ -2,16 +2,16 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_JSON_URL = URL_ROOT + "manifest_json.json";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test manifest JSON file started");
+ 
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+-  let count = yield getElementCount(".jsonPanelBox .treeTable .treeRow");
++  let count = await getElementCount(".jsonPanelBox .treeTable .treeRow");
+   is(count, 37, "There must be expected number of rows");
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_nul.js b/devtools/client/jsonview/test/browser_jsonview_nul.js
+--- a/devtools/client/jsonview/test/browser_jsonview_nul.js
++++ b/devtools/client/jsonview/test/browser_jsonview_nul.js
+@@ -1,18 +1,18 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test JSON with NUL started.");
+ 
+   const TEST_JSON_URL = "data:application/json,\"foo_%00_bar\"";
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+-  yield selectJsonViewContentTab("rawdata");
+-  let rawData = yield getElementText(".textPanelBox .data");
++  await selectJsonViewContentTab("rawdata");
++  let rawData = await getElementText(".textPanelBox .data");
+   is(rawData, "\"foo_\u0000_bar\"",
+      "The NUL character has been preserved.");
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_object-type.js b/devtools/client/jsonview/test/browser_jsonview_object-type.js
+--- a/devtools/client/jsonview/test/browser_jsonview_object-type.js
++++ b/devtools/client/jsonview/test/browser_jsonview_object-type.js
+@@ -2,26 +2,26 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const {ELLIPSIS} = require("devtools/shared/l10n");
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test Object type property started");
+ 
+   const TEST_JSON_URL = "data:application/json,{\"x\":{\"type\":\"string\"}}";
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+-  let count = yield getElementCount(".jsonPanelBox .treeTable .treeRow");
++  let count = await getElementCount(".jsonPanelBox .treeTable .treeRow");
+   is(count, 2, "There must be two rows");
+ 
+   // Collapse auto-expanded node.
+-  yield clickJsonNode(".jsonPanelBox .treeTable .treeLabel");
++  await clickJsonNode(".jsonPanelBox .treeTable .treeLabel");
+ 
+-  count = yield getElementCount(".jsonPanelBox .treeTable .treeRow");
++  count = await getElementCount(".jsonPanelBox .treeTable .treeRow");
+   is(count, 1, "There must be one row");
+ 
+-  let label = yield getElementText(".jsonPanelBox .treeTable .objectCell");
++  let label = await getElementText(".jsonPanelBox .treeTable .objectCell");
+   is(label, `{${ELLIPSIS}}`, "The label must be indicating an object");
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_save_json.js b/devtools/client/jsonview/test/browser_jsonview_save_json.js
+--- a/devtools/client/jsonview/test/browser_jsonview_save_json.js
++++ b/devtools/client/jsonview/test/browser_jsonview_save_json.js
+@@ -79,90 +79,90 @@ mockTransferRegisterer.register();
+ MockFilePicker.displayDirectory = destDir;
+ registerCleanupFunction(function() {
+   mockTransferRegisterer.unregister();
+   MockFilePicker.cleanup();
+   destDir.remove(true);
+   ok(!destDir.exists(), "Destination dir should be removed");
+ });
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test 1 save JSON started");
+ 
+   const JSON_FILE = "simple_json.json";
+   const TEST_JSON_URL = URL_ROOT + JSON_FILE;
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+   let promise, rawJSON, prettyJSON;
+-  yield fetch(new Request(TEST_JSON_URL))
++  await fetch(new Request(TEST_JSON_URL))
+     .then(response => response.text())
+     .then(function(data) {
+       info("Fetched JSON contents.");
+       rawJSON = data;
+       prettyJSON = JSON.stringify(JSON.parse(data), null, "  ");
+     });
+ 
+   // Attempt to save original JSON via "Save As" command
+   promise = awaitFileSave(JSON_FILE, "json");
+-  yield new Promise((resolve) => {
++  await new Promise((resolve) => {
+     info("Register to handle popupshown.");
+     document.addEventListener("popupshown", function(event) {
+       info("Context menu opened.");
+       let savePageCommand = document.getElementById("context-savepage");
+       savePageCommand.doCommand();
+       info("SavePage command done.");
+       event.target.hidePopup();
+       info("Context menu hidden.");
+       resolve();
+     }, {once: true});
+     rightClick("body");
+     info("Right clicked.");
+   });
+-  yield promise.then(getFileContents).then(function(data) {
++  await promise.then(getFileContents).then(function(data) {
+     is(data, rawJSON, "Original JSON contents should have been saved.");
+   });
+ 
+   // Attempt to save original JSON via "Save" button
+   promise = awaitFileSave(JSON_FILE, "json");
+-  yield click(saveButton);
++  await click(saveButton);
+   info("Clicked Save button.");
+-  yield promise.then(getFileContents).then(function(data) {
++  await promise.then(getFileContents).then(function(data) {
+     is(data, rawJSON, "Original JSON contents should have been saved.");
+   });
+ 
+   // Attempt to save prettified JSON via "Save" button
+-  yield selectJsonViewContentTab("rawdata");
++  await selectJsonViewContentTab("rawdata");
+   info("Switched to Raw Data tab.");
+-  yield click(prettifyButton);
++  await click(prettifyButton);
+   info("Clicked Pretty Print button.");
+   promise = awaitFileSave(JSON_FILE, "json");
+-  yield click(saveButton);
++  await click(saveButton);
+   info("Clicked Save button.");
+-  yield promise.then(getFileContents).then(function(data) {
++  await promise.then(getFileContents).then(function(data) {
+     is(data, prettyJSON, "Prettified JSON contents should have been saved.");
+   });
+ });
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test 2 save JSON started");
+ 
+   const TEST_JSON_URL = "data:application/json,2";
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+   info("Checking that application/json adds .json extension by default.");
+   let promise = awaitFileSave("index.json", "json");
+-  yield click(saveButton);
++  await click(saveButton);
+   info("Clicked Save button.");
+-  yield promise.then(getFileContents);
++  await promise.then(getFileContents);
+ });
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test 3 save JSON started");
+ 
+   const TEST_JSON_URL = "data:application/manifest+json,3";
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+   info("Checking that application/manifest+json does not add .json extension.");
+   let promise = awaitFileSave("index", null);
+-  yield click(saveButton);
++  await click(saveButton);
+   info("Clicked Save button.");
+-  yield promise.then(getFileContents);
++  await promise.then(getFileContents);
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_slash.js b/devtools/client/jsonview/test/browser_jsonview_slash.js
+--- a/devtools/client/jsonview/test/browser_jsonview_slash.js
++++ b/devtools/client/jsonview/test/browser_jsonview_slash.js
+@@ -1,16 +1,16 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test JSON with slash started.");
+ 
+   const TEST_JSON_URL = "data:application/json,{\"a/b\":[1,2],\"a\":{\"b\":[3,4]}}";
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+-  let countBefore = yield getElementCount(".jsonPanelBox .treeTable .treeRow");
++  let countBefore = await getElementCount(".jsonPanelBox .treeTable .treeRow");
+   is(countBefore, 7, "There must be seven rows");
+ });
+diff --git a/devtools/client/jsonview/test/browser_jsonview_theme.js b/devtools/client/jsonview/test/browser_jsonview_theme.js
+--- a/devtools/client/jsonview/test/browser_jsonview_theme.js
++++ b/devtools/client/jsonview/test/browser_jsonview_theme.js
+@@ -2,33 +2,33 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_JSON_URL = URL_ROOT + "valid_json.json";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test JSON theme started.");
+ 
+   let oldPref = SpecialPowers.getCharPref("devtools.theme");
+   SpecialPowers.setCharPref("devtools.theme", "light");
+ 
+-  yield addJsonViewTab(TEST_JSON_URL);
++  await addJsonViewTab(TEST_JSON_URL);
+ 
+-  is(yield getTheme(), "theme-light", "The initial theme is light");
++  is(await getTheme(), "theme-light", "The initial theme is light");
+ 
+   SpecialPowers.setCharPref("devtools.theme", "dark");
+-  is(yield getTheme(), "theme-dark", "Theme changed to dark");
++  is(await getTheme(), "theme-dark", "Theme changed to dark");
+ 
+   SpecialPowers.setCharPref("devtools.theme", "firebug");
+-  is(yield getTheme(), "theme-firebug", "Theme changed to firebug");
++  is(await getTheme(), "theme-firebug", "Theme changed to firebug");
+ 
+   SpecialPowers.setCharPref("devtools.theme", "light");
+-  is(yield getTheme(), "theme-light", "Theme changed to light");
++  is(await getTheme(), "theme-light", "Theme changed to light");
+ 
+   SpecialPowers.setCharPref("devtools.theme", oldPref);
+ });
+ 
+ function getTheme() {
+   return getElementAttr(":root", "class");
+ }
+diff --git a/devtools/client/jsonview/test/browser_jsonview_valid_json.js b/devtools/client/jsonview/test/browser_jsonview_valid_json.js
+--- a/devtools/client/jsonview/test/browser_jsonview_valid_json.js
++++ b/devtools/client/jsonview/test/browser_jsonview_valid_json.js
+@@ -2,42 +2,42 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_JSON_URL = URL_ROOT + "valid_json.json";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Test valid JSON started");
+ 
+-  let tab = yield addJsonViewTab(TEST_JSON_URL);
++  let tab = await addJsonViewTab(TEST_JSON_URL);
+ 
+   ok(tab.linkedBrowser.contentPrincipal.isNullPrincipal, "Should have null principal");
+ 
+-  is(yield countRows(), 3, "There must be three rows");
++  is(await countRows(), 3, "There must be three rows");
+ 
+-  let objectCellCount = yield getElementCount(
++  let objectCellCount = await getElementCount(
+     ".jsonPanelBox .treeTable .objectCell");
+   is(objectCellCount, 1, "There must be one object cell");
+ 
+-  let objectCellText = yield getElementText(
++  let objectCellText = await getElementText(
+     ".jsonPanelBox .treeTable .objectCell");
+   is(objectCellText, "", "The summary is hidden when object is expanded");
+ 
+   // Clicking the value does not collapse it (so that it can be selected and copied).
+-  yield clickJsonNode(".jsonPanelBox .treeTable .treeValueCell");
+-  is(yield countRows(), 3, "There must still be three rows");
++  await clickJsonNode(".jsonPanelBox .treeTable .treeValueCell");
++  is(await countRows(), 3, "There must still be three rows");
+ 
+   // Clicking the label collapses the auto-expanded node.
+-  yield clickJsonNode(".jsonPanelBox .treeTable .treeLabel");
+-  is(yield countRows(), 1, "There must be one row");
++  await clickJsonNode(".jsonPanelBox .treeTable .treeLabel");
++  is(await countRows(), 1, "There must be one row");
+ 
+   // Collapsed nodes are preserved when switching panels.
+-  yield selectJsonViewContentTab("headers");
+-  yield selectJsonViewContentTab("json");
+-  is(yield countRows(), 1, "There must still be one row");
++  await selectJsonViewContentTab("headers");
++  await selectJsonViewContentTab("json");
++  is(await countRows(), 1, "There must still be one row");
+ });
+ 
+ function countRows() {
+   return getElementCount(".jsonPanelBox .treeTable .treeRow");
+ }

+ 5777 - 0
frg/work-js/mozilla-release/patches/1440321-1i-memory-61a1.patch

@@ -0,0 +1,5777 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  0016065e7bfa51cdae524748c64441b806a47c83
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1i. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/memory/actions/allocations.js b/devtools/client/memory/actions/allocations.js
+--- a/devtools/client/memory/actions/allocations.js
++++ b/devtools/client/memory/actions/allocations.js
+@@ -1,20 +1,20 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+  * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ const { actions, ALLOCATION_RECORDING_OPTIONS } = require("../constants");
+ 
+ exports.toggleRecordingAllocationStacks = function(front) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     dispatch({ type: actions.TOGGLE_RECORD_ALLOCATION_STACKS_START });
+ 
+     if (getState().recordingAllocationStacks) {
+-      yield front.stopRecordingAllocations();
++      await front.stopRecordingAllocations();
+     } else {
+-      yield front.startRecordingAllocations(ALLOCATION_RECORDING_OPTIONS);
++      await front.startRecordingAllocations(ALLOCATION_RECORDING_OPTIONS);
+     }
+ 
+     dispatch({ type: actions.TOGGLE_RECORD_ALLOCATION_STACKS_END });
+   };
+ };
+diff --git a/devtools/client/memory/actions/census-display.js b/devtools/client/memory/actions/census-display.js
+--- a/devtools/client/memory/actions/census-display.js
++++ b/devtools/client/memory/actions/census-display.js
+@@ -3,19 +3,19 @@
+  * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ const { assert } = require("devtools/shared/DevToolsUtils");
+ const { actions } = require("../constants");
+ const { refresh } = require("./refresh");
+ 
+ exports.setCensusDisplayAndRefresh = function(heapWorker, display) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     dispatch(setCensusDisplay(display));
+-    yield dispatch(refresh(heapWorker));
++    await dispatch(refresh(heapWorker));
+   };
+ };
+ 
+ /**
+  * Clears out all cached census data in the snapshots and sets new display data
+  * for censuses.
+  *
+  * @param {censusDisplayModel} display
+diff --git a/devtools/client/memory/actions/diffing.js b/devtools/client/memory/actions/diffing.js
+--- a/devtools/client/memory/actions/diffing.js
++++ b/devtools/client/memory/actions/diffing.js
+@@ -41,17 +41,17 @@ const selectSnapshotForDiffing = exports
+ /**
+  * Compute the difference between the first and second snapshots.
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  * @param {snapshotModel} first
+  * @param {snapshotModel} second
+  */
+ const takeCensusDiff = exports.takeCensusDiff = function(heapWorker, first, second) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     assert(snapshotIsDiffable(first),
+            `First snapshot must be in a diffable state, found ${first.state}`);
+     assert(snapshotIsDiffable(second),
+            `Second snapshot must be in a diffable state, found ${second.state}`);
+ 
+     let report, parentMap;
+     let display = getState().censusDisplay;
+     let filter = getState().filter;
+@@ -83,17 +83,17 @@ const takeCensusDiff = exports.takeCensu
+       });
+ 
+       let opts = display.inverted
+         ? { asInvertedTreeNode: true }
+         : { asTreeNode: true };
+       opts.filter = filter || null;
+ 
+       try {
+-        ({ delta: report, parentMap } = yield heapWorker.takeCensusDiff(
++        ({ delta: report, parentMap } = await heapWorker.takeCensusDiff(
+           first.path,
+           second.path,
+           { breakdown: display.breakdown },
+           opts));
+       } catch (error) {
+         reportException("actions/diffing/takeCensusDiff", error);
+         dispatch({ type: actions.DIFFING_ERROR, error });
+         return;
+@@ -117,17 +117,17 @@ const takeCensusDiff = exports.takeCensu
+ /**
+  * Ensure that the current diffing data is up to date with the currently
+  * selected display, filter, etc. If the state is not up-to-date, then a
+  * recompute is triggered.
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  */
+ const refreshDiffing = exports.refreshDiffing = function(heapWorker) {
+-  return function* (dispatch, getState) {
++  return function(dispatch, getState) {
+     if (getState().diffing.secondSnapshotId === null) {
+       return;
+     }
+ 
+     assert(getState().diffing.firstSnapshotId,
+            "Should have first snapshot id");
+ 
+     if (getState().diffing.state === diffingState.TAKING_DIFF) {
+@@ -147,21 +147,21 @@ const refreshDiffing = exports.refreshDi
+ /**
+  * Select the given snapshot for diffing and refresh the diffing data if
+  * necessary (for example, if two snapshots are now selected for diffing).
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  * @param {snapshotModel} snapshot
+  */
+ exports.selectSnapshotForDiffingAndRefresh = function(heapWorker, snapshot) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     assert(getState().diffing,
+            "If we are selecting for diffing, we must be in diffing mode");
+     dispatch(selectSnapshotForDiffing(snapshot));
+-    yield dispatch(refreshDiffing(heapWorker));
++    await dispatch(refreshDiffing(heapWorker));
+   };
+ };
+ 
+ /**
+  * Expand the given node in the diffing's census's delta-report.
+  *
+  * @param {CensusTreeNode} node
+  */
+diff --git a/devtools/client/memory/actions/io.js b/devtools/client/memory/actions/io.js
+--- a/devtools/client/memory/actions/io.js
++++ b/devtools/client/memory/actions/io.js
+@@ -10,81 +10,81 @@ const { OS } = require("resource://gre/m
+ const {
+   selectSnapshot,
+   computeSnapshotData,
+   readSnapshot
+ } = require("./snapshot");
+ const VALID_EXPORT_STATES = [states.SAVED, states.READ];
+ 
+ exports.pickFileAndExportSnapshot = function(snapshot) {
+-  return function* (dispatch, getState) {
+-    let outputFile = yield openFilePicker({
++  return async function(dispatch, getState) {
++    let outputFile = await openFilePicker({
+       title: L10N.getFormatStr("snapshot.io.save.window"),
+       defaultName: OS.Path.basename(snapshot.path),
+       filters: [[L10N.getFormatStr("snapshot.io.filter"), "*.fxsnapshot"]],
+       mode: "save",
+     });
+ 
+     if (!outputFile) {
+       return;
+     }
+ 
+-    yield dispatch(exportSnapshot(snapshot, outputFile.path));
++    await dispatch(exportSnapshot(snapshot, outputFile.path));
+   };
+ };
+ 
+ const exportSnapshot = exports.exportSnapshot = function(snapshot, dest) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     dispatch({ type: actions.EXPORT_SNAPSHOT_START, snapshot });
+ 
+     assert(VALID_EXPORT_STATES.includes(snapshot.state),
+       `Snapshot is in invalid state for exporting: ${snapshot.state}`);
+ 
+     try {
+-      yield OS.File.copy(snapshot.path, dest);
++      await OS.File.copy(snapshot.path, dest);
+     } catch (error) {
+       reportException("exportSnapshot", error);
+       dispatch({ type: actions.EXPORT_SNAPSHOT_ERROR, snapshot, error });
+     }
+ 
+     dispatch({ type: actions.EXPORT_SNAPSHOT_END, snapshot });
+   };
+ };
+ 
+ exports.pickFileAndImportSnapshotAndCensus = function(heapWorker) {
+-  return function* (dispatch, getState) {
+-    let input = yield openFilePicker({
++  return async function(dispatch, getState) {
++    let input = await openFilePicker({
+       title: L10N.getFormatStr("snapshot.io.import.window"),
+       filters: [[L10N.getFormatStr("snapshot.io.filter"), "*.fxsnapshot"]],
+       mode: "open",
+     });
+ 
+     if (!input) {
+       return;
+     }
+ 
+-    yield dispatch(importSnapshotAndCensus(heapWorker, input.path));
++    await dispatch(importSnapshotAndCensus(heapWorker, input.path));
+   };
+ };
+ 
+ const importSnapshotAndCensus = function(heapWorker, path) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     const snapshot = immutableUpdate(createSnapshot(getState()), {
+       path,
+       state: states.IMPORTING,
+       imported: true,
+     });
+     const id = snapshot.id;
+ 
+     dispatch({ type: actions.IMPORT_SNAPSHOT_START, snapshot });
+     dispatch(selectSnapshot(snapshot.id));
+ 
+     try {
+-      yield dispatch(readSnapshot(heapWorker, id));
+-      yield dispatch(computeSnapshotData(heapWorker, id));
++      await dispatch(readSnapshot(heapWorker, id));
++      await dispatch(computeSnapshotData(heapWorker, id));
+     } catch (error) {
+       reportException("importSnapshot", error);
+       dispatch({ type: actions.IMPORT_SNAPSHOT_ERROR, error, id });
+     }
+ 
+     dispatch({ type: actions.IMPORT_SNAPSHOT_END, id });
+   };
+ };
+diff --git a/devtools/client/memory/actions/label-display.js b/devtools/client/memory/actions/label-display.js
+--- a/devtools/client/memory/actions/label-display.js
++++ b/devtools/client/memory/actions/label-display.js
+@@ -7,20 +7,20 @@ const { assert } = require("devtools/sha
+ const { actions } = require("../constants");
+ const { refresh } = require("./refresh");
+ 
+ /**
+  * Change the display we use for labeling individual nodes and refresh the
+  * current data.
+  */
+ exports.setLabelDisplayAndRefresh = function(heapWorker, display) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     // Clears out all stored census data and sets the display.
+     dispatch(setLabelDisplay(display));
+-    yield dispatch(refresh(heapWorker));
++    await dispatch(refresh(heapWorker));
+   };
+ };
+ 
+ /**
+  * Change the display we use for labeling individual nodes.
+  *
+  * @param {labelDisplayModel} display
+  */
+diff --git a/devtools/client/memory/actions/refresh.js b/devtools/client/memory/actions/refresh.js
+--- a/devtools/client/memory/actions/refresh.js
++++ b/devtools/client/memory/actions/refresh.js
+@@ -9,36 +9,36 @@ const { refreshDiffing } = require("./di
+ const snapshot = require("./snapshot");
+ 
+ /**
+  * Refresh the main thread's data from the heap analyses worker, if needed.
+  *
+  * @param {HeapAnalysesWorker} heapWorker
+  */
+ exports.refresh = function(heapWorker) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     switch (getState().view.state) {
+       case viewState.DIFFING:
+         assert(getState().diffing, "Should have diffing state if in diffing view");
+-        yield dispatch(refreshDiffing(heapWorker));
++        await dispatch(refreshDiffing(heapWorker));
+         return;
+ 
+       case viewState.CENSUS:
+-        yield dispatch(snapshot.refreshSelectedCensus(heapWorker));
++        await dispatch(snapshot.refreshSelectedCensus(heapWorker));
+         return;
+ 
+       case viewState.DOMINATOR_TREE:
+-        yield dispatch(snapshot.refreshSelectedDominatorTree(heapWorker));
++        await dispatch(snapshot.refreshSelectedDominatorTree(heapWorker));
+         return;
+ 
+       case viewState.TREE_MAP:
+-        yield dispatch(snapshot.refreshSelectedTreeMap(heapWorker));
++        await dispatch(snapshot.refreshSelectedTreeMap(heapWorker));
+         return;
+ 
+       case viewState.INDIVIDUALS:
+-        yield dispatch(snapshot.refreshIndividuals(heapWorker));
++        await dispatch(snapshot.refreshIndividuals(heapWorker));
+         return;
+ 
+       default:
+         assert(false, `Unexpected view state: ${getState().view.state}`);
+     }
+   };
+ };
+diff --git a/devtools/client/memory/actions/snapshot.js b/devtools/client/memory/actions/snapshot.js
+--- a/devtools/client/memory/actions/snapshot.js
++++ b/devtools/client/memory/actions/snapshot.js
+@@ -29,94 +29,94 @@ const TaskCache = require("./task-cache"
+  * A series of actions are fired from this task to save, read and generate the
+  * initial census from a snapshot.
+  *
+  * @param {MemoryFront}
+  * @param {HeapAnalysesClient}
+  * @param {Object}
+  */
+ exports.takeSnapshotAndCensus = function(front, heapWorker) {
+-  return function* (dispatch, getState) {
+-    const id = yield dispatch(takeSnapshot(front));
++  return async function(dispatch, getState) {
++    const id = await dispatch(takeSnapshot(front));
+     if (id === null) {
+       return;
+     }
+ 
+-    yield dispatch(readSnapshot(heapWorker, id));
++    await dispatch(readSnapshot(heapWorker, id));
+     if (getSnapshot(getState(), id).state !== states.READ) {
+       return;
+     }
+ 
+-    yield dispatch(computeSnapshotData(heapWorker, id));
++    await dispatch(computeSnapshotData(heapWorker, id));
+   };
+ };
+ 
+ /**
+  * Create the census for the snapshot with the provided snapshot id. If the
+  * current view is the DOMINATOR_TREE view, create the dominator tree for this
+  * snapshot as well.
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  * @param {snapshotId} id
+  */
+ const computeSnapshotData = exports.computeSnapshotData = function(heapWorker, id) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     if (getSnapshot(getState(), id).state !== states.READ) {
+       return;
+     }
+ 
+     // Decide which type of census to take.
+     const censusTaker = getCurrentCensusTaker(getState().view.state);
+-    yield dispatch(censusTaker(heapWorker, id));
++    await dispatch(censusTaker(heapWorker, id));
+ 
+     if (getState().view.state === viewState.DOMINATOR_TREE &&
+         !getSnapshot(getState(), id).dominatorTree) {
+-      yield dispatch(computeAndFetchDominatorTree(heapWorker, id));
++      await dispatch(computeAndFetchDominatorTree(heapWorker, id));
+     }
+   };
+ };
+ 
+ /**
+  * Selects a snapshot and if the snapshot's census is using a different
+  * display, take a new census.
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  * @param {snapshotId} id
+  */
+ exports.selectSnapshotAndRefresh = function(heapWorker, id) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     if (getState().diffing || getState().individuals) {
+       dispatch(view.changeView(viewState.CENSUS));
+     }
+ 
+     dispatch(selectSnapshot(id));
+-    yield dispatch(refresh.refresh(heapWorker));
++    await dispatch(refresh.refresh(heapWorker));
+   };
+ };
+ 
+ /**
+  * Take a snapshot and return its id on success, or null on failure.
+  *
+  * @param {MemoryFront} front
+  * @returns {Number|null}
+  */
+ const takeSnapshot = exports.takeSnapshot = function(front) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     if (getState().diffing || getState().individuals) {
+       dispatch(view.changeView(viewState.CENSUS));
+     }
+ 
+     const snapshot = createSnapshot(getState());
+     const id = snapshot.id;
+     dispatch({ type: actions.TAKE_SNAPSHOT_START, snapshot });
+     dispatch(selectSnapshot(id));
+ 
+     let path;
+     try {
+-      path = yield front.saveHeapSnapshot();
++      path = await front.saveHeapSnapshot();
+     } catch (error) {
+       reportException("takeSnapshot", error);
+       dispatch({ type: actions.SNAPSHOT_ERROR, id, error });
+       return null;
+     }
+ 
+     dispatch({ type: actions.TAKE_SNAPSHOT_END, id, path });
+     return snapshot.id;
+@@ -131,27 +131,27 @@ const takeSnapshot = exports.takeSnapsho
+  * @param {snapshotId} id
+  */
+ const readSnapshot = exports.readSnapshot =
+ TaskCache.declareCacheableTask({
+   getCacheKey(_, id) {
+     return id;
+   },
+ 
+-  task: function* (heapWorker, id, removeFromCache, dispatch, getState) {
++  task: async function(heapWorker, id, removeFromCache, dispatch, getState) {
+     const snapshot = getSnapshot(getState(), id);
+     assert([states.SAVED, states.IMPORTING].includes(snapshot.state),
+            `Should only read a snapshot once. Found snapshot in state ${snapshot.state}`);
+ 
+     let creationTime;
+ 
+     dispatch({ type: actions.READ_SNAPSHOT_START, id });
+     try {
+-      yield heapWorker.readHeapSnapshot(snapshot.path);
+-      creationTime = yield heapWorker.getCreationTime(snapshot.path);
++      await heapWorker.readHeapSnapshot(snapshot.path);
++      creationTime = await heapWorker.getCreationTime(snapshot.path);
+     } catch (error) {
+       removeFromCache();
+       reportException("readSnapshot", error);
+       dispatch({ type: actions.SNAPSHOT_ERROR, id, error });
+       return;
+     }
+ 
+     removeFromCache();
+@@ -183,17 +183,17 @@ function makeTakeCensusTask({ getDisplay
+    * @see `js/src/doc/Debugger/Debugger.Memory.md` for breakdown details
+    */
+   let thisTakeCensusTaskId = ++takeCensusTaskCounter;
+   return TaskCache.declareCacheableTask({
+     getCacheKey(_, id) {
+       return `take-census-task-${thisTakeCensusTaskId}-${id}`;
+     },
+ 
+-    task: function* (heapWorker, id, removeFromCache, dispatch, getState) {
++    task: async function(heapWorker, id, removeFromCache, dispatch, getState) {
+       const snapshot = getSnapshot(getState(), id);
+       if (!snapshot) {
+         removeFromCache();
+         return;
+       }
+ 
+       // Assert that snapshot is in a valid state
+       assert(canTakeCensus(snapshot),
+@@ -227,17 +227,17 @@ function makeTakeCensusTask({ getDisplay
+ 
+         let opts = display.inverted
+           ? { asInvertedTreeNode: true }
+           : { asTreeNode: true };
+ 
+         opts.filter = filter || null;
+ 
+         try {
+-          ({ report, parentMap } = yield heapWorker.takeCensus(
++          ({ report, parentMap } = await heapWorker.takeCensus(
+             snapshot.path,
+             { breakdown: display.breakdown },
+             opts));
+         } catch (error) {
+           removeFromCache();
+           reportException("takeCensus", error);
+           dispatch({ type: errorAction, id, error });
+           return;
+@@ -332,27 +332,27 @@ exports.focusIndividual = function(node)
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  * @param {SnapshotId} id
+  * @param {Object} censusBreakdown
+  * @param {Set<Number> | Number} reportLeafIndex
+  */
+ const fetchIndividuals = exports.fetchIndividuals =
+ function(heapWorker, id, censusBreakdown, reportLeafIndex) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     if (getState().view.state !== viewState.INDIVIDUALS) {
+       dispatch(view.changeView(viewState.INDIVIDUALS));
+     }
+ 
+     const snapshot = getSnapshot(getState(), id);
+     assert(snapshot && snapshot.state === states.READ,
+            "The snapshot should already be read into memory");
+ 
+     if (!dominatorTreeIsComputed(snapshot)) {
+-      yield dispatch(computeAndFetchDominatorTree(heapWorker, id));
++      await dispatch(computeAndFetchDominatorTree(heapWorker, id));
+     }
+ 
+     const snapshot_ = getSnapshot(getState(), id);
+     assert(snapshot_.dominatorTree && snapshot_.dominatorTree.root,
+            "Should have a dominator tree with a root.");
+ 
+     const dominatorTreeId = snapshot_.dominatorTree.dominatorTreeId;
+ 
+@@ -371,17 +371,17 @@ function(heapWorker, id, censusBreakdown
+         // We switched views while in the process of fetching individuals -- any
+         // further work is useless.
+         return;
+       }
+ 
+       dispatch({ type: actions.FETCH_INDIVIDUALS_START });
+ 
+       try {
+-        ({ nodes } = yield heapWorker.getCensusIndividuals({
++        ({ nodes } = await heapWorker.getCensusIndividuals({
+           dominatorTreeId,
+           indices,
+           censusBreakdown,
+           labelBreakdown: labelDisplay.breakdown,
+           maxRetainingPaths: Preferences.get("devtools.memory.max-retaining-paths"),
+           maxIndividuals: Preferences.get("devtools.memory.max-individuals"),
+         }));
+       } catch (error) {
+@@ -405,17 +405,17 @@ function(heapWorker, id, censusBreakdown
+ };
+ 
+ /**
+  * Refresh the current individuals view.
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  */
+ exports.refreshIndividuals = function(heapWorker) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     assert(getState().view.state === viewState.INDIVIDUALS,
+            "Should be in INDIVIDUALS view.");
+ 
+     const { individuals } = getState();
+ 
+     switch (individuals.state) {
+       case individualsState.COMPUTING_DOMINATOR_TREE:
+       case individualsState.FETCHING:
+@@ -432,71 +432,71 @@ exports.refreshIndividuals = function(he
+         // Doesn't hurt to retry: maybe we won't get an error this time around?
+         break;
+ 
+       default:
+         assert(false, `Unexpected individuals state: ${individuals.state}`);
+         return;
+     }
+ 
+-    yield dispatch(fetchIndividuals(heapWorker,
++    await dispatch(fetchIndividuals(heapWorker,
+                                     individuals.id,
+                                     individuals.censusBreakdown,
+                                     individuals.indices));
+   };
+ };
+ 
+ /**
+  * Refresh the selected snapshot's census data, if need be (for example,
+  * display configuration changed).
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  */
+ exports.refreshSelectedCensus = function(heapWorker) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     let snapshot = getState().snapshots.find(s => s.selected);
+     if (!snapshot || snapshot.state !== states.READ) {
+       return;
+     }
+ 
+     // Intermediate snapshot states will get handled by the task action that is
+     // orchestrating them. For example, if the snapshot census's state is
+     // SAVING, then the takeCensus action will keep taking a census until
+     // the inverted property matches the inverted state. If the snapshot is
+     // still in the process of being saved or read, the takeSnapshotAndCensus
+     // task action will follow through and ensure that a census is taken.
+     if ((snapshot.census && snapshot.census.state === censusState.SAVED) ||
+         !snapshot.census) {
+-      yield dispatch(takeCensus(heapWorker, snapshot.id));
++      await dispatch(takeCensus(heapWorker, snapshot.id));
+     }
+   };
+ };
+ 
+ /**
+  * Refresh the selected snapshot's tree map data, if need be (for example,
+  * display configuration changed).
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  */
+ exports.refreshSelectedTreeMap = function(heapWorker) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     let snapshot = getState().snapshots.find(s => s.selected);
+     if (!snapshot || snapshot.state !== states.READ) {
+       return;
+     }
+ 
+     // Intermediate snapshot states will get handled by the task action that is
+     // orchestrating them. For example, if the snapshot census's state is
+     // SAVING, then the takeCensus action will keep taking a census until
+     // the inverted property matches the inverted state. If the snapshot is
+     // still in the process of being saved or read, the takeSnapshotAndCensus
+     // task action will follow through and ensure that a census is taken.
+     if ((snapshot.treeMap && snapshot.treeMap.state === treeMapState.SAVED) ||
+         !snapshot.treeMap) {
+-      yield dispatch(takeTreeMap(heapWorker, snapshot.id));
++      await dispatch(takeTreeMap(heapWorker, snapshot.id));
+     }
+   };
+ };
+ 
+ /**
+  * Request that the `HeapAnalysesWorker` compute the dominator tree for the
+  * snapshot with the given `id`.
+  *
+@@ -506,26 +506,26 @@ exports.refreshSelectedTreeMap = functio
+  * @returns {Promise<DominatorTreeId>}
+  */
+ const computeDominatorTree = exports.computeDominatorTree =
+ TaskCache.declareCacheableTask({
+   getCacheKey(_, id) {
+     return id;
+   },
+ 
+-  task: function* (heapWorker, id, removeFromCache, dispatch, getState) {
++  task: async function(heapWorker, id, removeFromCache, dispatch, getState) {
+     const snapshot = getSnapshot(getState(), id);
+     assert(!(snapshot.dominatorTree && snapshot.dominatorTree.dominatorTreeId),
+            "Should not re-compute dominator trees");
+ 
+     dispatch({ type: actions.COMPUTE_DOMINATOR_TREE_START, id });
+ 
+     let dominatorTreeId;
+     try {
+-      dominatorTreeId = yield heapWorker.computeDominatorTree(snapshot.path);
++      dominatorTreeId = await heapWorker.computeDominatorTree(snapshot.path);
+     } catch (error) {
+       removeFromCache();
+       reportException("actions/snapshot/computeDominatorTree", error);
+       dispatch({ type: actions.DOMINATOR_TREE_ERROR, id, error });
+       return null;
+     }
+ 
+     removeFromCache();
+@@ -544,32 +544,32 @@ TaskCache.declareCacheableTask({
+  * @returns {Promise<DominatorTreeNode>}
+  */
+ const fetchDominatorTree = exports.fetchDominatorTree =
+ TaskCache.declareCacheableTask({
+   getCacheKey(_, id) {
+     return id;
+   },
+ 
+-  task: function* (heapWorker, id, removeFromCache, dispatch, getState) {
++  task: async function(heapWorker, id, removeFromCache, dispatch, getState) {
+     const snapshot = getSnapshot(getState(), id);
+     assert(dominatorTreeIsComputed(snapshot),
+            "Should have dominator tree model and it should be computed");
+ 
+     let display;
+     let root;
+     do {
+       display = getState().labelDisplay;
+       assert(display && display.breakdown,
+              `Should have a breakdown to describe nodes with, got: ${uneval(display)}`);
+ 
+       dispatch({ type: actions.FETCH_DOMINATOR_TREE_START, id, display });
+ 
+       try {
+-        root = yield heapWorker.getDominatorTree({
++        root = await heapWorker.getDominatorTree({
+           dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
+           breakdown: display.breakdown,
+           maxRetainingPaths: Preferences.get("devtools.memory.max-retaining-paths"),
+         });
+       } catch (error) {
+         removeFromCache();
+         reportException("actions/snapshot/fetchDominatorTree", error);
+         dispatch({ type: actions.DOMINATOR_TREE_ERROR, id, error });
+@@ -592,34 +592,35 @@ TaskCache.declareCacheableTask({
+  * @param {SnapshotId} id
+  * @param {DominatorTreeLazyChildren} lazyChildren
+  */
+ exports.fetchImmediatelyDominated = TaskCache.declareCacheableTask({
+   getCacheKey(_, id, lazyChildren) {
+     return `${id}-${lazyChildren.key()}`;
+   },
+ 
+-  task: function* (heapWorker, id, lazyChildren, removeFromCache, dispatch, getState) {
++  task: async function(heapWorker, id, lazyChildren, removeFromCache, dispatch,
++                        getState) {
+     const snapshot = getSnapshot(getState(), id);
+     assert(snapshot.dominatorTree, "Should have dominator tree model");
+     assert(snapshot.dominatorTree.state === dominatorTreeState.LOADED ||
+            snapshot.dominatorTree.state === dominatorTreeState.INCREMENTAL_FETCHING,
+            "Cannot fetch immediately dominated nodes in a dominator tree unless " +
+            " the dominator tree has already been computed");
+ 
+     let display;
+     let response;
+     do {
+       display = getState().labelDisplay;
+       assert(display, "Should have a display to describe nodes with.");
+ 
+       dispatch({ type: actions.FETCH_IMMEDIATELY_DOMINATED_START, id });
+ 
+       try {
+-        response = yield heapWorker.getImmediatelyDominated({
++        response = await heapWorker.getImmediatelyDominated({
+           dominatorTreeId: snapshot.dominatorTree.dominatorTreeId,
+           breakdown: display.breakdown,
+           nodeId: lazyChildren.parentNodeId(),
+           startIndex: lazyChildren.siblingIndex(),
+           maxRetainingPaths: Preferences.get("devtools.memory.max-retaining-paths"),
+         });
+       } catch (error) {
+         removeFromCache();
+@@ -651,41 +652,41 @@ exports.fetchImmediatelyDominated = Task
+  * @returns {Promise<DominatorTreeNode>}
+  */
+ const computeAndFetchDominatorTree = exports.computeAndFetchDominatorTree =
+ TaskCache.declareCacheableTask({
+   getCacheKey(_, id) {
+     return id;
+   },
+ 
+-  task: function* (heapWorker, id, removeFromCache, dispatch, getState) {
+-    const dominatorTreeId = yield dispatch(computeDominatorTree(heapWorker, id));
++  task: async function(heapWorker, id, removeFromCache, dispatch, getState) {
++    const dominatorTreeId = await dispatch(computeDominatorTree(heapWorker, id));
+     if (dominatorTreeId === null) {
+       removeFromCache();
+       return null;
+     }
+ 
+-    const root = yield dispatch(fetchDominatorTree(heapWorker, id));
++    const root = await dispatch(fetchDominatorTree(heapWorker, id));
+     removeFromCache();
+ 
+     if (!root) {
+       return null;
+     }
+ 
+     return root;
+   }
+ });
+ 
+ /**
+  * Update the currently selected snapshot's dominator tree.
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  */
+ exports.refreshSelectedDominatorTree = function(heapWorker) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     let snapshot = getState().snapshots.find(s => s.selected);
+     if (!snapshot) {
+       return;
+     }
+ 
+     if (snapshot.dominatorTree &&
+         !(snapshot.dominatorTree.state === dominatorTreeState.COMPUTED ||
+           snapshot.dominatorTree.state === dominatorTreeState.LOADED ||
+@@ -693,19 +694,19 @@ exports.refreshSelectedDominatorTree = f
+       return;
+     }
+ 
+     // We need to check for the snapshot state because if there was an error,
+     // we can't continue and if we are still saving or reading the snapshot,
+     // then takeSnapshotAndCensus will finish the job for us
+     if (snapshot.state === states.READ) {
+       if (snapshot.dominatorTree) {
+-        yield dispatch(fetchDominatorTree(heapWorker, snapshot.id));
++        await dispatch(fetchDominatorTree(heapWorker, snapshot.id));
+       } else {
+-        yield dispatch(computeAndFetchDominatorTree(heapWorker, snapshot.id));
++        await dispatch(computeAndFetchDominatorTree(heapWorker, snapshot.id));
+       }
+     }
+   };
+ };
+ 
+ /**
+  * Select the snapshot with the given id.
+  *
+@@ -720,17 +721,17 @@ const selectSnapshot = exports.selectSna
+ };
+ 
+ /**
+  * Delete all snapshots that are in the READ or ERROR state
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  */
+ exports.clearSnapshots = function(heapWorker) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     let snapshots = getState().snapshots.filter(s => {
+       let snapshotReady = s.state === states.READ || s.state === states.ERROR;
+       let censusReady = (s.treeMap && s.treeMap.state === treeMapState.SAVED) ||
+                         (s.census && s.census.state === censusState.SAVED);
+ 
+       return snapshotReady && censusReady;
+     });
+ 
+@@ -740,17 +741,17 @@ exports.clearSnapshots = function(heapWo
+ 
+     if (getState().diffing) {
+       dispatch(diffing.toggleDiffing());
+     }
+     if (getState().individuals) {
+       dispatch(view.popView());
+     }
+ 
+-    yield Promise.all(snapshots.map(snapshot => {
++    await Promise.all(snapshots.map(snapshot => {
+       return heapWorker.deleteHeapSnapshot(snapshot.path).catch(error => {
+         reportException("clearSnapshots", error);
+         dispatch({ type: actions.SNAPSHOT_ERROR, id: snapshot.id, error });
+       });
+     }));
+ 
+     dispatch({ type: actions.DELETE_SNAPSHOTS_END, ids });
+   };
+@@ -758,21 +759,21 @@ exports.clearSnapshots = function(heapWo
+ 
+ /**
+  * Delete a snapshot
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  * @param {snapshotModel} snapshot
+  */
+ exports.deleteSnapshot = function(heapWorker, snapshot) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     dispatch({ type: actions.DELETE_SNAPSHOTS_START, ids: [snapshot.id] });
+ 
+     try {
+-      yield heapWorker.deleteHeapSnapshot(snapshot.path);
++      await heapWorker.deleteHeapSnapshot(snapshot.path);
+     } catch (error) {
+       reportException("deleteSnapshot", error);
+       dispatch({ type: actions.SNAPSHOT_ERROR, id: snapshot.id, error });
+     }
+ 
+     dispatch({ type: actions.DELETE_SNAPSHOTS_END, ids: [snapshot.id] });
+   };
+ };
+diff --git a/devtools/client/memory/actions/task-cache.js b/devtools/client/memory/actions/task-cache.js
+--- a/devtools/client/memory/actions/task-cache.js
++++ b/devtools/client/memory/actions/task-cache.js
+@@ -60,40 +60,40 @@ const TaskCache = module.exports = class
+  * @param {Generator(...args) -> Any} task
+  *
+  * @returns Cacheable, Action-Creating Task
+  */
+ TaskCache.declareCacheableTask = function({ getCacheKey, task }) {
+   const cache = new TaskCache();
+ 
+   return function(...args) {
+-    return function* (dispatch, getState) {
++    return async function(dispatch, getState) {
+       const key = getCacheKey(...args);
+ 
+       const extantResult = cache.get(key);
+       if (extantResult) {
+         return extantResult;
+       }
+ 
+       // Ensure that we have our new entry in the cache *before* dispatching the
+       // task!
+       let resolve;
+       cache.put(key, new Promise(r => {
+         resolve = r;
+       }));
+ 
+-      resolve(dispatch(function* () {
++      resolve(dispatch(async function() {
+         try {
+           args.push(() => cache.remove(key), dispatch, getState);
+-          return yield* task(...args);
++          return await task(...args);
+         } catch (error) {
+           // Don't perma-cache errors.
+           if (cache.get(key)) {
+             cache.remove(key);
+           }
+           throw error;
+         }
+       }));
+ 
+-      return yield cache.get(key);
++      return cache.get(key);
+     };
+   };
+ };
+diff --git a/devtools/client/memory/actions/tree-map-display.js b/devtools/client/memory/actions/tree-map-display.js
+--- a/devtools/client/memory/actions/tree-map-display.js
++++ b/devtools/client/memory/actions/tree-map-display.js
+@@ -6,19 +6,19 @@
+ const { assert } = require("devtools/shared/DevToolsUtils");
+ const { actions } = require("../constants");
+ const { refresh } = require("./refresh");
+ /**
+  * Sets the tree map display as the current display and refreshes the tree map
+  * census.
+  */
+ exports.setTreeMapAndRefresh = function(heapWorker, display) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     dispatch(setTreeMap(display));
+-    yield dispatch(refresh(heapWorker));
++    await dispatch(refresh(heapWorker));
+   };
+ };
+ 
+ /**
+  * Clears out all cached census data in the snapshots and sets new display data
+  * for tree maps.
+  *
+  * @param {treeMapModel} display
+diff --git a/devtools/client/memory/actions/view.js b/devtools/client/memory/actions/view.js
+--- a/devtools/client/memory/actions/view.js
++++ b/devtools/client/memory/actions/view.js
+@@ -42,26 +42,26 @@ const popView = exports.popView = functi
+ /**
+  * Change the currently selected view and ensure all our data is up to date from
+  * the heap worker.
+  *
+  * @param {viewState} view
+  * @param {HeapAnalysesClient} heapWorker
+  */
+ exports.changeViewAndRefresh = function(view, heapWorker) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     dispatch(changeView(view));
+-    yield dispatch(refresh.refresh(heapWorker));
++    await dispatch(refresh.refresh(heapWorker));
+   };
+ };
+ 
+ /**
+  * Given that we are in the INDIVIDUALS view state, go back to the state we were
+  * previously in and refresh our data.
+  *
+  * @param {HeapAnalysesClient} heapWorker
+  */
+ exports.popViewAndRefresh = function(heapWorker) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     dispatch(popView());
+-    yield dispatch(refresh.refresh(heapWorker));
++    await dispatch(refresh.refresh(heapWorker));
+   };
+ };
+diff --git a/devtools/client/memory/initializer.js b/devtools/client/memory/initializer.js
+--- a/devtools/client/memory/initializer.js
++++ b/devtools/client/memory/initializer.js
+@@ -7,17 +7,16 @@
+ "use strict";
+ 
+ const BrowserLoaderModule = {};
+ ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
+ const { require } = BrowserLoaderModule.BrowserLoader({
+   baseURI: "resource://devtools/client/memory/",
+   window
+ });
+-const { Task } = require("devtools/shared/task");
+ const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
+ const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+ const { Provider } = require("devtools/client/shared/vendor/react-redux");
+ const App = createFactory(require("devtools/client/memory/app"));
+ const Store = require("devtools/client/memory/store");
+ const { assert } = require("devtools/shared/DevToolsUtils");
+ const Promise = require("Promise");
+ 
+@@ -27,34 +26,34 @@ const Promise = require("Promise");
+  */
+ var gToolbox, gFront, gHeapAnalysesClient;
+ 
+ /**
+  * Variables set by `initialize()`
+  */
+ var gStore, gRoot, gApp, gProvider, unsubscribe, isHighlighted;
+ 
+-var initialize = Task.async(function* () {
++var initialize = async function() {
+   gRoot = document.querySelector("#app");
+   gStore = Store();
+   gApp = createElement(App,
+     { toolbox: gToolbox, front: gFront, heapWorker: gHeapAnalysesClient });
+   gProvider = createElement(Provider, { store: gStore }, gApp);
+   ReactDOM.render(gProvider, gRoot);
+   unsubscribe = gStore.subscribe(onStateChange);
+-});
++};
+ 
+-var destroy = Task.async(function* () {
++var destroy = async function() {
+   const ok = ReactDOM.unmountComponentAtNode(gRoot);
+   assert(ok, "Should successfully unmount the memory tool's top level React component");
+ 
+   unsubscribe();
+ 
+   gStore = gRoot = gApp = gProvider = unsubscribe = isHighlighted = null;
+-});
++};
+ 
+ /**
+  * Fired on any state change, currently only handles toggling
+  * the highlighting of the tool when recording allocations.
+  */
+ function onStateChange() {
+   let isRecording = gStore.getState().allocations.recording;
+   if (isRecording === isHighlighted) {
+diff --git a/devtools/client/memory/panel.js b/devtools/client/memory/panel.js
+--- a/devtools/client/memory/panel.js
++++ b/devtools/client/memory/panel.js
+@@ -1,73 +1,72 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+-const { Task } = require("devtools/shared/task");
+ const EventEmitter = require("devtools/shared/old-event-emitter");
+ const { MemoryFront } = require("devtools/shared/fronts/memory");
+ const HeapAnalysesClient = require("devtools/shared/heapsnapshot/HeapAnalysesClient");
+ 
+ function MemoryPanel(iframeWindow, toolbox) {
+   this.panelWin = iframeWindow;
+   this._toolbox = toolbox;
+ 
+   EventEmitter.decorate(this);
+ }
+ 
+ MemoryPanel.prototype = {
+-  open: Task.async(function* () {
++  async open() {
+     if (this._opening) {
+       return this._opening;
+     }
+ 
+     this.panelWin.gToolbox = this._toolbox;
+     this.panelWin.gTarget = this.target;
+ 
+-    const rootForm = yield this.target.root;
++    const rootForm = await this.target.root;
+     this.panelWin.gFront = new MemoryFront(this.target.client,
+                                            this.target.form,
+                                            rootForm);
+     this.panelWin.gHeapAnalysesClient = new HeapAnalysesClient();
+ 
+-    yield this.panelWin.gFront.attach();
++    await this.panelWin.gFront.attach();
+ 
+     this._opening = this.panelWin.initialize().then(() => {
+       this.isReady = true;
+       this.emit("ready");
+       return this;
+     });
+ 
+     return this._opening;
+-  }),
++  },
+ 
+   // DevToolPanel API
+ 
+   get target() {
+     return this._toolbox.target;
+   },
+ 
+-  destroy: Task.async(function* () {
++  async destroy() {
+     // Make sure this panel is not already destroyed.
+     if (this._destroyer) {
+       return this._destroyer;
+     }
+ 
+-    yield this.panelWin.gFront.detach();
++    await this.panelWin.gFront.detach();
+ 
+     this._destroyer = this.panelWin.destroy().then(() => {
+       // Destroy front to ensure packet handler is removed from client
+       this.panelWin.gFront.destroy();
+       this.panelWin.gHeapAnalysesClient.destroy();
+       this.panelWin = null;
+       this._opening = null;
+       this.isReady = false;
+       this.emit("destroyed");
+     });
+ 
+     return this._destroyer;
+-  })
++  }
+ };
+ 
+ exports.MemoryPanel = MemoryPanel;
+diff --git a/devtools/client/memory/test/browser/browser_memory_allocationStackDisplay_01.js b/devtools/client/memory/test/browser/browser_memory_allocationStackDisplay_01.js
+--- a/devtools/client/memory/test/browser/browser_memory_allocationStackDisplay_01.js
++++ b/devtools/client/memory/test/browser/browser_memory_allocationStackDisplay_01.js
+@@ -8,32 +8,32 @@
+ const { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
+ const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
+ const censusDisplayActions = require("devtools/client/memory/actions/census-display");
+ const { viewState } = require("devtools/client/memory/constants");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const heapWorker = panel.panelWin.gHeapAnalysesClient;
+   const front = panel.panelWin.gFront;
+   const { getState, dispatch } = panel.panelWin.gStore;
+   const doc = panel.panelWin.document;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   dispatch(censusDisplayActions.setCensusDisplay(censusDisplays.invertedAllocationStack));
+   is(getState().censusDisplay.breakdown.by, "allocationStack");
+ 
+-  yield dispatch(toggleRecordingAllocationStacks(front));
++  await dispatch(toggleRecordingAllocationStacks(front));
+   ok(getState().allocations.recording);
+ 
+   // Let some allocations build up.
+-  yield waitForTime(500);
++  await waitForTime(500);
+ 
+-  yield dispatch(takeSnapshotAndCensus(front, heapWorker));
++  await dispatch(takeSnapshotAndCensus(front, heapWorker));
+ 
+   const names = [...doc.querySelectorAll(".frame-link-function-display-name")];
+   ok(names.length, "Should have rendered some allocation stack tree items");
+   ok(names.some(e => !!e.textContent.trim()),
+      "And at least some of them should have functionDisplayNames");
+ });
+diff --git a/devtools/client/memory/test/browser/browser_memory_clear_snapshots.js b/devtools/client/memory/test/browser/browser_memory_clear_snapshots.js
+--- a/devtools/client/memory/test/browser/browser_memory_clear_snapshots.js
++++ b/devtools/client/memory/test/browser/browser_memory_clear_snapshots.js
+@@ -5,34 +5,34 @@
+ 
+ /**
+  * Tests taking and then clearing snapshots.
+  */
+ 
+ const { treeMapState } = require("devtools/client/memory/constants");
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const { gStore, document } = panel.panelWin;
+   const { getState } = gStore;
+ 
+   let snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
+   is(getState().snapshots.length, 0, "Starts with no snapshots in store");
+   is(snapshotEls.length, 0, "No snapshots visible");
+ 
+   info("Take two snapshots");
+   takeSnapshot(panel.panelWin);
+   takeSnapshot(panel.panelWin);
+-  yield waitUntilState(gStore, state =>
++  await waitUntilState(gStore, state =>
+   state.snapshots.length === 2 &&
+   state.snapshots[0].treeMap && state.snapshots[1].treeMap &&
+   state.snapshots[0].treeMap.state === treeMapState.SAVED &&
+   state.snapshots[1].treeMap.state === treeMapState.SAVED);
+ 
+   snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
+   is(snapshotEls.length, 2, "Two snapshots visible");
+ 
+   info("Click on Clear Snapshots");
+-  yield clearSnapshots(panel.panelWin);
++  await clearSnapshots(panel.panelWin);
+   is(getState().snapshots.length, 0, "No snapshots in store");
+   snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
+   is(snapshotEls.length, 0, "No snapshot visible");
+ });
+diff --git a/devtools/client/memory/test/browser/browser_memory_diff_01.js b/devtools/client/memory/test/browser/browser_memory_diff_01.js
+--- a/devtools/client/memory/test/browser/browser_memory_diff_01.js
++++ b/devtools/client/memory/test/browser/browser_memory_diff_01.js
+@@ -7,65 +7,65 @@
+ 
+ const {
+   diffingState,
+   treeMapState
+ } = require("devtools/client/memory/constants");
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const store = panel.panelWin.gStore;
+   const { getState } = store;
+   const doc = panel.panelWin.document;
+ 
+   ok(!getState().diffing, "Not diffing by default.");
+ 
+   // Take two snapshots.
+   const takeSnapshotButton = doc.getElementById("take-snapshot");
+   EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
+-  yield waitForTime(1000);
++  await waitForTime(1000);
+   EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
+ 
+   // Enable diffing mode.
+   const diffButton = doc.getElementById("diff-snapshots");
+   EventUtils.synthesizeMouseAtCenter(diffButton, {}, panel.panelWin);
+-  yield waitUntilState(store,
++  await waitUntilState(store,
+                        state =>
+                          !!state.diffing &&
+                          state.diffing.state === diffingState.SELECTING);
+   ok(true, "Clicking the diffing button put us into the diffing state.");
+   is(getDisplayedSnapshotStatus(doc), "Select the baseline snapshot");
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots.length === 2 &&
+     state.snapshots[0].treeMap && state.snapshots[1].treeMap &&
+     state.snapshots[0].treeMap.state === treeMapState.SAVED &&
+     state.snapshots[1].treeMap.state === treeMapState.SAVED);
+ 
+   const listItems = [...doc.querySelectorAll(".snapshot-list-item")];
+   is(listItems.length, 2, "Should have two snapshot list items");
+ 
+   // Select the first snapshot.
+   EventUtils.synthesizeMouseAtCenter(listItems[0], {}, panel.panelWin);
+-  yield waitUntilState(store,
++  await waitUntilState(store,
+                        state =>
+                          state.diffing.state === diffingState.SELECTING &&
+                          state.diffing.firstSnapshotId);
+   is(getDisplayedSnapshotStatus(doc),
+      "Select the snapshot to compare to the baseline");
+ 
+   // Select the second snapshot.
+   EventUtils.synthesizeMouseAtCenter(listItems[1], {}, panel.panelWin);
+-  yield waitUntilState(store,
++  await waitUntilState(store,
+                        state =>
+                          state.diffing.state === diffingState.TAKING_DIFF);
+   ok(true, "Selecting two snapshots for diffing triggers computing the diff");
+ 
+   // .startsWith because the ellipsis is lost in translation.
+   ok(getDisplayedSnapshotStatus(doc).startsWith("Computing difference"));
+ 
+-  yield waitUntilState(store,
++  await waitUntilState(store,
+                        state => state.diffing.state === diffingState.TOOK_DIFF);
+   ok(true, "And that diff is computed successfully");
+   is(getDisplayedSnapshotStatus(doc), null, "No status text anymore");
+   ok(doc.querySelector(".heap-tree-item"), "And instead we should be showing the tree");
+ });
+diff --git a/devtools/client/memory/test/browser/browser_memory_displays_01.js b/devtools/client/memory/test/browser/browser_memory_displays_01.js
+--- a/devtools/client/memory/test/browser/browser_memory_displays_01.js
++++ b/devtools/client/memory/test/browser/browser_memory_displays_01.js
+@@ -5,37 +5,37 @@
+ /**
+  * Tests that the heap tree renders rows based on the display
+  */
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
+ const { viewState, censusState } = require("devtools/client/memory/constants");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const { gStore, document } = panel.panelWin;
+ 
+   const { dispatch } = panel.panelWin.gStore;
+ 
+   function $$(selector) {
+     return [...document.querySelectorAll(selector)];
+   }
+   dispatch(changeView(viewState.CENSUS));
+ 
+-  yield takeSnapshot(panel.panelWin);
++  await takeSnapshot(panel.panelWin);
+ 
+-  yield waitUntilState(gStore, state =>
++  await waitUntilState(gStore, state =>
+     state.snapshots[0].census &&
+     state.snapshots[0].census.state === censusState.SAVED);
+ 
+   info("Check coarse type heap view");
+ 
+   ["Function", "js::Shape", "Object", "strings"].forEach(findNameCell);
+ 
+-  yield setCensusDisplay(panel.panelWin, censusDisplays.allocationStack);
++  await setCensusDisplay(panel.panelWin, censusDisplays.allocationStack);
+   info("Check allocation stack heap view");
+   [L10N.getStr("tree-item.nostack")].forEach(findNameCell);
+ 
+   function findNameCell(name) {
+     const el = $$(".tree .heap-tree-item-name")
+       .find(e => e.textContent === name);
+     ok(el, `Found heap tree item cell for ${name}.`);
+   }
+diff --git a/devtools/client/memory/test/browser/browser_memory_dominator_trees_01.js b/devtools/client/memory/test/browser/browser_memory_dominator_trees_01.js
+--- a/devtools/client/memory/test/browser/browser_memory_dominator_trees_01.js
++++ b/devtools/client/memory/test/browser/browser_memory_dominator_trees_01.js
+@@ -12,34 +12,34 @@ const {
+ } = require("devtools/client/memory/constants");
+ const {
+   expandDominatorTreeNode,
+ } = require("devtools/client/memory/actions/snapshot");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_big_tree.html";
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   // Taking snapshots and computing dominator trees is slow :-/
+   requestLongerTimeout(4);
+ 
+   const store = panel.panelWin.gStore;
+   const { getState, dispatch } = store;
+   const doc = panel.panelWin.document;
+ 
+   dispatch(changeView(viewState.DOMINATOR_TREE));
+ 
+   // Take a snapshot.
+ 
+   const takeSnapshotButton = doc.getElementById("take-snapshot");
+   EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
+ 
+   // Wait for the dominator tree to be computed and fetched.
+ 
+-  yield waitUntilDominatorTreeState(store, [dominatorTreeState.LOADED]);
++  await waitUntilDominatorTreeState(store, [dominatorTreeState.LOADED]);
+   ok(true, "Computed and fetched the dominator tree.");
+ 
+   // Expand all the dominator tree nodes that are eagerly fetched, except for
+   // the leaves which will trigger fetching their lazily loaded subtrees.
+ 
+   const id = getState().snapshots[0].id;
+   const root = getState().snapshots[0].dominatorTree.root;
+   (function expandAllEagerlyFetched(node = root) {
+@@ -78,36 +78,36 @@ this.test = makeMemoryTest(TEST_URL, fun
+   ok(!getState().snapshots[0].dominatorTree.expanded.has(deepest.nodeId),
+      "The deepest node should not be expanded");
+ 
+   // Select the deepest node.
+ 
+   EventUtils.synthesizeMouseAtCenter(doc.querySelector(`.node-${deepest.nodeId}`),
+                                      {},
+                                      panel.panelWin);
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.focused.nodeId === deepest.nodeId);
+   ok(doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
+      "The deepest node should be focused now");
+ 
+   // Expand the deepest node, which triggers an incremental fetch of its lazily
+   // loaded subtree.
+ 
+   EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.expanded.has(deepest.nodeId));
+   is(getState().snapshots[0].dominatorTree.state,
+      dominatorTreeState.INCREMENTAL_FETCHING,
+      "Expanding the deepest node should start an incremental fetch of its subtree");
+   ok(doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
+      "The deepest node should still be focused after expansion");
+ 
+   // Wait for the incremental fetch to complete.
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(true, "And the incremental fetch completes.");
+   ok(doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
+      "The deepest node should still be focused after we have loaded its children");
+ 
+   // Find the most up-to-date version of the node whose children we just
+   // incrementally fetched.
+ 
+@@ -137,13 +137,13 @@ this.test = makeMemoryTest(TEST_URL, fun
+   const firstChild = newDeepest.children[0];
+   ok(firstChild, "deepest should have a first child");
+   ok(doc.querySelector(`.node-${firstChild.nodeId}`),
+      "and the first child should exist in the dom");
+ 
+   // Select the newly loaded first child by pressing the right arrow once more.
+ 
+   EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.focused === firstChild);
+   ok(doc.querySelector(`.node-${firstChild.nodeId}`).classList.contains("focused"),
+      "The first child should now be focused");
+ });
+diff --git a/devtools/client/memory/test/browser/browser_memory_dominator_trees_02.js b/devtools/client/memory/test/browser/browser_memory_dominator_trees_02.js
+--- a/devtools/client/memory/test/browser/browser_memory_dominator_trees_02.js
++++ b/devtools/client/memory/test/browser/browser_memory_dominator_trees_02.js
+@@ -13,52 +13,52 @@ const { changeView } = require("devtools
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
+ 
+ function clickOnNodeArrow(node, panel) {
+   EventUtils.synthesizeMouseAtCenter(node.querySelector(".arrow"),
+                                      {}, panel.panelWin);
+ }
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ panel }) {
+   // Taking snapshots and computing dominator trees is slow :-/
+   requestLongerTimeout(4);
+ 
+   const store = panel.panelWin.gStore;
+   const { getState, dispatch } = store;
+   const doc = panel.panelWin.document;
+ 
+   dispatch(changeView(viewState.DOMINATOR_TREE));
+ 
+   // Take a snapshot.
+   const takeSnapshotButton = doc.getElementById("take-snapshot");
+   EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
+ 
+   // Wait for the dominator tree to be computed and fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0] &&
+     state.snapshots[0].dominatorTree &&
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(true, "Computed and fetched the dominator tree.");
+ 
+   const root = getState().snapshots[0].dominatorTree.root;
+   ok(getState().snapshots[0].dominatorTree.expanded.has(root.nodeId),
+      "Root node is expanded by default");
+ 
+   // Click on root arrow to collapse the root element
+   const rootNode = doc.querySelector(`.node-${root.nodeId}`);
+   clickOnNodeArrow(rootNode, panel);
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0] &&
+     state.snapshots[0].dominatorTree &&
+     !state.snapshots[0].dominatorTree.expanded.has(root.nodeId));
+   ok(true, "Root node collapsed");
+ 
+   // Click on root arrow to expand it again
+   clickOnNodeArrow(rootNode, panel);
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0] &&
+     state.snapshots[0].dominatorTree &&
+     state.snapshots[0].dominatorTree.expanded.has(root.nodeId));
+   ok(true, "Root node is expanded again");
+ });
+diff --git a/devtools/client/memory/test/browser/browser_memory_filter_01.js b/devtools/client/memory/test/browser/browser_memory_filter_01.js
+--- a/devtools/client/memory/test/browser/browser_memory_filter_01.js
++++ b/devtools/client/memory/test/browser/browser_memory_filter_01.js
+@@ -9,43 +9,43 @@ const {
+   dominatorTreeState,
+   viewState,
+   censusState,
+ } = require("devtools/client/memory/constants");
+ const { changeViewAndRefresh, changeView } = require("devtools/client/memory/actions/view");
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const heapWorker = panel.panelWin.gHeapAnalysesClient;
+   const store = panel.panelWin.gStore;
+   const { dispatch } = store;
+   const doc = panel.panelWin.document;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   const takeSnapshotButton = doc.getElementById("take-snapshot");
+   EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots.length === 1 &&
+     state.snapshots[0].census &&
+     state.snapshots[0].census.state === censusState.SAVING);
+ 
+   let filterInput = doc.getElementById("filter");
+   EventUtils.synthesizeMouseAtCenter(filterInput, {}, panel.panelWin);
+   EventUtils.sendString("js::Shape", panel.panelWin);
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots.length === 1 &&
+     state.snapshots[0].census &&
+     state.snapshots[0].census.state === censusState.SAVING);
+   ok(true, "adding a filter string should trigger census recompute");
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots.length === 1 &&
+     state.snapshots[0].census &&
+     state.snapshots[0].census.state === censusState.SAVED);
+ 
+   let nameElem = doc.querySelector(".heap-tree-item-field.heap-tree-item-name");
+   ok(nameElem, "Should get a tree item row with a name");
+   is(nameElem.textContent.trim(), "js::Shape",
+     "the tree item should be the one we filtered for");
+@@ -53,23 +53,23 @@ this.test = makeMemoryTest(TEST_URL, fun
+     "and filter input contains the user value");
+ 
+   // Now switch the dominator view, then switch back to census view
+   // and check that the filter word is still correctly applied
+   dispatch(changeViewAndRefresh(viewState.DOMINATOR_TREE, heapWorker));
+   ok(true, "change view to dominator tree");
+ 
+   // Wait for the dominator tree to be computed and fetched.
+-  yield waitUntilDominatorTreeState(store, [dominatorTreeState.LOADED]);
++  await waitUntilDominatorTreeState(store, [dominatorTreeState.LOADED]);
+   ok(true, "computed and fetched the dominator tree.");
+ 
+   dispatch(changeViewAndRefresh(viewState.CENSUS, heapWorker));
+   ok(true, "change view back to census");
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots.length === 1 &&
+     state.snapshots[0].census &&
+     state.snapshots[0].census.state === censusState.SAVED);
+ 
+   nameElem = doc.querySelector(".heap-tree-item-field.heap-tree-item-name");
+   filterInput = doc.getElementById("filter");
+ 
+   ok(nameElem, "Should still get a tree item row with a name");
+diff --git a/devtools/client/memory/test/browser/browser_memory_individuals_01.js b/devtools/client/memory/test/browser/browser_memory_individuals_01.js
+--- a/devtools/client/memory/test/browser/browser_memory_individuals_01.js
++++ b/devtools/client/memory/test/browser/browser_memory_individuals_01.js
+@@ -10,56 +10,56 @@ const {
+   individualsState,
+   viewState,
+   censusState,
+ } = require("devtools/client/memory/constants");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const store = panel.panelWin.gStore;
+   const { dispatch } = store;
+   const doc = panel.panelWin.document;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   // Take a snapshot and wait for the census to finish.
+ 
+   const takeSnapshotButton = doc.getElementById("take-snapshot");
+   EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);
+ 
+-  yield waitUntilState(store, state => {
++  await waitUntilState(store, state => {
+     return state.snapshots.length === 1 &&
+            state.snapshots[0].census &&
+            state.snapshots[0].census.state === censusState.SAVED;
+   });
+ 
+   // Click on the first individuals button found, and wait for the individuals
+   // to be fetched.
+ 
+   const individualsButton = doc.querySelector(".individuals-button");
+   EventUtils.synthesizeMouseAtCenter(individualsButton, {}, panel.panelWin);
+ 
+-  yield waitUntilState(store, state => {
++  await waitUntilState(store, state => {
+     return state.view.state === viewState.INDIVIDUALS &&
+            state.individuals &&
+            state.individuals.state === individualsState.FETCHED;
+   });
+ 
+   ok(doc.getElementById("shortest-paths"),
+      "Should be showing the shortest paths component");
+   ok(doc.querySelector(".heap-tree-item"),
+      "Should be showing the individuals");
+ 
+   // Go back to the previous view.
+ 
+   const popViewButton = doc.getElementById("pop-view-button");
+   ok(popViewButton, "Should be showing the #pop-view-button");
+   EventUtils.synthesizeMouseAtCenter(popViewButton, {}, panel.panelWin);
+ 
+-  yield waitUntilState(store, state => {
++  await waitUntilState(store, state => {
+     return state.view.state === viewState.CENSUS;
+   });
+ 
+   ok(!doc.getElementById("shortest-paths"),
+      "Should not be showing the shortest paths component anymore");
+ });
+diff --git a/devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js b/devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js
+--- a/devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js
++++ b/devtools/client/memory/test/browser/browser_memory_keyboard-snapshot-list.js
+@@ -11,17 +11,17 @@ const {
+ } = require("devtools/client/memory/constants");
+ const {
+   takeSnapshotAndCensus
+ } = require("devtools/client/memory/actions/snapshot");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ panel }) {
+   // Creating snapshots already takes ~25 seconds on linux 32 debug machines
+   // which makes the test very likely to go over the allowed timeout
+   requestLongerTimeout(2);
+ 
+   const heapWorker = panel.panelWin.gHeapAnalysesClient;
+   const front = panel.panelWin.gFront;
+   const store = panel.panelWin.gStore;
+   const { dispatch } = store;
+@@ -29,70 +29,70 @@ this.test = makeMemoryTest(TEST_URL, fun
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   info("Take 3 snapshots");
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots.length == 3 &&
+     state.snapshots.every(s => s.census && s.census.state === censusState.SAVED));
+   ok(true, "All snapshots censuses are in SAVED state");
+ 
+-  yield waitUntilSnapshotSelected(store, 2);
++  await waitUntilSnapshotSelected(store, 2);
+   ok(true, "Third snapshot selected after creating all snapshots.");
+ 
+   info("Press ACCEL+UP key, expect second snapshot selected.");
+   EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin);
+-  yield waitUntilSnapshotSelected(store, 1);
++  await waitUntilSnapshotSelected(store, 1);
+   ok(true, "Second snapshot selected after alt+UP.");
+ 
+   info("Press ACCEL+UP key, expect first snapshot selected.");
+   EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin);
+-  yield waitUntilSnapshotSelected(store, 0);
++  await waitUntilSnapshotSelected(store, 0);
+   ok(true, "First snapshot is selected after ACCEL+UP");
+ 
+   info("Check ACCEL+UP is a noop when the first snapshot is selected.");
+   EventUtils.synthesizeKey("VK_UP", { accelKey: true }, panel.panelWin);
+   // We assume the snapshot selection should be synchronous here.
+   is(getSelectedSnapshotIndex(store), 0, "First snapshot is still selected");
+ 
+   info("Press ACCEL+DOWN key, expect second snapshot selected.");
+   EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin);
+-  yield waitUntilSnapshotSelected(store, 1);
++  await waitUntilSnapshotSelected(store, 1);
+   ok(true, "Second snapshot is selected after ACCEL+DOWN");
+ 
+   info("Click on first node.");
+   let firstNode = doc.querySelector(".tree .heap-tree-item-name");
+   EventUtils.synthesizeMouseAtCenter(firstNode, {}, panel.panelWin);
+-  yield waitUntilState(store, state => state.snapshots[1].census.focused ===
++  await waitUntilState(store, state => state.snapshots[1].census.focused ===
+       state.snapshots[1].census.report.children[0]
+   );
+   ok(true, "First root is selected after click.");
+ 
+   info("Press DOWN key, expect second root focused.");
+   EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin);
+-  yield waitUntilState(store, state => state.snapshots[1].census.focused ===
++  await waitUntilState(store, state => state.snapshots[1].census.focused ===
+       state.snapshots[1].census.report.children[1]
+   );
+   ok(true, "Second root is selected after pressing DOWN.");
+   is(getSelectedSnapshotIndex(store), 1, "Second snapshot is still selected");
+ 
+   info("Press UP key, expect second root focused.");
+   EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin);
+-  yield waitUntilState(store, state => state.snapshots[1].census.focused ===
++  await waitUntilState(store, state => state.snapshots[1].census.focused ===
+       state.snapshots[1].census.report.children[0]
+   );
+   ok(true, "First root is selected after pressing UP.");
+   is(getSelectedSnapshotIndex(store), 1, "Second snapshot is still selected");
+ 
+   info("Press ACCEL+DOWN key, expect third snapshot selected.");
+   EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin);
+-  yield waitUntilSnapshotSelected(store, 2);
++  await waitUntilSnapshotSelected(store, 2);
+   ok(true, "Thirdˆ snapshot is selected after ACCEL+DOWN");
+ 
+   info("Check ACCEL+DOWN is a noop when the last snapshot is selected.");
+   EventUtils.synthesizeKey("VK_DOWN", { accelKey: true }, panel.panelWin);
+   // We assume the snapshot selection should be synchronous here.
+   is(getSelectedSnapshotIndex(store), 2, "Third snapshot is still selected");
+ });
+diff --git a/devtools/client/memory/test/browser/browser_memory_keyboard.js b/devtools/client/memory/test/browser/browser_memory_keyboard.js
+--- a/devtools/client/memory/test/browser/browser_memory_keyboard.js
++++ b/devtools/client/memory/test/browser/browser_memory_keyboard.js
+@@ -29,78 +29,78 @@ function waitUntilFocused(store, node) {
+ 
+ function waitUntilExpanded(store, node) {
+   return waitUntilState(store, state =>
+     state.snapshots[0] &&
+     state.snapshots[0].census &&
+     state.snapshots[0].census.expanded.has(node.id));
+ }
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const heapWorker = panel.panelWin.gHeapAnalysesClient;
+   const front = panel.panelWin.gFront;
+   const store = panel.panelWin.gStore;
+   const { getState, dispatch } = store;
+   const doc = panel.panelWin.document;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   is(getState().censusDisplay.breakdown.by, "coarseType");
+ 
+-  yield dispatch(takeSnapshotAndCensus(front, heapWorker));
++  await dispatch(takeSnapshotAndCensus(front, heapWorker));
+   let census = getState().snapshots[0].census;
+   let root1 = census.report.children[0];
+   let root2 = census.report.children[0];
+   let root3 = census.report.children[0];
+   let root4 = census.report.children[0];
+   let child1 = root1.children[0];
+ 
+   info("Click on first node.");
+   let firstNode = doc.querySelector(".tree .heap-tree-item-name");
+   EventUtils.synthesizeMouseAtCenter(firstNode, {}, panel.panelWin);
+-  yield waitUntilFocused(store, root1);
++  await waitUntilFocused(store, root1);
+   ok(true, "First root is selected after click.");
+ 
+   info("Press DOWN key, expect second root focused.");
+   EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin);
+-  yield waitUntilFocused(store, root2);
++  await waitUntilFocused(store, root2);
+   ok(true, "Second root is selected after pressing DOWN arrow.");
+ 
+   info("Press DOWN key, expect third root focused.");
+   EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin);
+-  yield waitUntilFocused(store, root3);
++  await waitUntilFocused(store, root3);
+   ok(true, "Third root is selected after pressing DOWN arrow.");
+ 
+   info("Press DOWN key, expect fourth root focused.");
+   EventUtils.synthesizeKey("VK_DOWN", {}, panel.panelWin);
+-  yield waitUntilFocused(store, root4);
++  await waitUntilFocused(store, root4);
+   ok(true, "Fourth root is selected after pressing DOWN arrow.");
+ 
+   info("Press UP key, expect third root focused.");
+   EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin);
+-  yield waitUntilFocused(store, root3);
++  await waitUntilFocused(store, root3);
+   ok(true, "Third root is selected after pressing UP arrow.");
+ 
+   info("Press UP key, expect second root focused.");
+   EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin);
+-  yield waitUntilFocused(store, root2);
++  await waitUntilFocused(store, root2);
+   ok(true, "Second root is selected after pressing UP arrow.");
+ 
+   info("Press UP key, expect first root focused.");
+   EventUtils.synthesizeKey("VK_UP", {}, panel.panelWin);
+-  yield waitUntilFocused(store, root1);
++  await waitUntilFocused(store, root1);
+   ok(true, "First root is selected after pressing UP arrow.");
+ 
+   info("Press RIGHT key");
+   EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
+-  yield waitUntilExpanded(store, root1);
++  await waitUntilExpanded(store, root1);
+   ok(true, "Root node is expanded.");
+ 
+   info("Press RIGHT key, expect first child focused.");
+   EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
+-  yield waitUntilFocused(store, child1);
++  await waitUntilFocused(store, child1);
+   ok(true, "First child is selected after pressing RIGHT arrow.");
+ 
+   info("Press LEFT key, expect first root focused.");
+   EventUtils.synthesizeKey("VK_LEFT", {}, panel.panelWin);
+-  yield waitUntilFocused(store, root1);
++  await waitUntilFocused(store, root1);
+   ok(true, "First root is selected after pressing LEFT arrow.");
+ });
+diff --git a/devtools/client/memory/test/browser/browser_memory_no_allocation_stacks.js b/devtools/client/memory/test/browser/browser_memory_no_allocation_stacks.js
+--- a/devtools/client/memory/test/browser/browser_memory_no_allocation_stacks.js
++++ b/devtools/client/memory/test/browser/browser_memory_no_allocation_stacks.js
+@@ -7,29 +7,29 @@
+ 
+ const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
+ const censusDisplayActions = require("devtools/client/memory/actions/census-display");
+ const { viewState } = require("devtools/client/memory/constants");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const heapWorker = panel.panelWin.gHeapAnalysesClient;
+   const front = panel.panelWin.gFront;
+   const { getState, dispatch } = panel.panelWin.gStore;
+   const doc = panel.panelWin.document;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   ok(!getState().allocations.recording,
+      "Should not be recording allocagtions");
+ 
+-  yield dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield dispatch(censusDisplayActions.setCensusDisplayAndRefresh(
++  await dispatch(takeSnapshotAndCensus(front, heapWorker));
++  await dispatch(censusDisplayActions.setCensusDisplayAndRefresh(
+     heapWorker,
+     censusDisplays.allocationStack));
+ 
+   is(getState().censusDisplay.breakdown.by, "allocationStack",
+      "Should be using allocation stack breakdown");
+ 
+   ok(!getState().allocations.recording,
+      "Should still not be recording allocagtions");
+diff --git a/devtools/client/memory/test/browser/browser_memory_no_auto_expand.js b/devtools/client/memory/test/browser/browser_memory_no_auto_expand.js
+--- a/devtools/client/memory/test/browser/browser_memory_no_auto_expand.js
++++ b/devtools/client/memory/test/browser/browser_memory_no_auto_expand.js
+@@ -7,25 +7,25 @@
+ "use strict";
+ 
+ const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
+ const { viewState } = require("devtools/client/memory/constants");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const heapWorker = panel.panelWin.gHeapAnalysesClient;
+   const front = panel.panelWin.gFront;
+   const { getState, dispatch } = panel.panelWin.gStore;
+   const doc = panel.panelWin.document;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+-  yield dispatch(takeSnapshotAndCensus(front, heapWorker));
++  await dispatch(takeSnapshotAndCensus(front, heapWorker));
+ 
+   is(getState().allocations.recording, false);
+   const recordingCheckbox = doc.getElementById("record-allocation-stacks-checkbox");
+   EventUtils.synthesizeMouseAtCenter(recordingCheckbox, {}, panel.panelWin);
+   is(getState().allocations.recording, true);
+ 
+   const nameElems = [
+     ...doc.querySelectorAll(".heap-tree-item-field.heap-tree-item-name")
+diff --git a/devtools/client/memory/test/browser/browser_memory_percents_01.js b/devtools/client/memory/test/browser/browser_memory_percents_01.js
+--- a/devtools/client/memory/test/browser/browser_memory_percents_01.js
++++ b/devtools/client/memory/test/browser/browser_memory_percents_01.js
+@@ -16,25 +16,25 @@ function checkCells(cells) {
+   // Ignore the first header cell.
+   for (let cell of cells.slice(1)) {
+     const percent = cell.querySelector(".heap-tree-percent");
+     ok(percent, "should have a percent cell");
+     ok(percent.textContent.match(/^\d?\d%$/), "should be of the form nn% or n%");
+   }
+ }
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const heapWorker = panel.panelWin.gHeapAnalysesClient;
+   const front = panel.panelWin.gFront;
+   const { getState, dispatch } = panel.panelWin.gStore;
+   const doc = panel.panelWin.document;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+-  yield dispatch(takeSnapshotAndCensus(front, heapWorker));
++  await dispatch(takeSnapshotAndCensus(front, heapWorker));
+   is(getState().censusDisplay.breakdown.by, "coarseType",
+      "Should be using coarse type breakdown");
+ 
+   const bytesCells = [...doc.querySelectorAll(".heap-tree-item-bytes")];
+   checkCells(bytesCells);
+ 
+   const totalBytesCells = [...doc.querySelectorAll(".heap-tree-item-total-bytes")];
+   checkCells(totalBytesCells);
+diff --git a/devtools/client/memory/test/browser/browser_memory_refresh_does_not_leak.js b/devtools/client/memory/test/browser/browser_memory_refresh_does_not_leak.js
+--- a/devtools/client/memory/test/browser/browser_memory_refresh_does_not_leak.js
++++ b/devtools/client/memory/test/browser/browser_memory_refresh_does_not_leak.js
+@@ -10,19 +10,19 @@
+ // WHILE DEVTOOLS ARE OPEN! THIS IS NOT SPECIFIC TO THE MEMORY TOOL ONLY!
+ 
+ "use strict";
+ 
+ const { getLabelAndShallowSize } = require("devtools/shared/heapsnapshot/DominatorTreeNode");
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_empty.html";
+ 
+-function* getWindowsInSnapshot(front) {
++async function getWindowsInSnapshot(front) {
+   dumpn("Taking snapshot.");
+-  const path = yield front.saveHeapSnapshot();
++  const path = await front.saveHeapSnapshot();
+   dumpn("Took snapshot with path = " + path);
+   const snapshot = ChromeUtils.readHeapSnapshot(path);
+   dumpn("Read snapshot into memory, taking census.");
+   const report = snapshot.takeCensus({
+     breakdown: {
+       by: "objectClass",
+       then: { by: "bucket" },
+       other: { by: "count", count: true, bytes: false },
+@@ -45,38 +45,38 @@ const DESCRIPTION = {
+     then: { by: "count", count: true, bytes: false },
+   },
+   other: {
+     by: "internalType",
+     then: { by: "count", count: true, bytes: false },
+   }
+ };
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const front = panel.panelWin.gFront;
+ 
+-  const startWindows = yield getWindowsInSnapshot(front);
++  const startWindows = await getWindowsInSnapshot(front);
+   dumpn("Initial windows found = " + startWindows.map(w => "0x" +
+                                      w.toString(16)).join(", "));
+   is(startWindows.length, 1);
+ 
+-  yield refreshTab();
++  await refreshTab();
+ 
+-  const endWindows = yield getWindowsInSnapshot(front);
++  const endWindows = await getWindowsInSnapshot(front);
+   is(endWindows.length, 1);
+ 
+   if (endWindows.length === 1) {
+     return;
+   }
+ 
+   dumpn("Test failed, diagnosing leaking windows.");
+   dumpn("(This may fail if a moving GC has relocated the initial Window objects.)");
+ 
+   dumpn("Taking full runtime snapshot.");
+-  const path = yield front.saveHeapSnapshot({ boundaries: { runtime: true } });
++  const path = await front.saveHeapSnapshot({ boundaries: { runtime: true } });
+   dumpn("Full runtime's snapshot path = " + path);
+ 
+   dumpn("Reading full runtime heap snapshot.");
+   const snapshot = ChromeUtils.readHeapSnapshot(path);
+   dumpn("Done reading full runtime heap snapshot.");
+ 
+   const dominatorTree = snapshot.computeDominatorTree();
+   const paths = snapshot.computeShortestPaths(dominatorTree.root, startWindows, 50);
+diff --git a/devtools/client/memory/test/browser/browser_memory_simple_01.js b/devtools/client/memory/test/browser/browser_memory_simple_01.js
+--- a/devtools/client/memory/test/browser/browser_memory_simple_01.js
++++ b/devtools/client/memory/test/browser/browser_memory_simple_01.js
+@@ -6,40 +6,40 @@
+ /**
+  * Tests taking snapshots and default states.
+  */
+ 
+ const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
+ const { viewState, censusState } = require("devtools/client/memory/constants");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const { gStore, document } = panel.panelWin;
+   const { getState, dispatch } = gStore;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   let snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
+   is(getState().snapshots.length, 0, "Starts with no snapshots in store");
+   is(snapshotEls.length, 0, "No snapshots rendered");
+ 
+-  yield takeSnapshot(panel.panelWin);
++  await takeSnapshot(panel.panelWin);
+   snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
+   is(getState().snapshots.length, 1, "One snapshot was created in store");
+   is(snapshotEls.length, 1, "One snapshot was rendered");
+   ok(snapshotEls[0].classList.contains("selected"),
+      "Only snapshot has `selected` class");
+ 
+-  yield takeSnapshot(panel.panelWin);
++  await takeSnapshot(panel.panelWin);
+   snapshotEls = document.querySelectorAll("#memory-tool-container .list li");
+   is(getState().snapshots.length, 2, "Two snapshots created in store");
+   is(snapshotEls.length, 2, "Two snapshots rendered");
+   ok(!snapshotEls[0].classList.contains("selected"),
+      "First snapshot no longer has `selected` class");
+   ok(snapshotEls[1].classList.contains("selected"),
+      "Second snapshot has `selected` class");
+ 
+-  yield waitUntilCensusState(gStore, s => s.census,
++  await waitUntilCensusState(gStore, s => s.census,
+                              [censusState.SAVED, censusState.SAVED]);
+ 
+   ok(document.querySelector(".heap-tree-item-name"),
+     "Should have rendered some tree items");
+ });
+diff --git a/devtools/client/memory/test/browser/browser_memory_transferHeapSnapshot_e10s_01.js b/devtools/client/memory/test/browser/browser_memory_transferHeapSnapshot_e10s_01.js
+--- a/devtools/client/memory/test/browser/browser_memory_transferHeapSnapshot_e10s_01.js
++++ b/devtools/client/memory/test/browser/browser_memory_transferHeapSnapshot_e10s_01.js
+@@ -6,25 +6,25 @@
+ // Test that we can save a heap snapshot and transfer it over the RDP in e10s
+ // where the child process is sandboxed and so we have to use
+ // HeapSnapshotFileActor to get the heap snapshot file.
+ 
+ "use strict";
+ 
+ const TEST_URL = "data:text/html,<html><body></body></html>";
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const memoryFront = panel.panelWin.gFront;
+   ok(memoryFront, "Should get the MemoryFront");
+ 
+-  const snapshotFilePath = yield memoryFront.saveHeapSnapshot({
++  const snapshotFilePath = await memoryFront.saveHeapSnapshot({
+     // Force a copy so that we go through the HeapSnapshotFileActor's
+     // transferHeapSnapshot request and exercise this code path on e10s.
+     forceCopy: true
+   });
+ 
+-  ok(!!(yield OS.File.stat(snapshotFilePath)),
++  ok(!!(await OS.File.stat(snapshotFilePath)),
+      "Should have the heap snapshot file");
+ 
+   const snapshot = ChromeUtils.readHeapSnapshot(snapshotFilePath);
+   ok(snapshot instanceof HeapSnapshot,
+      "And we should be able to read a HeapSnapshot instance from the file");
+ });
+diff --git a/devtools/client/memory/test/browser/head.js b/devtools/client/memory/test/browser/head.js
+--- a/devtools/client/memory/test/browser/head.js
++++ b/devtools/client/memory/test/browser/head.js
+@@ -16,69 +16,69 @@ Services.scriptloader.loadSubScript(
+ var { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants");
+ var { L10N } = require("devtools/client/memory/utils");
+ 
+ Services.prefs.setBoolPref("devtools.memory.enabled", true);
+ 
+ /**
+  * Open the memory panel for the given tab.
+  */
+-this.openMemoryPanel = Task.async(function* (tab) {
++this.openMemoryPanel = async function(tab) {
+   info("Opening memory panel.");
+   const target = TargetFactory.forTab(tab);
+-  const toolbox = yield gDevTools.showToolbox(target, "memory");
++  const toolbox = await gDevTools.showToolbox(target, "memory");
+   info("Memory panel shown successfully.");
+   let panel = toolbox.getCurrentPanel();
+   return { tab, panel };
+-});
++};
+ 
+ /**
+  * Close the memory panel for the given tab.
+  */
+-this.closeMemoryPanel = Task.async(function* (tab) {
++this.closeMemoryPanel = async function(tab) {
+   info("Closing memory panel.");
+   const target = TargetFactory.forTab(tab);
+   const toolbox = gDevTools.getToolbox(target);
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+   info("Closed memory panel successfully.");
+-});
++};
+ 
+ /**
+  * Return a test function that adds a tab with the given url, opens the memory
+  * panel, runs the given generator, closes the memory panel, removes the tab,
+  * and finishes.
+  *
+  * Example usage:
+  *
+  *     this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
+  *         // Your tests go here...
+  *     });
+  */
+ function makeMemoryTest(url, generator) {
+-  return Task.async(function* () {
++  return async function() {
+     waitForExplicitFinish();
+ 
+     // It can take a long time to save a snapshot to disk, read the snapshots
+     // back from disk, and finally perform analyses on them.
+     requestLongerTimeout(2);
+ 
+-    const tab = yield addTab(url);
+-    const results = yield openMemoryPanel(tab);
++    const tab = await addTab(url);
++    const results = await openMemoryPanel(tab);
+ 
+     try {
+-      yield* generator(results);
++      await generator(results);
+     } catch (err) {
+       ok(false, "Got an error: " + DevToolsUtils.safeErrorString(err));
+     }
+ 
+-    yield closeMemoryPanel(tab);
+-    yield removeTab(tab);
++    await closeMemoryPanel(tab);
++    await removeTab(tab);
+ 
+     finish();
+-  });
++  };
+ }
+ 
+ function dumpn(msg) {
+   dump(`MEMORY-TEST: ${msg}\n`);
+ }
+ 
+ /**
+  * Returns a promise that will resolve when the provided store matches
+diff --git a/devtools/client/memory/test/chrome/head.js b/devtools/client/memory/test/chrome/head.js
+--- a/devtools/client/memory/test/chrome/head.js
++++ b/devtools/client/memory/test/chrome/head.js
+@@ -5,17 +5,16 @@
+ 
+ var { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
+ var { require } = BrowserLoader({
+   baseURI: "resource://devtools/client/memory/",
+   window
+ });
+ var { Assert } = require("resource://testing-common/Assert.jsm");
+ var Services = require("Services");
+-var { Task } = require("devtools/shared/task");
+ 
+ var EXPECTED_DTU_ASSERT_FAILURE_COUNT = 0;
+ 
+ SimpleTest.registerCleanupFunction(function() {
+   if (DevToolsUtils.assertionFailureCount !== EXPECTED_DTU_ASSERT_FAILURE_COUNT) {
+     ok(false, "Should have had the expected number of DevToolsUtils.assert() failures." +
+       "Expected " + EXPECTED_DTU_ASSERT_FAILURE_COUNT +
+       ", got " + DevToolsUtils.assertionFailureCount);
+diff --git a/devtools/client/memory/test/chrome/test_CensusTreeItem_01.html b/devtools/client/memory/test/chrome/test_CensusTreeItem_01.html
+--- a/devtools/client/memory/test/chrome/test_CensusTreeItem_01.html
++++ b/devtools/client/memory/test/chrome/test_CensusTreeItem_01.html
+@@ -11,55 +11,55 @@ Test that children pointers show up at t
+ </head>
+ <body>
+     <!-- Give the container height so that the whole tree is rendered. -->
+     <div id="container" style="height: 900px;"></div>
+ 
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              const container = document.getElementById("container");
+ 
+-             yield renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, {
++             await renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, {
+                inverted: true,
+                depth: 0,
+              })), container);
+ 
+              ok(!container.querySelector(".children-pointer"),
+                 "Don't show children pointer for roots when we are inverted");
+ 
+-             yield renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, {
++             await renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, {
+                inverted: true,
+                depth: 1,
+              })), container);
+ 
+              ok(container.querySelector(".children-pointer"),
+                 "Do show children pointer for non-roots when we are inverted");
+ 
+-             yield renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, {
++             await renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, {
+                inverted: false,
+                item: immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS.item, { children: undefined }),
+              })), container);
+ 
+              ok(!container.querySelector(".children-pointer"),
+                 "Don't show children pointer when non-inverted and no children");
+ 
+-             yield renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, {
++             await renderComponent(CensusTreeItem(immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS, {
+                inverted: false,
+                depth: 0,
+                item: immutableUpdate(TEST_CENSUS_TREE_ITEM_PROPS.item, { children: [{}] }),
+              })), container);
+ 
+              ok(container.querySelector(".children-pointer"),
+                 "Do show children pointer when non-inverted and have children");
+ 
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_DominatorTreeItem_01.html b/devtools/client/memory/test/chrome/test_DominatorTreeItem_01.html
+--- a/devtools/client/memory/test/chrome/test_DominatorTreeItem_01.html
++++ b/devtools/client/memory/test/chrome/test_DominatorTreeItem_01.html
+@@ -11,21 +11,21 @@ Test that we don't display `JS::ubi::Roo
+ </head>
+ <body>
+     <!-- Give the container height so that the whole tree is rendered. -->
+     <div id="container" style="height: 900px;"></div>
+ 
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              const container = document.getElementById("container");
+ 
+-             yield renderComponent(DominatorTreeItem({
++             await renderComponent(DominatorTreeItem({
+                item: makeTestDominatorTreeNode({ label: ["other", "JS::ubi::RootList"] }),
+                depth: 0,
+                arrow: dom.div(),
+                focused: true,
+                getPercentSize: _ => 50,
+                onViewSourceInDebugger: _ => { },
+              }), container);
+ 
+@@ -33,13 +33,13 @@ Test that we don't display `JS::ubi::Roo
+                 "Should not display `JS::ubi::RootList`");
+              ok(container.textContent.includes("GC Roots"),
+                 "Should display `GC Roots` instead");
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_DominatorTree_01.html b/devtools/client/memory/test/chrome/test_DominatorTree_01.html
+--- a/devtools/client/memory/test/chrome/test_DominatorTree_01.html
++++ b/devtools/client/memory/test/chrome/test_DominatorTree_01.html
+@@ -11,40 +11,40 @@ Test that we show a place holder for a s
+ </head>
+ <body>
+     <!-- Give the container height so that the whole tree is rendered. -->
+     <div id="container" style="height: 900px;"></div>
+ 
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              const container = document.getElementById("container");
+ 
+              const root = makeTestDominatorTreeNode({ moreChildrenAvailable: true});
+              ok(!root.children);
+ 
+              const expanded = new Set();
+              expanded.add(root.nodeId);
+ 
+-             yield renderComponent(DominatorTreeComponent(immutableUpdate(TEST_DOMINATOR_TREE_PROPS, {
++             await renderComponent(DominatorTreeComponent(immutableUpdate(TEST_DOMINATOR_TREE_PROPS, {
+                dominatorTree: immutableUpdate(TEST_DOMINATOR_TREE_PROPS.dominatorTree, {
+                  expanded,
+                  root,
+                  state: dominatorTreeState.INCREMENTAL_FETCHING,
+                  activeFetchRequestCount: 1,
+                }),
+              })), container);
+ 
+              ok(container.querySelector(".subtree-fetching"),
+                 "Expanded nodes with more children available, but no children " +
+                 "loaded, should get a placeholder");
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_DominatorTree_02.html b/devtools/client/memory/test/chrome/test_DominatorTree_02.html
+--- a/devtools/client/memory/test/chrome/test_DominatorTree_02.html
++++ b/devtools/client/memory/test/chrome/test_DominatorTree_02.html
+@@ -11,40 +11,40 @@ Test that we show a link to load more ch
+ </head>
+ <body>
+     <!-- Give the container height so that the whole tree is rendered. -->
+     <div id="container" style="height: 900px;"></div>
+ 
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              const container = document.getElementById("container");
+ 
+              const root = makeTestDominatorTreeNode({ moreChildrenAvailable: true }, [
+                makeTestDominatorTreeNode({}),
+              ]);
+              ok(root.children);
+              ok(root.moreChildrenAvailable);
+ 
+              const expanded = new Set();
+              expanded.add(root.nodeId);
+ 
+-             yield renderComponent(DominatorTreeComponent(immutableUpdate(TEST_DOMINATOR_TREE_PROPS, {
++             await renderComponent(DominatorTreeComponent(immutableUpdate(TEST_DOMINATOR_TREE_PROPS, {
+                dominatorTree: immutableUpdate(TEST_DOMINATOR_TREE_PROPS.dominatorTree, {
+                  expanded,
+                  root,
+                }),
+              })), container);
+ 
+              ok(container.querySelector(".more-children"),
+                 "Should get a link to load more children");
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_DominatorTree_03.html b/devtools/client/memory/test/chrome/test_DominatorTree_03.html
+--- a/devtools/client/memory/test/chrome/test_DominatorTree_03.html
++++ b/devtools/client/memory/test/chrome/test_DominatorTree_03.html
+@@ -11,49 +11,49 @@ Test that expanded DominatorTreeItems ar
+ </head>
+ <body>
+     <!-- Give the container height so that the whole tree is rendered. -->
+     <div id="container" style="height: 900px;"></div>
+ 
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-        window.onload = Task.async(function* () {
++        window.onload = async function () {
+           try {
+             const container = document.getElementById("container");
+ 
+             // simple tree with one root and one child
+             const root = makeTestDominatorTreeNode(
+               { moreChildrenAvailable: false },
+               [
+                 makeTestDominatorTreeNode({ moreChildrenAvailable: false }),
+               ]);
+             ok(root.children);
+ 
+             // root node is expanded
+             const expanded = new Set();
+             expanded.add(root.nodeId);
+ 
+-            let component = yield renderComponent(
++            let component = await renderComponent(
+               DominatorTreeComponent(immutableUpdate(
+                 TEST_DOMINATOR_TREE_PROPS,
+                 {
+                   dominatorTree: immutableUpdate(
+                     TEST_DOMINATOR_TREE_PROPS.dominatorTree,
+                     { expanded, root }
+                   ),
+                 })), container);
+             ok(true, "Dominator tree rendered");
+ 
+             is(container.querySelectorAll(".tree-node").length, 2,
+                 "Should display two rows");
+             is(container.querySelectorAll(".arrow.open").length, 1,
+                 "Should display one expanded arrow");
+ 
+-            yield renderComponent(
++            await renderComponent(
+               DominatorTreeComponent(immutableUpdate(
+                 TEST_DOMINATOR_TREE_PROPS,
+                 {
+                   dominatorTree: immutableUpdate(
+                     TEST_DOMINATOR_TREE_PROPS.dominatorTree,
+                     { expanded: new Set(), root }
+                   )
+                 })), container);
+@@ -65,13 +65,13 @@ Test that expanded DominatorTreeItems ar
+             is(container.querySelectorAll(".arrow.open").length, 0,
+                 "Should display no expanded arrow");
+ 
+           } catch(e) {
+             ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+           } finally {
+             SimpleTest.finish();
+           }
+-        });
++        };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_Heap_01.html b/devtools/client/memory/test/chrome/test_Heap_01.html
+--- a/devtools/client/memory/test/chrome/test_Heap_01.html
++++ b/devtools/client/memory/test/chrome/test_Heap_01.html
+@@ -10,17 +10,17 @@ Test that rendering a dominator tree err
+     <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ </head>
+ <body>
+     <div id="container"></div>
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+ 
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              ok(React, "Should get React");
+              ok(Heap, "Should get Heap");
+ 
+              const errorMessage = "Something went wrong!";
+              const container = document.getElementById("container");
+ 
+              const props = immutableUpdate(TEST_HEAP_PROPS, {
+@@ -28,23 +28,23 @@ Test that rendering a dominator tree err
+                snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, {
+                  dominatorTree: {
+                    error: new Error(errorMessage),
+                    state: dominatorTreeState.ERROR,
+                  }
+                })
+              });
+ 
+-             yield renderComponent(Heap(props), container);
++             await renderComponent(Heap(props), container);
+ 
+              ok(container.querySelector(".error"), "Should render an error view");
+              ok(container.textContent.includes(errorMessage),
+                 "Should see our error message");
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_Heap_02.html b/devtools/client/memory/test/chrome/test_Heap_02.html
+--- a/devtools/client/memory/test/chrome/test_Heap_02.html
++++ b/devtools/client/memory/test/chrome/test_Heap_02.html
+@@ -9,70 +9,70 @@ Test that the currently selected view is
+     <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">
+ </head>
+ <body>
+     <div id="container"></div>
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              ok(React, "Should get React");
+              ok(Heap, "Should get Heap");
+ 
+              const errorMessage = "Something went wrong!";
+              const container = document.getElementById("container");
+ 
+              // Dominator tree view.
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                view: { state: viewState.DOMINATOR_TREE, },
+              })), container);
+ 
+              ok(container.querySelector(`[data-state=${dominatorTreeState.LOADED}]`),
+                 "Should render the dominator tree.");
+ 
+              // Census view.
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                view: { state: viewState.CENSUS, },
+              })), container);
+ 
+              ok(container.querySelector(`[data-state=${censusState.SAVED}]`),
+                 "Should render the census.");
+ 
+              // Diffing view.
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                view: { state: viewState.DIFFING, },
+                snapshot: null,
+                diffing: {
+                  firstSnapshotId: null,
+                  secondSnapshotId: null,
+                  census: null,
+                  error: null,
+                  state: diffingState.SELECTING,
+                },
+              })), container);
+ 
+              ok(container.querySelector(`[data-state=${diffingState.SELECTING}]`),
+                 "Should render the diffing.");
+ 
+              // Initial view.
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                snapshot: null,
+                diffing: null,
+              })), container);
+ 
+              ok(container.querySelector("[data-state=initial]"),
+                 "With no snapshot, nor a diffing, should render initial prompt.");
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_Heap_03.html b/devtools/client/memory/test/chrome/test_Heap_03.html
+--- a/devtools/client/memory/test/chrome/test_Heap_03.html
++++ b/devtools/client/memory/test/chrome/test_Heap_03.html
+@@ -10,65 +10,65 @@ but not in other dominator tree states.
+     <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">
+ </head>
+ <body>
+     <div id="container"></div>
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              const container = document.getElementById("container");
+ 
+              for (let state of [dominatorTreeState.COMPUTING, dominatorTreeState.FETCHING]) {
+-               yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++               await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                  view: { state: viewState.DOMINATOR_TREE, },
+                  snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, {
+                    dominatorTree: immutableUpdate(TEST_HEAP_PROPS.snapshot.dominatorTree, {
+                      state,
+                      root: null,
+                      dominatorTreeId: state === dominatorTreeState.FETCHING ? 1 : null,
+                    }),
+                  }),
+                })), container);
+ 
+                ok(container.querySelector(".devtools-throbber"),
+                   `Should show a throbber for state = ${state}`);
+              }
+ 
+              for (let state of [dominatorTreeState.LOADED, dominatorTreeState.INCREMENTAL_FETCHING]) {
+-               yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++               await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                  view: { state: viewState.DOMINATOR_TREE, },
+                  snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, {
+                    dominatorTree: immutableUpdate(TEST_HEAP_PROPS.snapshot.dominatorTree, {
+                      state,
+                      activeFetchRequestCount: state === dominatorTreeState.INCREMENTAL_FETCHING ? 1 : undefined,
+                    }),
+                  }),
+                })), container);
+ 
+                ok(!container.querySelector(".devtools-throbber"),
+                   `Should not show a throbber for state = ${state}`);
+              }
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                view: { state: viewState.DOMINATOR_TREE, },
+                snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, {
+                  dominatorTree: {
+                    state: dominatorTreeState.ERROR,
+                    error: new Error("example error for testing"),
+                  },
+                }),
+              })), container);
+ 
+              ok(!container.querySelector(".devtools-throbber"),
+                 `Should not show a throbber for ERROR state`);
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_Heap_04.html b/devtools/client/memory/test/chrome/test_Heap_04.html
+--- a/devtools/client/memory/test/chrome/test_Heap_04.html
++++ b/devtools/client/memory/test/chrome/test_Heap_04.html
+@@ -9,21 +9,21 @@ Test that we show the "hey you're not re
+     <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">
+ </head>
+ <body>
+     <div id="container"></div>
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              const container = document.getElementById("container");
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, {
+                  census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, {
+                    report: {
+                      bytes: 1,
+                      totalBytes: 1,
+                      count: 1,
+                      totalCount: 1,
+                      id: 1,
+@@ -44,17 +44,17 @@ Test that we show the "hey you're not re
+                    display: censusDisplays.allocationStack,
+                  }),
+                }),
+              })), container);
+ 
+              ok(container.querySelector(".no-allocation-stacks"),
+                 "When there are no allocation stacks, we should show the message");
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, {
+                  census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, {
+                    report: {
+                      bytes: 1,
+                      totalBytes: 1,
+                      count: 1,
+                      totalCount: 1,
+                      id: 1,
+@@ -85,17 +85,17 @@ Test that we show the "hey you're not re
+                    display: censusDisplays.allocationStack,
+                  }),
+                }),
+              })), container);
+ 
+              ok(!container.querySelector(".no-allocation-stacks"),
+                 "When there are allocation stacks, we should not show the message");
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, {
+                  census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, {
+                    report: {
+                      bytes: 1,
+                      totalBytes: 1,
+                      count: 1,
+                      totalCount: 1,
+                      id: 1,
+@@ -109,13 +109,13 @@ Test that we show the "hey you're not re
+ 
+              ok(!container.querySelector(".no-allocation-stacks"),
+                 "When there isn't census data, we should not show the message");
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_Heap_05.html b/devtools/client/memory/test/chrome/test_Heap_05.html
+--- a/devtools/client/memory/test/chrome/test_Heap_05.html
++++ b/devtools/client/memory/test/chrome/test_Heap_05.html
+@@ -9,21 +9,21 @@ Test that we show a message when the cen
+     <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">
+ </head>
+ <body>
+     <div id="container"></div>
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              const container = document.getElementById("container");
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, {
+                  census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, {
+                    report: {
+                      bytes: 1,
+                      totalBytes: 1,
+                      count: 1,
+                      totalCount: 1,
+                      id: 1,
+@@ -74,59 +74,59 @@ Test that we show a message when the cen
+                parentMap: Object.create(null),
+                display: censusDisplays.allocationStack,
+                filter: null,
+                expanded: new Immutable.Set(),
+                focused: null,
+                state: censusState.SAVED,
+              };
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, {
+                  census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, emptyCensus),
+                }),
+              })), container);
+ 
+              ok(container.querySelector(".empty"),
+                 "When the report is empty in census view, we show the empty message");
+              ok(container.textContent.includes(L10N.getStr("heapview.empty")));
+ 
+              // Empty Diffing Report
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                view: { state: viewState.DIFFING, },
+                diffing: {
+                  firstSnapshotId: 1,
+                  secondSnapshotId: 2,
+                  census: emptyCensus,
+                  state: diffingState.TOOK_DIFF,
+                },
+                snapshot: null,
+              })), container);
+ 
+              ok(container.querySelector(".empty"),
+                 "When the report is empty in diffing view, the empty message is shown");
+              ok(container.textContent.includes(L10N.getStr("heapview.no-difference")));
+ 
+              // Empty Filtered Census
+ 
+-             yield renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
++             await renderComponent(Heap(immutableUpdate(TEST_HEAP_PROPS, {
+                snapshot: immutableUpdate(TEST_HEAP_PROPS.snapshot, {
+                  census: immutableUpdate(TEST_HEAP_PROPS.snapshot.census, immutableUpdate(emptyCensus, {
+                    filter: "zzzz"
+                  })),
+                }),
+              })), container);
+ 
+              ok(container.querySelector(".empty"),
+                 "When the report is empty in census view w/ filter, we show the empty message");
+              ok(container.textContent.includes(L10N.getStr("heapview.none-match")));
+ 
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_List_01.html b/devtools/client/memory/test/chrome/test_List_01.html
+--- a/devtools/client/memory/test/chrome/test_List_01.html
++++ b/devtools/client/memory/test/chrome/test_List_01.html
+@@ -10,28 +10,28 @@ Test to verify the delete button calls t
+     <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ </head>
+ <body>
+     <div id="container"></div>
+ 
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+           try {
+             const container = document.getElementById("container");
+ 
+             let deletedSnapshots = [];
+ 
+             let snapshots = [ TEST_SNAPSHOT, TEST_SNAPSHOT, TEST_SNAPSHOT ]
+               .map((snapshot, index) => immutableUpdate(snapshot, {
+                 index: snapshot.index + index
+               }));
+ 
+-            yield renderComponent(
++            await renderComponent(
+               List({
+                 itemComponent: SnapshotListItem,
+                 onClick: noop,
+                 onDelete: (item) => deletedSnapshots.push(item),
+                 items: snapshots
+               }),
+               container
+             );
+@@ -62,13 +62,13 @@ Test to verify the delete button calls t
+               "Deleted snapshot was added to the deleted list\n");
+ 
+ 
+           } catch(e) {
+             ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+           } finally {
+             SimpleTest.finish();
+           }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_ShortestPaths_01.html b/devtools/client/memory/test/chrome/test_ShortestPaths_01.html
+--- a/devtools/client/memory/test/chrome/test_ShortestPaths_01.html
++++ b/devtools/client/memory/test/chrome/test_ShortestPaths_01.html
+@@ -18,21 +18,21 @@ Test that the ShortestPaths component pr
+ </head>
+ <body>
+     <!-- Give the container height so that the whole tree is rendered. -->
+     <div id="container" style="height: 900px;"></div>
+ 
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              const container = document.getElementById("container");
+ 
+-             yield renderComponent(ShortestPaths(TEST_SHORTEST_PATHS_PROPS), container);
++             await renderComponent(ShortestPaths(TEST_SHORTEST_PATHS_PROPS), container);
+ 
+              let found1 = false;
+              let found2 = false;
+              let found3 = false;
+ 
+              let found1to2 = false;
+              let found1to3 = false;
+              let found2to3 = false;
+@@ -100,13 +100,13 @@ Test that the ShortestPaths component pr
+              ok(found1to3, "Should have rendered edge 1->3");
+              ok(found2to3, "Should have rendered edge 2->3");
+ 
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_ShortestPaths_02.html b/devtools/client/memory/test/chrome/test_ShortestPaths_02.html
+--- a/devtools/client/memory/test/chrome/test_ShortestPaths_02.html
++++ b/devtools/client/memory/test/chrome/test_ShortestPaths_02.html
+@@ -18,28 +18,28 @@ Test that the ShortestPaths component re
+ </head>
+ <body>
+     <!-- Give the container height so that the whole tree is rendered. -->
+     <div id="container" style="height: 900px;"></div>
+ 
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              const container = document.getElementById("container");
+ 
+-             yield renderComponent(ShortestPaths(immutableUpdate(TEST_SHORTEST_PATHS_PROPS,
++             await renderComponent(ShortestPaths(immutableUpdate(TEST_SHORTEST_PATHS_PROPS,
+                                                                  { graph: null })),
+                                    container);
+ 
+              ok(container.textContent.includes(L10N.getStr("shortest-paths.select-node")),
+                 "The node selection prompt is displayed");
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_SnapshotListItem_01.html b/devtools/client/memory/test/chrome/test_SnapshotListItem_01.html
+--- a/devtools/client/memory/test/chrome/test_SnapshotListItem_01.html
++++ b/devtools/client/memory/test/chrome/test_SnapshotListItem_01.html
+@@ -11,43 +11,43 @@ path.
+     <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ </head>
+ <body>
+     <div id="container"></div>
+ 
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+           try {
+             const container = document.getElementById("container");
+ 
+-            yield renderComponent(
++            await renderComponent(
+               SnapshotListItem(TEST_SNAPSHOT_LIST_ITEM_PROPS),
+               container
+             );
+ 
+             ok(container.querySelector('.delete'),
+                "Should have delete button when there is a path");
+ 
+             const pathlessProps = immutableUpdate(
+                 TEST_SNAPSHOT_LIST_ITEM_PROPS,
+                 {item: immutableUpdate(TEST_SNAPSHOT, {path: null})}
+             );
+ 
+-            yield renderComponent(
++            await renderComponent(
+               SnapshotListItem(pathlessProps),
+               container
+             );
+ 
+             ok(!container.querySelector('.delete'),
+                "No delete button should be found if there is no path\n");
+ 
+           } catch(e) {
+             ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+           } finally {
+             SimpleTest.finish();
+           }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_Toolbar_01.html b/devtools/client/memory/test/chrome/test_Toolbar_01.html
+--- a/devtools/client/memory/test/chrome/test_Toolbar_01.html
++++ b/devtools/client/memory/test/chrome/test_Toolbar_01.html
+@@ -9,39 +9,39 @@ Test that the Toolbar component shows th
+     <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">
+ </head>
+ <body>
+     <div id="container"></div>
+     <pre id="test">
+         <script src="head.js" type="application/javascript"></script>
+         <script type="application/javascript">
+-         window.onload = Task.async(function* () {
++         window.onload = async function () {
+            try {
+              const container = document.getElementById("container");
+ 
+              // Census and dominator tree views.
+ 
+              for (let view of [viewState.CENSUS, viewState.DOMINATOR_TREE]) {
+-               yield renderComponent(Toolbar(immutableUpdate(TEST_TOOLBAR_PROPS, {
++               await renderComponent(Toolbar(immutableUpdate(TEST_TOOLBAR_PROPS, {
+                  view: { state: view },
+                })), container);
+ 
+                ok(container.querySelector("#select-view"),
+                   `The view selector is shown in view = ${view}`);
+              }
+ 
+-             yield renderComponent(Toolbar(immutableUpdate(TEST_TOOLBAR_PROPS, {
++             await renderComponent(Toolbar(immutableUpdate(TEST_TOOLBAR_PROPS, {
+                view: { state: viewState.DIFFING, },
+              })), container);
+ 
+              ok(!container.querySelector("#select-view"),
+                 "The view selector is NOT shown in the DIFFING view");
+            } catch(e) {
+              ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+            } finally {
+              SimpleTest.finish();
+            }
+-         });
++         };
+         </script>
+     </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/chrome/test_TreeMap_01.html b/devtools/client/memory/test/chrome/test_TreeMap_01.html
+--- a/devtools/client/memory/test/chrome/test_TreeMap_01.html
++++ b/devtools/client/memory/test/chrome/test_TreeMap_01.html
+@@ -15,30 +15,30 @@ Test that the Tree Map correctly renders
+ </head>
+ <body>
+   <!-- Give the container height so that the whole tree is rendered. -->
+   <div id="container" style="height: 900px;"></div>
+ 
+   <pre id="test">
+     <script src="head.js" type="application/javascript"></script>
+     <script type="application/javascript">
+-      window.onload = Task.async(function*() {
++      window.onload = async function () {
+         try {
+           const container = document.getElementById("container");
+ 
+-          yield renderComponent(TreeMap(TEST_TREE_MAP_PROPS), container);
++          await renderComponent(TreeMap(TEST_TREE_MAP_PROPS), container);
+ 
+           let treeMapContainer = container.querySelector(".tree-map-container");
+           ok(treeMapContainer, "Component creates a container");
+ 
+           let canvases = treeMapContainer.querySelectorAll("canvas");
+           is(canvases.length, 2, "Creates 2 canvases");
+ 
+         } catch(e) {
+           ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+         } finally {
+           SimpleTest.finish();
+         }
+-      });
++      };
+     </script>
+   </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/memory/test/unit/head.js b/devtools/client/memory/test/unit/head.js
+--- a/devtools/client/memory/test/unit/head.js
++++ b/devtools/client/memory/test/unit/head.js
+@@ -13,17 +13,16 @@ flags.testing = true;
+ flags.wantLogging = true;
+ flags.wantVerbose = false;
+ 
+ var { OS } = require("resource://gre/modules/osfile.jsm");
+ var { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
+ var { TargetFactory } = require("devtools/client/framework/target");
+ var promise = require("promise");
+ var defer = require("devtools/shared/defer");
+-var { Task } = require("devtools/shared/task");
+ var { expectState } = require("devtools/server/actors/common");
+ var HeapSnapshotFileUtils = require("devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
+ var HeapAnalysesClient = require("devtools/shared/heapsnapshot/HeapAnalysesClient");
+ var { addDebuggerToGlobal } = require("resource://gre/modules/jsdebugger.jsm");
+ var Store = require("devtools/client/memory/store");
+ var { L10N } = require("devtools/client/memory/utils");
+ var SYSTEM_PRINCIPAL =
+   Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
+@@ -46,38 +45,38 @@ function initDebugger() {
+ }
+ 
+ function StubbedMemoryFront() {
+   this.state = "detached";
+   this.recordingAllocations = false;
+   this.dbg = initDebugger();
+ }
+ 
+-StubbedMemoryFront.prototype.attach = Task.async(function* () {
++StubbedMemoryFront.prototype.attach = async function() {
+   this.state = "attached";
+-});
++};
+ 
+-StubbedMemoryFront.prototype.detach = Task.async(function* () {
++StubbedMemoryFront.prototype.detach = async function() {
+   this.state = "detached";
+-});
++};
+ 
+ StubbedMemoryFront.prototype.saveHeapSnapshot = expectState("attached",
+-  Task.async(function* () {
++  async function() {
+     return ChromeUtils.saveHeapSnapshot({ runtime: true });
+-  }), "saveHeapSnapshot");
++  }, "saveHeapSnapshot");
+ 
+ StubbedMemoryFront.prototype.startRecordingAllocations = expectState("attached",
+-  Task.async(function* () {
++  async function() {
+     this.recordingAllocations = true;
+-  }));
++  });
+ 
+ StubbedMemoryFront.prototype.stopRecordingAllocations = expectState("attached",
+-  Task.async(function* () {
++  async function() {
+     this.recordingAllocations = false;
+-  }));
++  });
+ 
+ function waitUntilSnapshotState(store, expected) {
+   let predicate = () => {
+     let snapshots = store.getState().snapshots;
+     info(snapshots.map(x => x.state));
+     return snapshots.length === expected.length &&
+            expected.every((state, i) => state === "*" || snapshots[i].state === state);
+   };
+@@ -116,16 +115,16 @@ function waitUntilCensusState(store, get
+                     (!census && !state) ||
+                     (census && census.state === state);
+            });
+   };
+   info(`Waiting for snapshots' censuses to be of state: ${expected}`);
+   return waitUntilState(store, predicate);
+ }
+ 
+-function* createTempFile() {
++async function createTempFile() {
+   let file = FileUtils.getFile("TmpD", ["tmp.fxsnapshot"]);
+   file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+   let destPath = file.path;
+-  let stat = yield OS.File.stat(destPath);
++  let stat = await OS.File.stat(destPath);
+   ok(stat.size === 0, "new file is 0 bytes at start");
+   return destPath;
+ }
+diff --git a/devtools/client/memory/test/unit/test_action-clear-snapshots_01.js b/devtools/client/memory/test/unit/test_action-clear-snapshots_01.js
+--- a/devtools/client/memory/test/unit/test_action-clear-snapshots_01.js
++++ b/devtools/client/memory/test/unit/test_action-clear-snapshots_01.js
+@@ -4,33 +4,33 @@
+ "use strict";
+ 
+ // Test clearSnapshots deletes snapshots with READ censuses
+ 
+ let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
+ let { actions } = require("devtools/client/memory/constants");
+ const { treeMapState } = require("devtools/client/memory/constants");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
++  await waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
+   ok(true, "snapshot created");
+ 
+   ok(true, "dispatch clearSnapshots action");
+   let deleteEvents = Promise.all([
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
+   ]);
+   dispatch(clearSnapshots(heapWorker));
+-  yield deleteEvents;
++  await deleteEvents;
+   ok(true, "received delete snapshots events");
+ 
+   equal(getState().snapshots.length, 0, "no snapshot remaining");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-clear-snapshots_02.js b/devtools/client/memory/test/unit/test_action-clear-snapshots_02.js
+--- a/devtools/client/memory/test/unit/test_action-clear-snapshots_02.js
++++ b/devtools/client/memory/test/unit/test_action-clear-snapshots_02.js
+@@ -3,43 +3,43 @@
+ 
+ "use strict";
+ 
+ // Test clearSnapshots preserves snapshots with state != READ or ERROR
+ 
+ let { takeSnapshotAndCensus, clearSnapshots, takeSnapshot } = require("devtools/client/memory/actions/snapshot");
+ let { snapshotState: states, treeMapState, actions } = require("devtools/client/memory/constants");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   ok(true, "create a snapshot with a census in SAVED state");
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   ok(true, "create a snapshot in SAVED state");
+   dispatch(takeSnapshot(front));
+-  yield waitUntilSnapshotState(store, [states.SAVED, states.SAVED]);
+-  yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
++  await waitUntilSnapshotState(store, [states.SAVED, states.SAVED]);
++  await waitUntilCensusState(store, snapshot => snapshot.treeMap,
+                              [treeMapState.SAVED, null]);
+   ok(true, "snapshots created with expected states");
+ 
+   ok(true, "dispatch clearSnapshots action");
+   let deleteEvents = Promise.all([
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
+   ]);
+   dispatch(clearSnapshots(heapWorker));
+-  yield deleteEvents;
++  await deleteEvents;
+   ok(true, "received delete snapshots events");
+ 
+   equal(getState().snapshots.length, 1, "one snapshot remaining");
+   let remainingSnapshot = getState().snapshots[0];
+   equal(remainingSnapshot.treeMap, undefined,
+     "remaining snapshot doesn't have a treeMap property");
+   equal(remainingSnapshot.census, undefined,
+     "remaining snapshot doesn't have a census property");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-clear-snapshots_03.js b/devtools/client/memory/test/unit/test_action-clear-snapshots_03.js
+--- a/devtools/client/memory/test/unit/test_action-clear-snapshots_03.js
++++ b/devtools/client/memory/test/unit/test_action-clear-snapshots_03.js
+@@ -3,42 +3,42 @@
+ 
+ "use strict";
+ 
+ // Test clearSnapshots deletes snapshots with state ERROR
+ 
+ let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
+ let { snapshotState: states, treeMapState, actions } = require("devtools/client/memory/constants");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   ok(true, "create a snapshot with a treeMap");
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilSnapshotState(store, [states.SAVED]);
++  await waitUntilSnapshotState(store, [states.SAVED]);
+   ok(true, "snapshot created with a SAVED state");
+-  yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
++  await waitUntilCensusState(store, snapshot => snapshot.treeMap,
+                              [treeMapState.SAVED]);
+   ok(true, "treeMap created with a SAVED state");
+ 
+   ok(true, "set snapshot state to error");
+   let id = getState().snapshots[0].id;
+   dispatch({ type: actions.SNAPSHOT_ERROR, id, error: new Error("_") });
+-  yield waitUntilSnapshotState(store, [states.ERROR]);
++  await waitUntilSnapshotState(store, [states.ERROR]);
+   ok(true, "snapshot set to error state");
+ 
+   ok(true, "dispatch clearSnapshots action");
+   let deleteEvents = Promise.all([
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
+   ]);
+   dispatch(clearSnapshots(heapWorker));
+-  yield deleteEvents;
++  await deleteEvents;
+   ok(true, "received delete snapshots events");
+   equal(getState().snapshots.length, 0, "error snapshot deleted");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-clear-snapshots_04.js b/devtools/client/memory/test/unit/test_action-clear-snapshots_04.js
+--- a/devtools/client/memory/test/unit/test_action-clear-snapshots_04.js
++++ b/devtools/client/memory/test/unit/test_action-clear-snapshots_04.js
+@@ -3,44 +3,44 @@
+ 
+ "use strict";
+ 
+ // Test clearSnapshots deletes several snapshots
+ 
+ let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
+ let { snapshotState: states, actions, treeMapState } = require("devtools/client/memory/constants");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   ok(true, "create 3 snapshots with a saved census");
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
++  await waitUntilCensusState(store, snapshot => snapshot.treeMap,
+     [treeMapState.SAVED, treeMapState.SAVED, treeMapState.SAVED]);
+   ok(true, "snapshots created with a saved census");
+ 
+   ok(true, "set first snapshot state to error");
+   let id = getState().snapshots[0].id;
+   dispatch({ type: actions.SNAPSHOT_ERROR, id, error: new Error("_") });
+-  yield waitUntilSnapshotState(store,
++  await waitUntilSnapshotState(store,
+     [states.ERROR, states.READ, states.READ]);
+   ok(true, "first snapshot set to error state");
+ 
+   ok(true, "dispatch clearSnapshots action");
+   let deleteEvents = Promise.all([
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
+   ]);
+   dispatch(clearSnapshots(heapWorker));
+-  yield deleteEvents;
++  await deleteEvents;
+   ok(true, "received delete snapshots events");
+ 
+   equal(getState().snapshots.length, 0, "no snapshot remaining");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-clear-snapshots_05.js b/devtools/client/memory/test/unit/test_action-clear-snapshots_05.js
+--- a/devtools/client/memory/test/unit/test_action-clear-snapshots_05.js
++++ b/devtools/client/memory/test/unit/test_action-clear-snapshots_05.js
+@@ -3,43 +3,43 @@
+ 
+ "use strict";
+ 
+ // Test clearSnapshots deletes several snapshots
+ 
+ let { takeSnapshotAndCensus, clearSnapshots } = require("devtools/client/memory/actions/snapshot");
+ let { actions, treeMapState } = require("devtools/client/memory/constants");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   ok(true, "create 2 snapshots with a saved census");
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   ok(true, "snapshots created with a saved census");
+-  yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
++  await waitUntilCensusState(store, snapshot => snapshot.treeMap,
+                              [treeMapState.SAVED, treeMapState.SAVED]);
+ 
+   let errorHeapWorker = {
+     deleteHeapSnapshot: function() {
+       return Promise.reject("_");
+     }
+   };
+ 
+   ok(true, "dispatch clearSnapshots action");
+   let deleteEvents = Promise.all([
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_END),
+     waitUntilAction(store, actions.SNAPSHOT_ERROR),
+     waitUntilAction(store, actions.SNAPSHOT_ERROR),
+   ]);
+   dispatch(clearSnapshots(errorHeapWorker));
+-  yield deleteEvents;
++  await deleteEvents;
+   ok(true, "received delete snapshots and snapshot error events");
+   equal(getState().snapshots.length, 0, "no snapshot remaining");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-clear-snapshots_06.js b/devtools/client/memory/test/unit/test_action-clear-snapshots_06.js
+--- a/devtools/client/memory/test/unit/test_action-clear-snapshots_06.js
++++ b/devtools/client/memory/test/unit/test_action-clear-snapshots_06.js
+@@ -13,48 +13,48 @@ const {
+   actions,
+   treeMapState
+ } = require("devtools/client/memory/constants");
+ const {
+   toggleDiffing,
+   selectSnapshotForDiffingAndRefresh
+ } = require("devtools/client/memory/actions/diffing");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   ok(true, "create 2 snapshots with a saved census");
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
++  await waitUntilCensusState(store, snapshot => snapshot.treeMap,
+                              [treeMapState.SAVED, treeMapState.SAVED]);
+   ok(true, "snapshots created with a saved census");
+ 
+   dispatch(toggleDiffing());
+   dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
+                                               getState().snapshots[0]));
+   dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
+                                               getState().snapshots[1]));
+ 
+   ok(getState().diffing, "We should be in diffing view");
+ 
+-  yield waitUntilAction(store, actions.TAKE_CENSUS_DIFF_END);
++  await waitUntilAction(store, actions.TAKE_CENSUS_DIFF_END);
+   ok(true, "Received TAKE_CENSUS_DIFF_END action");
+ 
+   ok(true, "Dispatch clearSnapshots action");
+   let deleteEvents = Promise.all([
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
+     waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
+   ]);
+   dispatch(clearSnapshots(heapWorker));
+-  yield deleteEvents;
++  await deleteEvents;
+   ok(true, "received delete snapshots events");
+ 
+   ok(getState().snapshots.length === 0, "Snapshots array should be empty");
+   ok(!getState().diffing, "We should no longer be diffing");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-export-snapshot.js b/devtools/client/memory/test/unit/test_action-export-snapshot.js
+--- a/devtools/client/memory/test/unit/test_action-export-snapshot.js
++++ b/devtools/client/memory/test/unit/test_action-export-snapshot.js
+@@ -4,34 +4,34 @@
+ "use strict";
+ 
+ // Test exporting a snapshot to a user specified location on disk.
+ 
+ let { exportSnapshot } = require("devtools/client/memory/actions/io");
+ let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
+ let { actions, treeMapState } = require("devtools/client/memory/constants");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+-  let destPath = yield createTempFile();
++  let destPath = await createTempFile();
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, snapshot => snapshot.treeMap,
++  await waitUntilCensusState(store, snapshot => snapshot.treeMap,
+                              [treeMapState.SAVED]);
+ 
+   let exportEvents = Promise.all([
+     waitUntilAction(store, actions.EXPORT_SNAPSHOT_START),
+     waitUntilAction(store, actions.EXPORT_SNAPSHOT_END)
+   ]);
+   dispatch(exportSnapshot(getState().snapshots[0], destPath));
+-  yield exportEvents;
++  await exportEvents;
+ 
+-  let stat = yield OS.File.stat(destPath);
++  let stat = await OS.File.stat(destPath);
+   info(stat.size);
+   ok(stat.size > 0, "destination file is more than 0 bytes");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-filter-02.js b/devtools/client/memory/test/unit/test_action-filter-02.js
+--- a/devtools/client/memory/test/unit/test_action-filter-02.js
++++ b/devtools/client/memory/test/unit/test_action-filter-02.js
+@@ -5,56 +5,56 @@
+ 
+ // Test that changing filter state properly refreshes the selected census.
+ 
+ let { viewState, censusState } = require("devtools/client/memory/constants");
+ let { setFilterStringAndRefresh } = require("devtools/client/memory/actions/filter");
+ let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
+ let { changeView } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   equal(getState().filter, null, "no filter by default");
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+ 
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED, censusState.SAVED, censusState.SAVED]);
+   ok(true, "saved 3 snapshots and took a census of each of them");
+ 
+   dispatch(setFilterStringAndRefresh("str", heapWorker));
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED, censusState.SAVED, censusState.SAVING]);
+   ok(true, "setting filter string should recompute the selected snapshot's census");
+ 
+   equal(getState().filter, "str", "now inverted");
+ 
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED, censusState.SAVED, censusState.SAVED]);
+ 
+   equal(getState().snapshots[0].census.filter, null);
+   equal(getState().snapshots[1].census.filter, null);
+   equal(getState().snapshots[2].census.filter, "str");
+ 
+   dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED, censusState.SAVING, censusState.SAVED]);
+   ok(true, "selecting non-inverted census should trigger a recompute");
+ 
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED, censusState.SAVED, censusState.SAVED]);
+ 
+   equal(getState().snapshots[0].census.filter, null);
+   equal(getState().snapshots[1].census.filter, "str");
+   equal(getState().snapshots[2].census.filter, "str");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-filter-03.js b/devtools/client/memory/test/unit/test_action-filter-03.js
+--- a/devtools/client/memory/test/unit/test_action-filter-03.js
++++ b/devtools/client/memory/test/unit/test_action-filter-03.js
+@@ -6,45 +6,45 @@
+ // Test that changing filter state in the middle of taking a snapshot results in
+ // the properly fitered census.
+ 
+ let { snapshotState: states, censusState, viewState } = require("devtools/client/memory/constants");
+ let { setFilterString, setFilterStringAndRefresh } = require("devtools/client/memory/actions/filter");
+ let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
+ let { changeView } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilSnapshotState(store, [states.SAVING]);
++  await waitUntilSnapshotState(store, [states.SAVING]);
+ 
+   dispatch(setFilterString("str"));
+ 
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED]);
+   equal(getState().filter, "str",
+         "should want filtered trees");
+   equal(getState().snapshots[0].census.filter, "str",
+         "snapshot-we-were-in-the-middle-of-saving's census should be filtered");
+ 
+   dispatch(setFilterStringAndRefresh("", heapWorker));
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVING]);
+   ok(true, "changing filter string retriggers census");
+   ok(!getState().filter, "no longer filtering");
+ 
+   dispatch(setFilterString("obj"));
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED]);
+   equal(getState().filter, "obj", "filtering for obj now");
+   equal(getState().snapshots[0].census.filter, "obj",
+         "census-we-were-in-the-middle-of-recomputing should be filtered again");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-import-snapshot-and-census.js b/devtools/client/memory/test/unit/test_action-import-snapshot-and-census.js
+--- a/devtools/client/memory/test/unit/test_action-import-snapshot-and-census.js
++++ b/devtools/client/memory/test/unit/test_action-import-snapshot-and-census.js
+@@ -7,33 +7,33 @@
+  * Tests the task creator `importSnapshotAndCensus()` for the whole flow of
+  * importing a snapshot, and its sub-actions.
+  */
+ 
+ let { actions, snapshotState: states, treeMapState } = require("devtools/client/memory/constants");
+ let { exportSnapshot, importSnapshotAndCensus } = require("devtools/client/memory/actions/io");
+ let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { subscribe, dispatch, getState } = store;
+ 
+-  let destPath = yield createTempFile();
++  let destPath = await createTempFile();
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
++  await waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
+ 
+   let exportEvents = Promise.all([
+     waitUntilAction(store, actions.EXPORT_SNAPSHOT_START),
+     waitUntilAction(store, actions.EXPORT_SNAPSHOT_END)
+   ]);
+   dispatch(exportSnapshot(getState().snapshots[0], destPath));
+-  yield exportEvents;
++  await exportEvents;
+ 
+   // Now import our freshly exported snapshot
+   let snapshotI = 0;
+   let censusI = 0;
+   let snapshotStates = ["IMPORTING", "READING", "READ"];
+   let censusStates = ["SAVING", "SAVED"];
+   let expectStates = () => {
+     let snapshot = getState().snapshots[1];
+@@ -53,17 +53,17 @@ add_task(function* () {
+         censusI++;
+       }
+     }
+   };
+ 
+   let unsubscribe = subscribe(expectStates);
+   dispatch(importSnapshotAndCensus(heapWorker, destPath));
+ 
+-  yield waitUntilState(store, () => {
++  await waitUntilState(store, () => {
+     return snapshotI === snapshotStates.length &&
+            censusI === censusStates.length;
+   });
+   unsubscribe();
+   equal(snapshotI, snapshotStates.length,
+     "importSnapshotAndCensus() produces the correct sequence of states in a snapshot");
+   equal(getState().snapshots[1].state, states.READ,
+     "imported snapshot is in READ state");
+diff --git a/devtools/client/memory/test/unit/test_action-import-snapshot-dominator-tree.js b/devtools/client/memory/test/unit/test_action-import-snapshot-dominator-tree.js
+--- a/devtools/client/memory/test/unit/test_action-import-snapshot-dominator-tree.js
++++ b/devtools/client/memory/test/unit/test_action-import-snapshot-dominator-tree.js
+@@ -9,20 +9,20 @@
+  * should be computed.
+  */
+ 
+ let { snapshotState, dominatorTreeState, viewState, treeMapState } =
+   require("devtools/client/memory/constants");
+ let { importSnapshotAndCensus } = require("devtools/client/memory/actions/io");
+ let { changeViewAndRefresh } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { subscribe, dispatch, getState } = store;
+ 
+   dispatch(changeViewAndRefresh(viewState.DOMINATOR_TREE, heapWorker));
+   equal(getState().view.state, viewState.DOMINATOR_TREE,
+         "We should now be in the DOMINATOR_TREE view");
+ 
+   let i = 0;
+@@ -40,20 +40,20 @@ add_task(function* () {
+     let snapshot = getState().snapshots[0];
+     if (snapshot && hasExpectedState(snapshot, expected[i])) {
+       ok(true, `Found expected state ${expected[i]}`);
+       i++;
+     }
+   };
+ 
+   let unsubscribe = subscribe(expectStates);
+-  const snapshotPath = yield front.saveHeapSnapshot();
++  const snapshotPath = await front.saveHeapSnapshot();
+   dispatch(importSnapshotAndCensus(heapWorker, snapshotPath));
+ 
+-  yield waitUntilState(store, () => i === expected.length);
++  await waitUntilState(store, () => i === expected.length);
+   unsubscribe();
+   equal(i, expected.length, "importSnapshotAndCensus() produces the correct " +
+     "sequence of states in a snapshot");
+   equal(getState().snapshots[0].dominatorTree.state, dominatorTreeState.LOADED,
+     "imported snapshot's dominator tree is in LOADED state");
+   ok(getState().snapshots[0].selected, "imported snapshot is selected");
+ });
+ 
+diff --git a/devtools/client/memory/test/unit/test_action-select-snapshot.js b/devtools/client/memory/test/unit/test_action-select-snapshot.js
+--- a/devtools/client/memory/test/unit/test_action-select-snapshot.js
++++ b/devtools/client/memory/test/unit/test_action-select-snapshot.js
+@@ -5,32 +5,32 @@
+ 
+ /**
+  * Tests the reducer responding to the action `selectSnapshot(snapshot)`
+  */
+ 
+ let actions = require("devtools/client/memory/actions/snapshot");
+ let { snapshotState: states } = require("devtools/client/memory/constants");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+ 
+   for (let i = 0; i < 5; i++) {
+     store.dispatch(actions.takeSnapshot(front));
+   }
+ 
+-  yield waitUntilState(store,
++  await waitUntilState(store,
+     ({ snapshots }) => snapshots.length === 5 && snapshots.every(isDone));
+ 
+   for (let i = 0; i < 5; i++) {
+     info(`Selecting snapshot[${i}]`);
+     store.dispatch(actions.selectSnapshot(store.getState().snapshots[i].id));
+-    yield waitUntilState(store, ({ snapshots }) => snapshots[i].selected);
++    await waitUntilState(store, ({ snapshots }) => snapshots[i].selected);
+ 
+     let { snapshots } = store.getState();
+     ok(snapshots[i].selected, `snapshot[${i}] selected`);
+     equal(snapshots.filter(s => !s.selected).length, 4,
+           "All other snapshots are unselected");
+   }
+ });
+ 
+diff --git a/devtools/client/memory/test/unit/test_action-set-display-and-refresh-01.js b/devtools/client/memory/test/unit/test_action-set-display-and-refresh-01.js
+--- a/devtools/client/memory/test/unit/test_action-set-display-and-refresh-01.js
++++ b/devtools/client/memory/test/unit/test_action-set-display-and-refresh-01.js
+@@ -13,101 +13,101 @@
+ let { censusDisplays, censusState, viewState } = require("devtools/client/memory/constants");
+ let { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
+ let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+ // We test setting an invalid display, which triggers an assertion failure.
+ EXPECTED_DTU_ASSERT_FAILURE_COUNT = 1;
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   // Test default display with no snapshots
+   equal(getState().censusDisplay.breakdown.by, "coarseType",
+         "default coarseType display selected at start.");
+   dispatch(setCensusDisplayAndRefresh(heapWorker,
+                                       censusDisplays.allocationStack));
+   equal(getState().censusDisplay.breakdown.by, "allocationStack",
+         "display changed with no snapshots");
+ 
+   // Test invalid displays
+   ok(getState().errors.length === 0, "No error actions in the queue.");
+   dispatch(setCensusDisplayAndRefresh(heapWorker, {}));
+-  yield waitUntilState(store, () => getState().errors.length === 1);
++  await waitUntilState(store, () => getState().errors.length === 1);
+   ok(true, "Emits an error action when passing in an invalid display object");
+ 
+   equal(getState().censusDisplay.breakdown.by, "allocationStack",
+     "current display unchanged when passing invalid display");
+ 
+   // Test new snapshots
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED]);
+ 
+   equal(getState().snapshots[0].census.display, censusDisplays.allocationStack,
+         "New snapshot's census uses correct display");
+ 
+   // Updates when changing display during `SAVING`
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED, censusState.SAVING]);
+   dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType));
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED, censusState.SAVED]);
+   equal(getState().snapshots[1].census.display, censusDisplays.coarseType,
+         "Changing display while saving a snapshot results " +
+         "in a census using the new display");
+ 
+   // Updates when changing display during `SAVING_CENSUS`
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED, censusState.SAVED, censusState.SAVING]);
+   dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED, censusState.SAVED, censusState.SAVED]);
+   equal(getState().snapshots[2].census.display, censusDisplays.allocationStack,
+         "Display can be changed while saving census, stores updated display in snapshot");
+ 
+   // Updates census on currently selected snapshot when changing display
+   ok(getState().snapshots[2].selected, "Third snapshot currently selected");
+   dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType));
+-  yield waitUntilState(store,
++  await waitUntilState(store,
+                        state => state.snapshots[2].census.state === censusState.SAVING);
+-  yield waitUntilState(store,
++  await waitUntilState(store,
+                        state => state.snapshots[2].census.state === censusState.SAVED);
+   equal(getState().snapshots[2].census.display, censusDisplays.coarseType,
+         "Snapshot census updated when changing displays " +
+         "after already generating one census");
+ 
+   dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
+-  yield waitUntilState(store,
++  await waitUntilState(store,
+                        state => state.snapshots[2].census.state === censusState.SAVED);
+   equal(getState().snapshots[2].census.display, censusDisplays.allocationStack,
+         "Snapshot census updated when changing displays " +
+         "after already generating one census");
+ 
+   // Does not update unselected censuses.
+   ok(!getState().snapshots[1].selected, "Second snapshot selected currently");
+   equal(getState().snapshots[1].census.display, censusDisplays.coarseType,
+         "Second snapshot using `coarseType` display still and " +
+         "not yet updated to correct display");
+ 
+   // Updates to current display when switching to stale snapshot.
+   dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED, censusState.SAVING, censusState.SAVED]);
+-  yield waitUntilCensusState(store, snapshot => snapshot.census,
++  await waitUntilCensusState(store, snapshot => snapshot.census,
+                              [censusState.SAVED, censusState.SAVED, censusState.SAVED]);
+ 
+   ok(getState().snapshots[1].selected, "Second snapshot selected currently");
+   equal(getState().snapshots[1].census.display, censusDisplays.allocationStack,
+         "Second snapshot using `allocationStack` display and updated to correct display");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-set-display-and-refresh-02.js b/devtools/client/memory/test/unit/test_action-set-display-and-refresh-02.js
+--- a/devtools/client/memory/test/unit/test_action-set-display-and-refresh-02.js
++++ b/devtools/client/memory/test/unit/test_action-set-display-and-refresh-02.js
+@@ -18,30 +18,30 @@ let CUSTOM = {
+   tooltip: "Custom tooltip",
+   inverted: false,
+   breakdown: {
+     by: "internalType",
+     then: { by: "count", bytes: true, count: false }
+   }
+ };
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+   dispatch(setCensusDisplayAndRefresh(heapWorker, CUSTOM));
+   equal(getState().censusDisplay, CUSTOM,
+         "CUSTOM display stored in display state.");
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+ 
+   equal(getState().snapshots[0].census.display, CUSTOM,
+   "New snapshot stored CUSTOM display when done taking census");
+   ok(getState().snapshots[0].census.report.children.length, "Census has some children");
+   // Ensure we don't have `count` in any results
+   ok(getState().snapshots[0].census.report.children.every(c => !c.count),
+      "Census used CUSTOM display without counts");
+   // Ensure we do have `bytes` in the results
+diff --git a/devtools/client/memory/test/unit/test_action-set-display.js b/devtools/client/memory/test/unit/test_action-set-display.js
+--- a/devtools/client/memory/test/unit/test_action-set-display.js
++++ b/devtools/client/memory/test/unit/test_action-set-display.js
+@@ -12,20 +12,20 @@
+ let { censusDisplays, censusState, viewState } = require("devtools/client/memory/constants");
+ let { setCensusDisplay } = require("devtools/client/memory/actions/census-display");
+ let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+ // We test setting an invalid display, which triggers an assertion failure.
+ EXPECTED_DTU_ASSERT_FAILURE_COUNT = 1;
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   // Test default display with no snapshots
+   equal(getState().censusDisplay.breakdown.by, "coarseType",
+         "default coarseType display selected at start.");
+@@ -41,12 +41,12 @@ add_task(function* () {
+   } catch (e) {
+     ok(true, "Throws when passing in an invalid display object");
+   }
+   equal(getState().censusDisplay.breakdown.by, "allocationStack",
+     "current display unchanged when passing invalid display");
+ 
+   // Test new snapshots
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+   equal(getState().snapshots[0].census.display, censusDisplays.allocationStack,
+         "New snapshots use the current, non-default display");
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-take-census.js b/devtools/client/memory/test/unit/test_action-take-census.js
+--- a/devtools/client/memory/test/unit/test_action-take-census.js
++++ b/devtools/client/memory/test/unit/test_action-take-census.js
+@@ -10,46 +10,46 @@
+ var { snapshotState: states, censusDisplays, censusState, viewState } = require("devtools/client/memory/constants");
+ var actions = require("devtools/client/memory/actions/snapshot");
+ var { changeView } = require("devtools/client/memory/actions/view");
+ 
+ // This tests taking a census on a snapshot that is still being read, which
+ // triggers an assertion failure.
+ EXPECTED_DTU_ASSERT_FAILURE_COUNT = 1;
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+ 
+   store.dispatch(changeView(viewState.CENSUS));
+ 
+   store.dispatch(actions.takeSnapshot(front));
+-  yield waitUntilState(store, () => {
++  await waitUntilState(store, () => {
+     let snapshots = store.getState().snapshots;
+     return snapshots.length === 1 && snapshots[0].state === states.SAVED;
+   });
+ 
+   let snapshot = store.getState().snapshots[0];
+   equal(snapshot.census, null, "No census data exists yet on the snapshot.");
+ 
+   // Test error case of wrong state.
+   store.dispatch(actions.takeCensus(heapWorker, snapshot.id));
+-  yield waitUntilState(store, () => store.getState().errors.length === 1);
++  await waitUntilState(store, () => store.getState().errors.length === 1);
+ 
+   dumpn("Found error: " + store.getState().errors[0]);
+   ok(/Assertion failure/.test(store.getState().errors[0]),
+     "Error thrown when taking a census of a snapshot that has not been read.");
+ 
+   store.dispatch(actions.readSnapshot(heapWorker, snapshot.id));
+-  yield waitUntilState(store, () => store.getState().snapshots[0].state === states.READ);
++  await waitUntilState(store, () => store.getState().snapshots[0].state === states.READ);
+ 
+   store.dispatch(actions.takeCensus(heapWorker, snapshot.id));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+ 
+   snapshot = store.getState().snapshots[0];
+   ok(snapshot.census, "Snapshot has census after saved census");
+   ok(snapshot.census.report.children.length, "Census is in tree node form");
+   equal(snapshot.census.display, censusDisplays.coarseType,
+         "Snapshot stored correct display used for the census");
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-take-snapshot-and-census.js b/devtools/client/memory/test/unit/test_action-take-snapshot-and-census.js
+--- a/devtools/client/memory/test/unit/test_action-take-snapshot-and-census.js
++++ b/devtools/client/memory/test/unit/test_action-take-snapshot-and-census.js
+@@ -6,20 +6,20 @@
+ /**
+  * Tests the task creator `takeSnapshotAndCensus()` for the whole flow of
+  * taking a snapshot, and its sub-actions.
+  */
+ 
+ let { snapshotState: states, treeMapState } = require("devtools/client/memory/constants");
+ let actions = require("devtools/client/memory/actions/snapshot");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+ 
+   let snapshotI = 0;
+   let censusI = 0;
+   let snapshotStates = ["SAVING", "SAVED", "READING", "READ"];
+   let censusStates = ["SAVING", "SAVED"];
+   let expectStates = () => {
+     let snapshot = store.getState().snapshots[0];
+@@ -39,17 +39,17 @@ add_task(function* () {
+         censusI++;
+       }
+     }
+   };
+ 
+   let unsubscribe = store.subscribe(expectStates);
+   store.dispatch(actions.takeSnapshotAndCensus(front, heapWorker));
+ 
+-  yield waitUntilState(store, () => {
++  await waitUntilState(store, () => {
+     return snapshotI === snapshotStates.length &&
+            censusI === censusStates.length;
+   });
+   unsubscribe();
+ 
+   ok(true,
+     "takeSnapshotAndCensus() produces the correct sequence of states in a snapshot");
+   let snapshot = store.getState().snapshots[0];
+diff --git a/devtools/client/memory/test/unit/test_action-take-snapshot.js b/devtools/client/memory/test/unit/test_action-take-snapshot.js
+--- a/devtools/client/memory/test/unit/test_action-take-snapshot.js
++++ b/devtools/client/memory/test/unit/test_action-take-snapshot.js
+@@ -5,19 +5,19 @@
+ 
+ /**
+  * Tests the async reducer responding to the action `takeSnapshot(front)`
+  */
+ 
+ let actions = require("devtools/client/memory/actions/snapshot");
+ let { snapshotState: states } = require("devtools/client/memory/constants");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+ 
+   let unsubscribe = store.subscribe(checkState);
+ 
+   let foundPendingState = false;
+   let foundDoneState = false;
+ 
+   function checkState() {
+@@ -36,16 +36,16 @@ add_task(function* () {
+       ok(lastSnapshot.path, "Snapshot fetched with a path");
+       ok(snapshots.every(s => s.selected === (s.id === lastSnapshot.id)),
+         "Only recent snapshot is selected");
+     }
+   }
+ 
+   for (let i = 0; i < 4; i++) {
+     store.dispatch(actions.takeSnapshot(front));
+-    yield waitUntilState(store, () => foundPendingState && foundDoneState);
++    await waitUntilState(store, () => foundPendingState && foundDoneState);
+ 
+     // reset state trackers
+     foundDoneState = foundPendingState = false;
+   }
+ 
+   unsubscribe();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-01.js b/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-01.js
+--- a/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-01.js
++++ b/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-01.js
+@@ -15,61 +15,61 @@ const {
+   setCensusDisplayAndRefresh
+ } = require("devtools/client/memory/actions/census-display");
+ const {
+   takeSnapshotAndCensus,
+   selectSnapshotAndRefresh,
+ } = require("devtools/client/memory/actions/snapshot");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   // Select a non-inverted display.
+   dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
+   equal(getState().censusDisplay.inverted, false, "not inverted by default");
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+ 
+-  yield waitUntilCensusState(store, s => s.census,
++  await waitUntilCensusState(store, s => s.census,
+                              [censusState.SAVED, censusState.SAVED, censusState.SAVED]);
+   ok(true, "saved 3 snapshots and took a census of each of them");
+ 
+   // Select an inverted display.
+   dispatch(setCensusDisplayAndRefresh(heapWorker,
+                                       censusDisplays.invertedAllocationStack));
+ 
+-  yield waitUntilCensusState(store, s => s.census,
++  await waitUntilCensusState(store, s => s.census,
+                              [censusState.SAVED, censusState.SAVED, censusState.SAVING]);
+   ok(true, "toggling inverted should recompute the selected snapshot's census");
+ 
+   equal(getState().censusDisplay.inverted, true, "now inverted");
+ 
+-  yield waitUntilCensusState(store, s => s.census,
++  await waitUntilCensusState(store, s => s.census,
+                              [censusState.SAVED, censusState.SAVED, censusState.SAVED]);
+ 
+   equal(getState().snapshots[0].census.display.inverted, false);
+   equal(getState().snapshots[1].census.display.inverted, false);
+   equal(getState().snapshots[2].census.display.inverted, true);
+ 
+   dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id));
+-  yield waitUntilCensusState(store, s => s.census,
++  await waitUntilCensusState(store, s => s.census,
+                              [censusState.SAVED, censusState.SAVING, censusState.SAVED]);
+   ok(true, "selecting non-inverted census should trigger a recompute");
+ 
+-  yield waitUntilCensusState(store, s => s.census,
++  await waitUntilCensusState(store, s => s.census,
+                              [censusState.SAVED, censusState.SAVED, censusState.SAVED]);
+ 
+   equal(getState().snapshots[0].census.display.inverted, false);
+   equal(getState().snapshots[1].census.display.inverted, true);
+   equal(getState().snapshots[2].census.display.inverted, true);
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-02.js b/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-02.js
+--- a/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-02.js
++++ b/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-02.js
+@@ -9,49 +9,49 @@
+ const { censusDisplays, snapshotState: states, censusState, viewState } = require("devtools/client/memory/constants");
+ const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
+ const {
+   setCensusDisplay,
+   setCensusDisplayAndRefresh,
+ } = require("devtools/client/memory/actions/census-display");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   dispatch(setCensusDisplay(censusDisplays.allocationStack));
+   equal(getState().censusDisplay.inverted, false,
+         "Should not have an inverted census display");
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilSnapshotState(store, [states.SAVING]);
++  await waitUntilSnapshotState(store, [states.SAVING]);
+ 
+   dispatch(setCensusDisplayAndRefresh(heapWorker,
+                                       censusDisplays.invertedAllocationStack));
+ 
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+ 
+   ok(getState().censusDisplay.inverted,
+      "should want inverted trees");
+   ok(getState().snapshots[0].census.display.inverted,
+      "snapshot-we-were-in-the-middle-of-saving's census should be inverted");
+ 
+   dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
+   ok(true, "toggling inverted retriggers census");
+   ok(!getState().censusDisplay.inverted, "no longer inverted");
+ 
+   dispatch(setCensusDisplayAndRefresh(heapWorker,
+                                       censusDisplays.invertedAllocationStack));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+   ok(getState().censusDisplay.inverted, "inverted again");
+   ok(getState().snapshots[0].census.display.inverted,
+      "census-we-were-in-the-middle-of-recomputing should be inverted again");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action-toggle-recording-allocations.js b/devtools/client/memory/test/unit/test_action-toggle-recording-allocations.js
+--- a/devtools/client/memory/test/unit/test_action-toggle-recording-allocations.js
++++ b/devtools/client/memory/test/unit/test_action-toggle-recording-allocations.js
+@@ -4,36 +4,36 @@
+ "use strict";
+ 
+ /**
+  * Test toggling the recording of allocation stacks.
+  */
+ 
+ let { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   equal(getState().allocations.recording, false, "not recording by default");
+   equal(getState().allocations.togglingInProgress, false,
+         "not in the process of toggling by default");
+ 
+   dispatch(toggleRecordingAllocationStacks(front));
+-  yield waitUntilState(store, () => getState().allocations.togglingInProgress);
++  await waitUntilState(store, () => getState().allocations.togglingInProgress);
+   ok(true, "`togglingInProgress` set to true when toggling on");
+-  yield waitUntilState(store, () => !getState().allocations.togglingInProgress);
++  await waitUntilState(store, () => !getState().allocations.togglingInProgress);
+ 
+   equal(getState().allocations.recording, true, "now we are recording");
+   ok(front.recordingAllocations, "front is recording too");
+ 
+   dispatch(toggleRecordingAllocationStacks(front));
+-  yield waitUntilState(store, () => getState().allocations.togglingInProgress);
++  await waitUntilState(store, () => getState().allocations.togglingInProgress);
+   ok(true, "`togglingInProgress` set to true when toggling off");
+-  yield waitUntilState(store, () => !getState().allocations.togglingInProgress);
++  await waitUntilState(store, () => !getState().allocations.togglingInProgress);
+ 
+   equal(getState().allocations.recording, false, "now we are not recording");
+   ok(front.recordingAllocations, "front is not recording anymore");
+ 
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action_diffing_01.js b/devtools/client/memory/test/unit/test_action_diffing_01.js
+--- a/devtools/client/memory/test/unit/test_action_diffing_01.js
++++ b/devtools/client/memory/test/unit/test_action_diffing_01.js
+@@ -2,26 +2,26 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // Test toggling of diffing.
+ 
+ const { toggleDiffing } = require("devtools/client/memory/actions/diffing");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   equal(getState().diffing, null, "not diffing by default");
+ 
+   dispatch(toggleDiffing());
+   ok(getState().diffing, "now diffing after toggling");
+ 
+   dispatch(toggleDiffing());
+   equal(getState().diffing, null, "not diffing again after toggling again");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action_diffing_02.js b/devtools/client/memory/test/unit/test_action_diffing_02.js
+--- a/devtools/client/memory/test/unit/test_action_diffing_02.js
++++ b/devtools/client/memory/test/unit/test_action_diffing_02.js
+@@ -5,39 +5,39 @@
+ 
+ // Test that toggling diffing unselects all snapshots.
+ 
+ const { censusState, viewState } = require("devtools/client/memory/constants");
+ const { toggleDiffing } = require("devtools/client/memory/actions/diffing");
+ const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   equal(getState().diffing, null, "not diffing by default");
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.census,
++  await waitUntilCensusState(store, s => s.census,
+     [censusState.SAVED, censusState.SAVED, censusState.SAVED]);
+ 
+   ok(getState().snapshots.some(s => s.selected),
+      "One of the new snapshots is selected");
+ 
+   dispatch(toggleDiffing());
+   ok(getState().diffing, "now diffing after toggling");
+ 
+   for (let s of getState().snapshots) {
+     ok(!s.selected,
+        "No snapshot should be selected after entering diffing mode");
+   }
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action_diffing_03.js b/devtools/client/memory/test/unit/test_action_diffing_03.js
+--- a/devtools/client/memory/test/unit/test_action_diffing_03.js
++++ b/devtools/client/memory/test/unit/test_action_diffing_03.js
+@@ -13,31 +13,31 @@ const {
+ const { takeSnapshot } = require("devtools/client/memory/actions/snapshot");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+ // We test that you (1) cannot select a snapshot that is not in a diffable
+ // state, and (2) cannot select more than 2 snapshots for diffing. Both attempts
+ // trigger assertion failures.
+ EXPECTED_DTU_ASSERT_FAILURE_COUNT = 2;
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+   equal(getState().diffing, null, "not diffing by default");
+ 
+   dispatch(takeSnapshot(front, heapWorker));
+   dispatch(takeSnapshot(front, heapWorker));
+   dispatch(takeSnapshot(front, heapWorker));
+ 
+-  yield waitUntilSnapshotState(store,
++  await waitUntilSnapshotState(store,
+         [snapshotState.SAVED, snapshotState.SAVED, snapshotState.SAVED]);
+   dispatch(takeSnapshot(front));
+ 
+   // Start diffing.
+   dispatch(toggleDiffing());
+   ok(getState().diffing, "now diffing after toggling");
+   equal(getState().diffing.firstSnapshotId, null,
+         "no first snapshot selected");
+@@ -92,10 +92,10 @@ add_task(function* () {
+   try {
+     dispatch(selectSnapshotForDiffing(getState().snapshots[2]));
+   } catch (error) {
+     threw = true;
+   }
+   ok(threw, "Can't select more than two snapshots for diffing");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action_diffing_04.js b/devtools/client/memory/test/unit/test_action_diffing_04.js
+--- a/devtools/client/memory/test/unit/test_action_diffing_04.js
++++ b/devtools/client/memory/test/unit/test_action_diffing_04.js
+@@ -15,61 +15,61 @@ const {
+   selectSnapshotForDiffingAndRefresh
+ } = require("devtools/client/memory/actions/diffing");
+ const {
+   takeSnapshot,
+   readSnapshot
+ } = require("devtools/client/memory/actions/snapshot");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+   dispatch(changeView(viewState.CENSUS));
+ 
+   equal(getState().diffing, null, "not diffing by default");
+ 
+-  const s1 = yield dispatch(takeSnapshot(front, heapWorker));
+-  const s2 = yield dispatch(takeSnapshot(front, heapWorker));
+-  const s3 = yield dispatch(takeSnapshot(front, heapWorker));
++  const s1 = await dispatch(takeSnapshot(front, heapWorker));
++  const s2 = await dispatch(takeSnapshot(front, heapWorker));
++  const s3 = await dispatch(takeSnapshot(front, heapWorker));
+   dispatch(readSnapshot(heapWorker, s1));
+   dispatch(readSnapshot(heapWorker, s2));
+   dispatch(readSnapshot(heapWorker, s3));
+-  yield waitUntilSnapshotState(store,
++  await waitUntilSnapshotState(store,
+     [snapshotState.READ, snapshotState.READ, snapshotState.READ]);
+ 
+   dispatch(toggleDiffing());
+   dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
+                                               getState().snapshots[0]));
+   dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
+                                               getState().snapshots[1]));
+ 
+   ok(getState().diffing, "We should be diffing.");
+   equal(getState().diffing.firstSnapshotId, getState().snapshots[0].id,
+         "First snapshot selected.");
+   equal(getState().diffing.secondSnapshotId, getState().snapshots[1].id,
+         "Second snapshot selected.");
+ 
+-  yield waitUntilState(store,
++  await waitUntilState(store,
+                        state =>
+                          state.diffing.state === diffingState.TAKING_DIFF);
+   ok(true,
+      "Selecting two snapshots for diffing should trigger computing a diff.");
+ 
+-  yield waitUntilState(store,
++  await waitUntilState(store,
+                        state => state.diffing.state === diffingState.TOOK_DIFF);
+   ok(true, "And then the diff should complete.");
+   ok(getState().diffing.census, "And we should have a census.");
+   ok(getState().diffing.census.report, "And that census should have a report.");
+   equal(getState().diffing.census.display, getState().censusDisplay,
+         "And that census should have the correct display");
+   equal(getState().diffing.census.filter, getState().filter,
+         "And that census should have the correct filter");
+   equal(getState().diffing.census.display.inverted,
+         getState().censusDisplay.inverted,
+         "And that census should have the correct inversion");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_action_diffing_05.js b/devtools/client/memory/test/unit/test_action_diffing_05.js
+--- a/devtools/client/memory/test/unit/test_action_diffing_05.js
++++ b/devtools/client/memory/test/unit/test_action_diffing_05.js
+@@ -22,46 +22,46 @@ const {
+   setFilterStringAndRefresh,
+ } = require("devtools/client/memory/actions/filter");
+ const {
+   takeSnapshot,
+   readSnapshot,
+ } = require("devtools/client/memory/actions/snapshot");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+   dispatch(changeView(viewState.CENSUS));
+ 
+-  yield dispatch(setCensusDisplayAndRefresh(heapWorker,
++  await dispatch(setCensusDisplayAndRefresh(heapWorker,
+                                         censusDisplays.allocationStack));
+   equal(getState().censusDisplay.inverted, false,
+         "not inverted at start");
+ 
+   equal(getState().diffing, null, "not diffing by default");
+ 
+-  const s1 = yield dispatch(takeSnapshot(front, heapWorker));
+-  const s2 = yield dispatch(takeSnapshot(front, heapWorker));
+-  const s3 = yield dispatch(takeSnapshot(front, heapWorker));
++  const s1 = await dispatch(takeSnapshot(front, heapWorker));
++  const s2 = await dispatch(takeSnapshot(front, heapWorker));
++  const s3 = await dispatch(takeSnapshot(front, heapWorker));
+   dispatch(readSnapshot(heapWorker, s1));
+   dispatch(readSnapshot(heapWorker, s2));
+   dispatch(readSnapshot(heapWorker, s3));
+-  yield waitUntilSnapshotState(store,
++  await waitUntilSnapshotState(store,
+     [snapshotState.READ, snapshotState.READ, snapshotState.READ]);
+ 
+-  yield dispatch(toggleDiffing());
++  await dispatch(toggleDiffing());
+   dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
+                                               getState().snapshots[0]));
+   dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
+                                               getState().snapshots[1]));
+-  yield waitUntilState(store,
++  await waitUntilState(store,
+                        state => state.diffing.state === diffingState.TOOK_DIFF);
+ 
+   const shouldTriggerRecompute = [
+     {
+       name: "toggling inversion",
+       func: () => dispatch(setCensusDisplayAndRefresh(
+         heapWorker,
+         censusDisplays.invertedAllocationStack))
+@@ -77,22 +77,22 @@ add_task(function* () {
+                                             censusDisplays.coarseType))
+     }
+   ];
+ 
+   for (let { name, func } of shouldTriggerRecompute) {
+     dumpn(`Testing that "${name}" triggers a diff recompute`);
+     func();
+ 
+-    yield waitUntilState(store,
++    await waitUntilState(store,
+                          state =>
+                            state.diffing.state === diffingState.TAKING_DIFF);
+     ok(true, "triggered diff recompute.");
+ 
+-    yield waitUntilState(store,
++    await waitUntilState(store,
+                          state =>
+                            state.diffing.state === diffingState.TOOK_DIFF);
+     ok(true, "And then the diff should complete.");
+     ok(getState().diffing.census, "And we should have a census.");
+     ok(getState().diffing.census.report,
+        "And that census should have a report.");
+     equal(getState().diffing.census.display,
+           getState().censusDisplay,
+@@ -100,10 +100,10 @@ add_task(function* () {
+     equal(getState().diffing.census.filter, getState().filter,
+           "And that census should have the correct filter");
+     equal(getState().diffing.census.display.inverted,
+           getState().censusDisplay.inverted,
+           "And that census should have the correct inversion");
+   }
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_dominator_trees_01.js b/devtools/client/memory/test/unit/test_dominator_trees_01.js
+--- a/devtools/client/memory/test/unit/test_dominator_trees_01.js
++++ b/devtools/client/memory/test/unit/test_dominator_trees_01.js
+@@ -9,50 +9,50 @@ let {
+   dominatorTreeState,
+   treeMapState,
+ } = require("devtools/client/memory/constants");
+ let {
+   takeSnapshotAndCensus,
+   computeAndFetchDominatorTree,
+ } = require("devtools/client/memory/actions/snapshot");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
++  await waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
+   ok(!getState().snapshots[0].dominatorTree,
+      "There shouldn't be a dominator tree model yet since it is not computed " +
+      "until we switch to the dominators view.");
+ 
+   // Change to the dominator tree view.
+   dispatch(computeAndFetchDominatorTree(heapWorker, getState().snapshots[0].id));
+   ok(getState().snapshots[0].dominatorTree,
+      "Should now have a dominator tree model for the selected snapshot");
+ 
+   // Wait for the dominator tree to start being computed.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.COMPUTING);
+   ok(true, "The dominator tree started computing");
+   ok(!getState().snapshots[0].dominatorTree.root,
+      "When the dominator tree is computing, we should not have its root");
+ 
+   // Wait for the dominator tree to finish computing and start being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
+   ok(true, "The dominator tree started fetching");
+   ok(!getState().snapshots[0].dominatorTree.root,
+      "When the dominator tree is fetching, we should not have its root");
+ 
+   // Wait for the dominator tree to finish being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(true, "The dominator tree was fetched");
+   ok(getState().snapshots[0].dominatorTree.root,
+      "When the dominator tree is loaded, we should have its root");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_dominator_trees_02.js b/devtools/client/memory/test/unit/test_dominator_trees_02.js
+--- a/devtools/client/memory/test/unit/test_dominator_trees_02.js
++++ b/devtools/client/memory/test/unit/test_dominator_trees_02.js
+@@ -13,49 +13,49 @@ const {
+ } = require("devtools/client/memory/constants");
+ const {
+   takeSnapshotAndCensus,
+ } = require("devtools/client/memory/actions/snapshot");
+ const {
+   changeViewAndRefresh
+ } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
++  await waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
+   ok(!getState().snapshots[0].dominatorTree,
+      "There shouldn't be a dominator tree model yet since it is not computed " +
+      "until we switch to the dominators view.");
+ 
+   dispatch(changeViewAndRefresh(viewState.DOMINATOR_TREE, heapWorker));
+   ok(getState().snapshots[0].dominatorTree,
+      "Should now have a dominator tree model for the selected snapshot");
+ 
+   // Wait for the dominator tree to start being computed.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.COMPUTING);
+   ok(true, "The dominator tree started computing");
+   ok(!getState().snapshots[0].dominatorTree.root,
+      "When the dominator tree is computing, we should not have its root");
+ 
+   // Wait for the dominator tree to finish computing and start being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
+   ok(true, "The dominator tree started fetching");
+   ok(!getState().snapshots[0].dominatorTree.root,
+      "When the dominator tree is fetching, we should not have its root");
+ 
+   // Wait for the dominator tree to finish being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(true, "The dominator tree was fetched");
+   ok(getState().snapshots[0].dominatorTree.root,
+      "When the dominator tree is loaded, we should have its root");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_dominator_trees_03.js b/devtools/client/memory/test/unit/test_dominator_trees_03.js
+--- a/devtools/client/memory/test/unit/test_dominator_trees_03.js
++++ b/devtools/client/memory/test/unit/test_dominator_trees_03.js
+@@ -12,47 +12,47 @@ const {
+ } = require("devtools/client/memory/constants");
+ const {
+   takeSnapshotAndCensus,
+ } = require("devtools/client/memory/actions/snapshot");
+ const {
+   changeView
+ } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.DOMINATOR_TREE));
+   equal(getState().view.state, viewState.DOMINATOR_TREE,
+         "We should now be in the DOMINATOR_TREE view");
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+ 
+   // Wait for the dominator tree to start being computed.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0] && state.snapshots[0].dominatorTree);
+   equal(getState().snapshots[0].dominatorTree.state,
+         dominatorTreeState.COMPUTING,
+         "The dominator tree started computing");
+   ok(!getState().snapshots[0].dominatorTree.root,
+      "When the dominator tree is computing, we should not have its root");
+ 
+   // Wait for the dominator tree to finish computing and start being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
+   ok(true, "The dominator tree started fetching");
+   ok(!getState().snapshots[0].dominatorTree.root,
+      "When the dominator tree is fetching, we should not have its root");
+ 
+   // Wait for the dominator tree to finish being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(true, "The dominator tree was fetched");
+   ok(getState().snapshots[0].dominatorTree.root,
+      "When the dominator tree is loaded, we should have its root");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_dominator_trees_04.js b/devtools/client/memory/test/unit/test_dominator_trees_04.js
+--- a/devtools/client/memory/test/unit/test_dominator_trees_04.js
++++ b/devtools/client/memory/test/unit/test_dominator_trees_04.js
+@@ -13,54 +13,54 @@ const {
+ } = require("devtools/client/memory/constants");
+ const {
+   takeSnapshotAndCensus,
+ } = require("devtools/client/memory/actions/snapshot");
+ const {
+   changeView
+ } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+ 
+   for (let intermediateSnapshotState of [states.SAVING, states.READING, states.READ]) {
+     dumpn("Testing switching to the DOMINATOR_TREE view in the middle of the " +
+           `${intermediateSnapshotState} snapshot state`);
+ 
+     let store = Store();
+     let { getState, dispatch } = store;
+ 
+     dispatch(takeSnapshotAndCensus(front, heapWorker));
+-    yield waitUntilSnapshotState(store, [intermediateSnapshotState]);
++    await waitUntilSnapshotState(store, [intermediateSnapshotState]);
+ 
+     dispatch(changeView(viewState.DOMINATOR_TREE));
+     equal(getState().view.state, viewState.DOMINATOR_TREE,
+           "We should now be in the DOMINATOR_TREE view");
+ 
+     // Wait for the dominator tree to start being computed.
+-    yield waitUntilState(store, state =>
++    await waitUntilState(store, state =>
+       state.snapshots[0] && state.snapshots[0].dominatorTree);
+     equal(getState().snapshots[0].dominatorTree.state,
+           dominatorTreeState.COMPUTING,
+           "The dominator tree started computing");
+     ok(!getState().snapshots[0].dominatorTree.root,
+        "When the dominator tree is computing, we should not have its root");
+ 
+     // Wait for the dominator tree to finish computing and start being fetched.
+-    yield waitUntilState(store, state =>
++    await waitUntilState(store, state =>
+       state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
+     ok(true, "The dominator tree started fetching");
+     ok(!getState().snapshots[0].dominatorTree.root,
+        "When the dominator tree is fetching, we should not have its root");
+ 
+     // Wait for the dominator tree to finish being fetched.
+-    yield waitUntilState(store, state =>
++    await waitUntilState(store, state =>
+       state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+     ok(true, "The dominator tree was fetched");
+     ok(getState().snapshots[0].dominatorTree.root,
+        "When the dominator tree is loaded, we should have its root");
+   }
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_dominator_trees_05.js b/devtools/client/memory/test/unit/test_dominator_trees_05.js
+--- a/devtools/client/memory/test/unit/test_dominator_trees_05.js
++++ b/devtools/client/memory/test/unit/test_dominator_trees_05.js
+@@ -13,44 +13,44 @@ let {
+ } = require("devtools/client/memory/constants");
+ let {
+   takeSnapshotAndCensus,
+   selectSnapshotAndRefresh,
+ } = require("devtools/client/memory/actions/snapshot");
+ 
+ let { changeView } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.treeMap,
++  await waitUntilCensusState(store, s => s.treeMap,
+                              [treeMapState.SAVED, treeMapState.SAVED]);
+ 
+   ok(getState().snapshots[1].selected, "The second snapshot is selected");
+ 
+   // Change to the dominator tree view.
+   dispatch(changeView(viewState.DOMINATOR_TREE));
+ 
+   // Wait for the dominator tree to finish being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[1].dominatorTree &&
+     state.snapshots[1].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(true, "The second snapshot's dominator tree was fetched");
+ 
+   // Select the first snapshot.
+   dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[0].id));
+ 
+   // And now the first snapshot should have its dominator tree fetched and
+   // computed because of the new selection.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree &&
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(true, "The first snapshot's dominator tree was fetched");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_dominator_trees_06.js b/devtools/client/memory/test/unit/test_dominator_trees_06.js
+--- a/devtools/client/memory/test/unit/test_dominator_trees_06.js
++++ b/devtools/client/memory/test/unit/test_dominator_trees_06.js
+@@ -13,28 +13,28 @@ const {
+   takeSnapshotAndCensus,
+   fetchImmediatelyDominated,
+ } = require("devtools/client/memory/actions/snapshot");
+ const DominatorTreeLazyChildren
+   = require("devtools/client/memory/dominator-tree-lazy-children");
+ 
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.DOMINATOR_TREE));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+ 
+   // Wait for the dominator tree to finish being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0] &&
+     state.snapshots[0].dominatorTree &&
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(getState().snapshots[0].dominatorTree.root,
+      "The dominator tree was fetched");
+ 
+   // Find a node that has children, but none of them are loaded.
+ 
+@@ -67,17 +67,17 @@ add_task(function* () {
+   dispatch(fetchImmediatelyDominated(heapWorker, getState().snapshots[0].id,
+                                      lazyChildren));
+ 
+   equal(getState().snapshots[0].dominatorTree.state,
+         dominatorTreeState.INCREMENTAL_FETCHING,
+         "Fetching immediately dominated children should put us in the " +
+         "INCREMENTAL_FETCHING state");
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(true,
+      "The dominator tree should go back to LOADED after the incremental " +
+      "fetching is done.");
+ 
+   const newRoot = getState().snapshots[0].dominatorTree.root;
+   ok(oldRoot !== newRoot,
+      "When we insert new nodes, we get a new tree");
+@@ -116,10 +116,10 @@ add_task(function* () {
+ 
+   const newNode = findNewNode(newRoot);
+   ok(newNode, "Should find the node in the new tree again");
+   ok(newNode !== oldNode,
+     "We did not mutate the old node in place, instead created a new node");
+   ok(newNode.children, "And the new node should have the children attached");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_dominator_trees_07.js b/devtools/client/memory/test/unit/test_dominator_trees_07.js
+--- a/devtools/client/memory/test/unit/test_dominator_trees_07.js
++++ b/devtools/client/memory/test/unit/test_dominator_trees_07.js
+@@ -14,28 +14,28 @@ const {
+   takeSnapshotAndCensus,
+   fetchImmediatelyDominated,
+ } = require("devtools/client/memory/actions/snapshot");
+ const DominatorTreeLazyChildren
+   = require("devtools/client/memory/dominator-tree-lazy-children");
+ 
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.DOMINATOR_TREE));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+ 
+   // Wait for the dominator tree to finish being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0] &&
+     state.snapshots[0].dominatorTree &&
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(getState().snapshots[0].dominatorTree.root,
+      "The dominator tree was fetched");
+ 
+   // Find a node that has more children.
+ 
+@@ -89,17 +89,17 @@ add_task(function* () {
+   dispatch(fetchImmediatelyDominated(heapWorker, getState().snapshots[0].id,
+                                      new DominatorTreeLazyChildren(oldNode2.nodeId, 0)));
+ 
+   equal(getState().snapshots[0].dominatorTree.state,
+         dominatorTreeState.INCREMENTAL_FETCHING,
+         "Fetching immediately dominated children should put us in the " +
+         "INCREMENTAL_FETCHING state");
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(true,
+      "The dominator tree should go back to LOADED after the incremental " +
+      "fetching is done.");
+ 
+   const newRoot = getState().snapshots[0].dominatorTree.root;
+   ok(oldRoot !== newRoot,
+      "When we insert new nodes, we get a new tree");
+@@ -133,10 +133,10 @@ add_task(function* () {
+   const newNode2 = findNodeWithId(oldNode2.nodeId, newRoot);
+   ok(newNode2, "Should find the second node in the new tree again");
+   ok(newNode2 !== oldNode2,
+      "We did not mutate the second old node in place, instead created a new node");
+   ok(newNode2.children,
+      "And the new node should have the new children attached");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_dominator_trees_08.js b/devtools/client/memory/test/unit/test_dominator_trees_08.js
+--- a/devtools/client/memory/test/unit/test_dominator_trees_08.js
++++ b/devtools/client/memory/test/unit/test_dominator_trees_08.js
+@@ -17,33 +17,33 @@ const {
+ } = require("devtools/client/memory/actions/label-display");
+ const {
+   changeView,
+ } = require("devtools/client/memory/actions/view");
+ const {
+   takeSnapshotAndCensus,
+ } = require("devtools/client/memory/actions/snapshot");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.DOMINATOR_TREE));
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
++  await waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
+   ok(!getState().snapshots[0].dominatorTree,
+      "There shouldn't be a dominator tree model yet since it is not computed " +
+      "until we switch to the dominators view.");
+ 
+   // Wait for the dominator tree to finish being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0] &&
+     state.snapshots[0].dominatorTree &&
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+ 
+   ok(getState().labelDisplay,
+      "We have a default display for describing nodes in a dominator tree");
+   equal(getState().labelDisplay,
+         labelDisplays.coarseType,
+@@ -52,26 +52,26 @@ add_task(function* () {
+         getState().snapshots[0].dominatorTree.display,
+         "and the newly computed dominator tree has that display");
+ 
+   // Switch to the allocationStack display.
+   dispatch(setLabelDisplayAndRefresh(
+     heapWorker,
+     labelDisplays.allocationStack));
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
+   ok(true,
+      "switching display types caused the dominator tree to be fetched " +
+      "again.");
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   equal(getState().snapshots[0].dominatorTree.display,
+         labelDisplays.allocationStack,
+         "The new dominator tree's display is allocationStack");
+   equal(getState().labelDisplay,
+         labelDisplays.allocationStack,
+         "as is our requested dominator tree display");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_dominator_trees_09.js b/devtools/client/memory/test/unit/test_dominator_trees_09.js
+--- a/devtools/client/memory/test/unit/test_dominator_trees_09.js
++++ b/devtools/client/memory/test/unit/test_dominator_trees_09.js
+@@ -17,33 +17,33 @@ const {
+ } = require("devtools/client/memory/actions/label-display");
+ const {
+   changeView,
+ } = require("devtools/client/memory/actions/view");
+ const {
+   takeSnapshotAndCensus,
+ } = require("devtools/client/memory/actions/snapshot");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.DOMINATOR_TREE));
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
++  await waitUntilCensusState(store, s => s.treeMap, [treeMapState.SAVED]);
+   ok(!getState().snapshots[0].dominatorTree,
+      "There shouldn't be a dominator tree model yet since it is not computed " +
+      "until we switch to the dominators view.");
+ 
+   // Wait for the dominator tree to start fetching.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0] &&
+     state.snapshots[0].dominatorTree &&
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
+ 
+   ok(getState().labelDisplay,
+      "We have a default display for describing nodes in a dominator tree");
+   equal(getState().labelDisplay,
+         labelDisplays.coarseType,
+@@ -54,21 +54,21 @@ add_task(function* () {
+ 
+   // Switch to the allocationStack display while we are still fetching the
+   // dominator tree.
+   dispatch(setLabelDisplayAndRefresh(
+     heapWorker,
+     labelDisplays.allocationStack));
+ 
+   // Wait for the dominator tree to finish being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+ 
+   equal(getState().snapshots[0].dominatorTree.display,
+         labelDisplays.allocationStack,
+         "The new dominator tree's display is allocationStack");
+   equal(getState().labelDisplay,
+         labelDisplays.allocationStack,
+         "as is our requested dominator tree display");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_dominator_trees_10.js b/devtools/client/memory/test/unit/test_dominator_trees_10.js
+--- a/devtools/client/memory/test/unit/test_dominator_trees_10.js
++++ b/devtools/client/memory/test/unit/test_dominator_trees_10.js
+@@ -17,28 +17,28 @@ let {
+ } = require("devtools/client/memory/actions/snapshot");
+ const {
+   changeView,
+ } = require("devtools/client/memory/actions/view");
+ const {
+   setLabelDisplayAndRefresh,
+ } = require("devtools/client/memory/actions/label-display");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.DOMINATOR_TREE));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+ 
+   // Wait for the dominator tree to finish being fetched.
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0] &&
+     state.snapshots[0].dominatorTree &&
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(true, "The dominator tree was fetched");
+ 
+   const root = getState().snapshots[0].dominatorTree.root;
+   ok(root, "When the dominator tree is loaded, we should have its root");
+ 
+@@ -48,24 +48,24 @@ add_task(function* () {
+ 
+   equal(getState().labelDisplay, labelDisplays.coarseType,
+         "Using labelDisplays.coarseType by default");
+   dispatch(setLabelDisplayAndRefresh(heapWorker,
+                                              labelDisplays.allocationStack));
+   equal(getState().labelDisplay, labelDisplays.allocationStack,
+         "Using labelDisplays.allocationStack now");
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING);
+   ok(true, "We started re-fetching the dominator tree");
+ 
+-  yield waitUntilState(store, state =>
++  await waitUntilState(store, state =>
+     state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED);
+   ok(true, "The dominator tree was loaded again");
+ 
+   ok(getState().snapshots[0].dominatorTree.focused,
+      "Still have a focused node");
+   equal(getState().snapshots[0].dominatorTree.focused.nodeId, root.nodeId,
+         "Focused node is the same as before");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_individuals_01.js b/devtools/client/memory/test/unit/test_individuals_01.js
+--- a/devtools/client/memory/test/unit/test_individuals_01.js
++++ b/devtools/client/memory/test/unit/test_individuals_01.js
+@@ -19,29 +19,29 @@ const {
+ } = require("devtools/client/memory/actions/view");
+ 
+ const EXPECTED_INDIVIDUAL_STATES = [
+   individualsState.COMPUTING_DOMINATOR_TREE,
+   individualsState.FETCHING,
+   individualsState.FETCHED,
+ ];
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   equal(getState().individuals, null,
+         "no individuals state by default");
+ 
+   dispatch(changeView(viewState.CENSUS));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+ 
+   const root = getState().snapshots[0].census.report;
+   ok(root, "Should have a census");
+ 
+   const reportLeafIndex = findReportLeafIndex(root);
+   ok(reportLeafIndex, "Should get a reportLeafIndex");
+ 
+   const snapshotId = getState().snapshots[0].id;
+@@ -50,24 +50,24 @@ add_task(function* () {
+   const breakdown = getState().snapshots[0].census.display.breakdown;
+   ok(breakdown, "Should have a breakdown");
+ 
+   dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
+                             reportLeafIndex));
+ 
+   // Wait for each expected state.
+   for (let state of EXPECTED_INDIVIDUAL_STATES) {
+-    yield waitUntilState(store, s => {
++    await waitUntilState(store, s => {
+       return s.view.state === viewState.INDIVIDUALS &&
+              s.individuals &&
+              s.individuals.state === state;
+     });
+     ok(true, `Reached state = ${state}`);
+   }
+ 
+   ok(getState().individuals, "Should have individuals state");
+   ok(getState().individuals.nodes, "Should have individuals nodes");
+   ok(getState().individuals.nodes.length > 0,
+      "Should have a positive number of nodes");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_individuals_02.js b/devtools/client/memory/test/unit/test_individuals_02.js
+--- a/devtools/client/memory/test/unit/test_individuals_02.js
++++ b/devtools/client/memory/test/unit/test_individuals_02.js
+@@ -22,29 +22,29 @@ const {
+ } = require("devtools/client/memory/actions/view");
+ 
+ const EXPECTED_INDIVIDUAL_STATES = [
+   individualsState.COMPUTING_DOMINATOR_TREE,
+   individualsState.FETCHING,
+   individualsState.FETCHED,
+ ];
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   equal(getState().individuals, null,
+         "no individuals state by default");
+ 
+   dispatch(changeView(viewState.CENSUS));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+ 
+   const root = getState().snapshots[0].census.report;
+   ok(root, "Should have a census");
+ 
+   const reportLeafIndex = findReportLeafIndex(root);
+   ok(reportLeafIndex, "Should get a reportLeafIndex");
+ 
+   const snapshotId = getState().snapshots[0].id;
+@@ -62,24 +62,24 @@ add_task(function* () {
+ 
+   // Fetch individuals in the middle of computing the dominator tree.
+ 
+   dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
+                             reportLeafIndex));
+ 
+   // Wait for each expected state.
+   for (let state of EXPECTED_INDIVIDUAL_STATES) {
+-    yield waitUntilState(store, s => {
++    await waitUntilState(store, s => {
+       return s.view.state === viewState.INDIVIDUALS &&
+              s.individuals &&
+              s.individuals.state === state;
+     });
+     ok(true, `Reached state = ${state}`);
+   }
+ 
+   ok(getState().individuals, "Should have individuals state");
+   ok(getState().individuals.nodes, "Should have individuals nodes");
+   ok(getState().individuals.nodes.length > 0,
+      "Should have a positive number of nodes");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_individuals_03.js b/devtools/client/memory/test/unit/test_individuals_03.js
+--- a/devtools/client/memory/test/unit/test_individuals_03.js
++++ b/devtools/client/memory/test/unit/test_individuals_03.js
+@@ -24,37 +24,37 @@ const {
+ } = require("devtools/client/memory/actions/diffing");
+ 
+ const EXPECTED_INDIVIDUAL_STATES = [
+   individualsState.COMPUTING_DOMINATOR_TREE,
+   individualsState.FETCHING,
+   individualsState.FETCHED,
+ ];
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   // Take two snapshots and diff them from each other.
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.census,
++  await waitUntilCensusState(store, s => s.census,
+                              [censusState.SAVED, censusState.SAVED]);
+ 
+   dispatch(changeView(viewState.DIFFING));
+   dispatch(selectSnapshotForDiffingAndRefresh(heapWorker, getState().snapshots[0]));
+   dispatch(selectSnapshotForDiffingAndRefresh(heapWorker, getState().snapshots[1]));
+ 
+-  yield waitUntilState(store, state => {
++  await waitUntilState(store, state => {
+     return state.diffing &&
+            state.diffing.state === diffingState.TOOK_DIFF;
+   });
+   ok(getState().diffing.census);
+ 
+   // Fetch individuals.
+ 
+   const root = getState().diffing.census.report;
+@@ -68,36 +68,36 @@ add_task(function* () {
+ 
+   const breakdown = getState().censusDisplay.breakdown;
+   ok(breakdown, "Should have a breakdown");
+ 
+   dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
+                             reportLeafIndex));
+ 
+   for (let state of EXPECTED_INDIVIDUAL_STATES) {
+-    yield waitUntilState(store, s => {
++    await waitUntilState(store, s => {
+       return s.view.state === viewState.INDIVIDUALS &&
+              s.individuals &&
+              s.individuals.state === state;
+     });
+     ok(true, `Reached state = ${state}`);
+   }
+ 
+   ok(getState().individuals, "Should have individuals state");
+   ok(getState().individuals.nodes, "Should have individuals nodes");
+   ok(getState().individuals.nodes.length > 0,
+      "Should have a positive number of nodes");
+ 
+   // Pop the view back to the diffing.
+ 
+   dispatch(popViewAndRefresh(heapWorker));
+ 
+-  yield waitUntilState(store, state => {
++  await waitUntilState(store, state => {
+     return state.diffing &&
+       state.diffing.state === diffingState.TOOK_DIFF;
+   });
+ 
+   ok(getState().diffing.census.report,
+      "We have our census diff again after popping back to the last view");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_individuals_04.js b/devtools/client/memory/test/unit/test_individuals_04.js
+--- a/devtools/client/memory/test/unit/test_individuals_04.js
++++ b/devtools/client/memory/test/unit/test_individuals_04.js
+@@ -22,30 +22,30 @@ const {
+ } = require("devtools/client/memory/actions/filter");
+ 
+ const EXPECTED_INDIVIDUAL_STATES = [
+   individualsState.COMPUTING_DOMINATOR_TREE,
+   individualsState.FETCHING,
+   individualsState.FETCHED,
+ ];
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+   dispatch(setFilterString("Array"));
+ 
+   // Take a snapshot and wait for the census to finish.
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+ 
+   // Fetch individuals.
+ 
+   const root = getState().snapshots[0].census.report;
+   ok(root, "Should have a census");
+ 
+   const reportLeafIndex = findReportLeafIndex(root, "Array");
+   ok(reportLeafIndex, "Should get a reportLeafIndex for Array");
+@@ -55,17 +55,17 @@ add_task(function* () {
+ 
+   const breakdown = getState().censusDisplay.breakdown;
+   ok(breakdown, "Should have a breakdown");
+ 
+   dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
+                             reportLeafIndex));
+ 
+   for (let state of EXPECTED_INDIVIDUAL_STATES) {
+-    yield waitUntilState(store, s => {
++    await waitUntilState(store, s => {
+       return s.view.state === viewState.INDIVIDUALS &&
+              s.individuals &&
+              s.individuals.state === state;
+     });
+     ok(true, `Reached state = ${state}`);
+   }
+ 
+   ok(getState().individuals, "Should have individuals state");
+@@ -77,10 +77,10 @@ add_task(function* () {
+ 
+   for (let node of getState().individuals.nodes) {
+     dumpn("Checking node: " + node.label.join(" > "));
+     ok(node.label.find(part => part === "Array"),
+        "The node should be an Array node");
+   }
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_individuals_05.js b/devtools/client/memory/test/unit/test_individuals_05.js
+--- a/devtools/client/memory/test/unit/test_individuals_05.js
++++ b/devtools/client/memory/test/unit/test_individuals_05.js
+@@ -23,30 +23,30 @@ const {
+ } = require("devtools/client/memory/actions/census-display");
+ 
+ const EXPECTED_INDIVIDUAL_STATES = [
+   individualsState.COMPUTING_DOMINATOR_TREE,
+   individualsState.FETCHING,
+   individualsState.FETCHED,
+ ];
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+   dispatch(setCensusDisplay(censusDisplays.invertedAllocationStack));
+ 
+   // Take a snapshot and wait for the census to finish.
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+ 
+   // Fetch individuals.
+ 
+   const root = getState().snapshots[0].census.report;
+   ok(root, "Should have a census");
+ 
+   const reportLeafIndex = findReportLeafIndex(root, "noStack");
+   ok(reportLeafIndex, "Should get a reportLeafIndex for noStack");
+@@ -56,24 +56,24 @@ add_task(function* () {
+ 
+   const breakdown = getState().censusDisplay.breakdown;
+   ok(breakdown, "Should have a breakdown");
+ 
+   dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
+                             reportLeafIndex));
+ 
+   for (let state of EXPECTED_INDIVIDUAL_STATES) {
+-    yield waitUntilState(store, s => {
++    await waitUntilState(store, s => {
+       return s.view.state === viewState.INDIVIDUALS &&
+              s.individuals &&
+              s.individuals.state === state;
+     });
+     ok(true, `Reached state = ${state}`);
+   }
+ 
+   ok(getState().individuals, "Should have individuals state");
+   ok(getState().individuals.nodes, "Should have individuals nodes");
+   ok(getState().individuals.nodes.length > 0,
+      "Should have a positive number of nodes");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_individuals_06.js b/devtools/client/memory/test/unit/test_individuals_06.js
+--- a/devtools/client/memory/test/unit/test_individuals_06.js
++++ b/devtools/client/memory/test/unit/test_individuals_06.js
+@@ -21,29 +21,29 @@ const {
+ } = require("devtools/client/memory/actions/view");
+ 
+ const EXPECTED_INDIVIDUAL_STATES = [
+   individualsState.COMPUTING_DOMINATOR_TREE,
+   individualsState.FETCHING,
+   individualsState.FETCHED,
+ ];
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+   // Take a snapshot and wait for the census to finish.
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+ 
+   // Fetch individuals.
+ 
+   const root = getState().snapshots[0].census.report;
+   ok(root, "Should have a census");
+ 
+   const reportLeafIndex = findReportLeafIndex(root);
+   ok(reportLeafIndex, "Should get a reportLeafIndex");
+@@ -53,17 +53,17 @@ add_task(function* () {
+ 
+   const breakdown = getState().censusDisplay.breakdown;
+   ok(breakdown, "Should have a breakdown");
+ 
+   dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
+                             reportLeafIndex));
+ 
+   for (let state of EXPECTED_INDIVIDUAL_STATES) {
+-    yield waitUntilState(store, s => {
++    await waitUntilState(store, s => {
+       return s.view.state === viewState.INDIVIDUALS &&
+              s.individuals &&
+              s.individuals.state === state;
+     });
+     ok(true, `Reached state = ${state}`);
+   }
+ 
+   ok(getState().individuals, "Should have individuals state");
+@@ -72,10 +72,10 @@ add_task(function* () {
+      "Should have a positive number of nodes");
+ 
+   dispatch(clearSnapshots(heapWorker));
+ 
+   equal(getState().view.state, viewState.CENSUS,
+         "Went back to census view");
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_pop_view_01.js b/devtools/client/memory/test/unit/test_pop_view_01.js
+--- a/devtools/client/memory/test/unit/test_pop_view_01.js
++++ b/devtools/client/memory/test/unit/test_pop_view_01.js
+@@ -20,29 +20,29 @@ const {
+ } = require("devtools/client/memory/actions/view");
+ 
+ const TEST_STATES = [
+   individualsState.COMPUTING_DOMINATOR_TREE,
+   individualsState.FETCHING,
+   individualsState.FETCHED,
+ ];
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   const { getState, dispatch } = store;
+ 
+   equal(getState().individuals, null,
+         "no individuals state by default");
+ 
+   dispatch(changeView(viewState.CENSUS));
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+ 
+   const root = getState().snapshots[0].census.report;
+   ok(root, "Should have a census");
+ 
+   const reportLeafIndex = findReportLeafIndex(root);
+   ok(reportLeafIndex, "Should get a reportLeafIndex");
+ 
+   const snapshotId = getState().snapshots[0].id;
+@@ -53,26 +53,26 @@ add_task(function* () {
+ 
+   for (let state of TEST_STATES) {
+     dumpn(`Testing popping back to the old view from state = ${state}`);
+ 
+     dispatch(fetchIndividuals(heapWorker, snapshotId, breakdown,
+                               reportLeafIndex));
+ 
+     // Wait for the expected test state.
+-    yield waitUntilState(store, s => {
++    await waitUntilState(store, s => {
+       return s.view.state === viewState.INDIVIDUALS &&
+              s.individuals &&
+              s.individuals.state === state;
+     });
+     ok(true, `Reached state = ${state}`);
+ 
+     // Pop back to the CENSUS state.
+     dispatch(popViewAndRefresh(heapWorker));
+-    yield waitUntilState(store, s => {
++    await waitUntilState(store, s => {
+       return s.view.state === viewState.CENSUS;
+     });
+     ok(!getState().individuals, "Should no longer have individuals");
+   }
+ 
+   heapWorker.destroy();
+-  yield front.detach();
++  await front.detach();
+ });
+diff --git a/devtools/client/memory/test/unit/test_utils-get-snapshot-totals.js b/devtools/client/memory/test/unit/test_utils-get-snapshot-totals.js
+--- a/devtools/client/memory/test/unit/test_utils-get-snapshot-totals.js
++++ b/devtools/client/memory/test/unit/test_utils-get-snapshot-totals.js
+@@ -9,30 +9,30 @@
+  */
+ 
+ const { censusDisplays, viewState, censusState } = require("devtools/client/memory/constants");
+ const { getSnapshotTotals } = require("devtools/client/memory/utils");
+ const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
+ const { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display");
+ const { changeView } = require("devtools/client/memory/actions/view");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let front = new StubbedMemoryFront();
+   let heapWorker = new HeapAnalysesClient();
+-  yield front.attach();
++  await front.attach();
+   let store = Store();
+   let { getState, dispatch } = store;
+ 
+   dispatch(changeView(viewState.CENSUS));
+ 
+-  yield dispatch(setCensusDisplayAndRefresh(heapWorker,
++  await dispatch(setCensusDisplayAndRefresh(heapWorker,
+                                             censusDisplays.allocationStack));
+ 
+   dispatch(takeSnapshotAndCensus(front, heapWorker));
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+ 
+   ok(!getState().snapshots[0].census.display.inverted, "Snapshot is not inverted");
+ 
+   let census = getState().snapshots[0].census;
+   let result = aggregate(census.report);
+   let totalBytes = result.bytes;
+   let totalCount = result.count;
+ 
+@@ -41,18 +41,18 @@ add_task(function* () {
+ 
+   result = getSnapshotTotals(getState().snapshots[0].census);
+   equal(totalBytes, result.bytes, "getSnapshotTotals reuslted in correct bytes");
+   equal(totalCount, result.count, "getSnapshotTotals reuslted in correct count");
+ 
+   dispatch(setCensusDisplayAndRefresh(heapWorker,
+                                       censusDisplays.invertedAllocationStack));
+ 
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
+-  yield waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVING]);
++  await waitUntilCensusState(store, s => s.census, [censusState.SAVED]);
+   ok(getState().snapshots[0].census.display.inverted, "Snapshot is inverted");
+ 
+   result = getSnapshotTotals(getState().snapshots[0].census);
+   equal(totalBytes, result.bytes,
+         "getSnapshotTotals reuslted in correct bytes when inverted");
+   equal(totalCount, result.count,
+         "getSnapshotTotals reuslted in correct count when inverted");
+ });

+ 8649 - 0
frg/work-js/mozilla-release/patches/1440321-1j-performance-61a1.patch

@@ -0,0 +1,8649 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  cd565ec7778c863168c0e4aa47ef4511b0ca0b86
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1j. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/performance/components/test/head.js b/devtools/client/performance/components/test/head.js
+--- a/devtools/client/performance/components/test/head.js
++++ b/devtools/client/performance/components/test/head.js
+@@ -6,17 +6,16 @@
+ /* global window, document, SimpleTest, requestAnimationFrame, is, ok */
+ /* exported Cc, Ci, Cu, Cr, Assert, Task, TargetFactory, Toolbox, browserRequire,
+    forceRender, setProps, dumpn, checkOptimizationHeader, checkOptimizationTree */
+ let { require } = ChromeUtils.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
+ let { Assert } = require("resource://testing-common/Assert.jsm");
+ let { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
+ let defer = require("devtools/shared/defer");
+ let flags = require("devtools/shared/flags");
+-let { Task } = require("devtools/shared/task");
+ let { TargetFactory } = require("devtools/client/framework/target");
+ let { Toolbox } = require("devtools/client/framework/toolbox");
+ 
+ flags.testing = true;
+ let { require: browserRequire } = BrowserLoader({
+   baseURI: "resource://devtools/client/performance/",
+   window
+ });
+diff --git a/devtools/client/performance/components/test/test_jit_optimizations_01.html b/devtools/client/performance/components/test/test_jit_optimizations_01.html
+--- a/devtools/client/performance/components/test/test_jit_optimizations_01.html
++++ b/devtools/client/performance/components/test/test_jit_optimizations_01.html
+@@ -8,17 +8,17 @@ Test the rendering of the JIT Optimizati
+   <title>JITOptimizations component test</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">
+ </head>
+ <body style="height: 10000px;">
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     let React = browserRequire("devtools/client/shared/vendor/react");
+     let JITOptimizations = React.createFactory(browserRequire("devtools/client/performance/components/jit-optimizations"));
+     ok(JITOptimizations, "Should get JITOptimizations");
+     let opts;
+ 
+     opts = ReactDOM.render(JITOptimizations({
+@@ -27,17 +27,17 @@ window.onload = Task.async(function* () 
+         isMetaCategory: false,
+         url: "http://internet.com/file.js",
+         line: 1,
+         functionName: "myfunc",
+       },
+       optimizationSites: OPTS_DATA_GENERAL,
+       autoExpandDepth: 1000,
+     }), window.document.body);
+-    yield forceRender(opts);
++    await forceRender(opts);
+ 
+     checkOptimizationHeader("myfunc", "file.js", "1");
+ 
+     checkOptimizationTree([
+       { type: "site", strategy: "GetElem_TypedArray", samples: "90" },
+       { type: "types", count: "2" },
+       { type: "type", site: "Receiver", mirType: "Object" },
+       { type: "observedtype", keyedBy: "constructor", name: "MyView" },
+@@ -58,13 +58,13 @@ window.onload = Task.async(function* () 
+       { type: "attempt", strategy: "Call_Inline", outcome: "CantInlineBigData" },
+     ]);
+ 
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/performance/modules/widgets/graphs.js b/devtools/client/performance/modules/widgets/graphs.js
+--- a/devtools/client/performance/modules/widgets/graphs.js
++++ b/devtools/client/performance/modules/widgets/graphs.js
+@@ -2,17 +2,16 @@
+  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+  * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ /**
+  * This file contains the base line graph that all Performance line graphs use.
+  */
+ 
+-const { Task } = require("devtools/shared/task");
+ const { extend } = require("devtools/shared/extend");
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ const MountainGraphWidget = require("devtools/client/shared/widgets/MountainGraphWidget");
+ const { CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");
+ 
+ const defer = require("devtools/shared/defer");
+ const EventEmitter = require("devtools/shared/old-event-emitter");
+ 
+@@ -201,59 +200,59 @@ GraphsController.prototype = {
+ 
+   /**
+    * Iterates through all graphs and renders the data
+    * from a RecordingModel. Takes a resolution value used in
+    * some graphs.
+    * Saves rendering progress as a promise to be consumed by `destroy`,
+    * to wait for cleaning up rendering during destruction.
+    */
+-  render: Task.async(function* (recordingData, resolution) {
++  async render(recordingData, resolution) {
+     // Get the previous render promise so we don't start rendering
+     // until the previous render cycle completes, which can occur
+     // especially when a recording is finished, and triggers a
+     // fresh rendering at a higher rate
+-    yield (this._rendering && this._rendering.promise);
++    await (this._rendering && this._rendering.promise);
+ 
+     // Check after yielding to ensure we're not tearing down,
+     // as this can create a race condition in tests
+     if (this._destroyed) {
+       return;
+     }
+ 
+     this._rendering = defer();
+-    for (let graph of (yield this._getEnabled())) {
+-      yield graph.setPerformanceData(recordingData, resolution);
++    for (let graph of (await this._getEnabled())) {
++      await graph.setPerformanceData(recordingData, resolution);
+       this.emit("rendered", graph.graphName);
+     }
+     this._rendering.resolve();
+-  }),
++  },
+ 
+   /**
+    * Destroys the underlying graphs.
+    */
+-  destroy: Task.async(function* () {
++  async destroy() {
+     let primary = this._getPrimaryLink();
+ 
+     this._destroyed = true;
+ 
+     if (primary) {
+       primary.off("selecting", this._onSelecting);
+     }
+ 
+     // If there was rendering, wait until the most recent render cycle
+     // has finished
+     if (this._rendering) {
+-      yield this._rendering.promise;
++      await this._rendering.promise;
+     }
+ 
+     for (let graph of this.getWidgets()) {
+-      yield graph.destroy();
++      await graph.destroy();
+     }
+-  }),
++  },
+ 
+   /**
+    * Applies the theme to the underlying graphs. Optionally takes
+    * a `redraw` boolean in the options to force redraw.
+    */
+   setTheme: function(options = {}) {
+     let theme = options.theme || this._getTheme();
+     for (let graph of this.getWidgets()) {
+@@ -262,30 +261,30 @@ GraphsController.prototype = {
+     }
+   },
+ 
+   /**
+    * Sets up the graph, if needed. Returns a promise resolving
+    * to the graph if it is enabled once it's ready, or otherwise returns
+    * null if disabled.
+    */
+-  isAvailable: Task.async(function* (graphName) {
++  async isAvailable(graphName) {
+     if (!this._enabled.has(graphName)) {
+       return null;
+     }
+ 
+     let graph = this.get(graphName);
+ 
+     if (!graph) {
+-      graph = yield this._construct(graphName);
++      graph = await this._construct(graphName);
+     }
+ 
+-    yield graph.ready();
++    await graph.ready();
+     return graph;
+-  }),
++  },
+ 
+   /**
+    * Enable or disable a subgraph controlled by GraphsController.
+    * This determines what graphs are visible and get rendered.
+    */
+   enable: function(graphName, isEnabled) {
+     let el = this.$(this._definition[graphName].selector);
+     el.classList[isEnabled ? "remove" : "add"]("hidden");
+@@ -351,48 +350,48 @@ GraphsController.prototype = {
+       return this._getPrimaryLink().dropSelection();
+     }
+     return null;
+   },
+ 
+   /**
+    * Makes sure the selection is enabled or disabled in all the graphs.
+    */
+-  selectionEnabled: Task.async(function* (enabled) {
+-    for (let graph of (yield this._getEnabled())) {
++  async selectionEnabled(enabled) {
++    for (let graph of (await this._getEnabled())) {
+       graph.selectionEnabled = enabled;
+     }
+-  }),
++  },
+ 
+   /**
+    * Creates the graph `graphName` and initializes it.
+    */
+-  _construct: Task.async(function* (graphName) {
++  async _construct(graphName) {
+     let def = this._definition[graphName];
+     let el = this.$(def.selector);
+     let filter = this._getFilter();
+     let graph = this._graphs[graphName] = new def.constructor(el, filter);
+     graph.graphName = graphName;
+ 
+-    yield graph.ready();
++    await graph.ready();
+ 
+     // Sync the graphs' animations and selections together
+     if (def.primaryLink) {
+       graph.on("selecting", this._onSelecting);
+     } else {
+       CanvasGraphUtils.linkAnimation(this._getPrimaryLink(), graph);
+       CanvasGraphUtils.linkSelection(this._getPrimaryLink(), graph);
+     }
+ 
+     // Sets the container element's visibility based off of enabled status
+     el.classList[this._enabled.has(graphName) ? "remove" : "add"]("hidden");
+ 
+     this.setTheme();
+     return graph;
+-  }),
++  },
+ 
+   /**
+    * Returns the main graph for this collection, that all graphs
+    * are bound to for syncing and selection.
+    */
+   _getPrimaryLink: function() {
+     return this.get(this._primaryLink);
+   },
+@@ -405,30 +404,30 @@ GraphsController.prototype = {
+   },
+ 
+   /**
+    * Resolves to an array with all graphs that are enabled, and
+    * creates them if needed. Different than just iterating over `this._graphs`,
+    * as those could be enabled. Uses caching, as rendering happens many times per second,
+    * compared to how often which graphs/features are changed (rarely).
+    */
+-  _getEnabled: Task.async(function* () {
++  async _getEnabled() {
+     if (this._enabledGraphs) {
+       return this._enabledGraphs;
+     }
+     let enabled = [];
+     for (let graphName of this._enabled) {
+-      let graph = yield this.isAvailable(graphName);
++      let graph = await this.isAvailable(graphName);
+       if (graph) {
+         enabled.push(graph);
+       }
+     }
+     this._enabledGraphs = enabled;
+     return this._enabledGraphs;
+-  }),
++  },
+ };
+ 
+ /**
+  * A base class for performance graphs to inherit from.
+  *
+  * @param nsIDOMNode parent
+  *        The parent node holding the overview.
+  * @param string metric
+@@ -436,20 +435,20 @@ GraphsController.prototype = {
+  */
+ function OptimizationsGraph(parent) {
+   MountainGraphWidget.call(this, parent);
+   this.setTheme();
+ }
+ 
+ OptimizationsGraph.prototype = extend(MountainGraphWidget.prototype, {
+ 
+-  render: Task.async(function* (threadNode, frameNode) {
++  async render(threadNode, frameNode) {
+     // Regardless if we draw or clear the graph, wait
+     // until it's ready.
+-    yield this.ready();
++    await this.ready();
+ 
+     if (!threadNode || !frameNode) {
+       this.setData([]);
+       return;
+     }
+ 
+     let { sampleTimes } = threadNode;
+ 
+@@ -472,18 +471,18 @@ OptimizationsGraph.prototype = extend(Mo
+     // log an error.
+     if (!data) {
+       console.error(
+         `FrameNode#${frameNode.location} does not have optimizations data to render.`);
+       return;
+     }
+ 
+     this.dataOffsetX = startTime;
+-    yield this.setData(data);
+-  }),
++    await this.setData(data);
++  },
+ 
+   /**
+    * Sets the theme via `theme` to either "light" or "dark",
+    * and updates the internal styling to match. Requires a redraw
+    * to see the effects.
+    */
+   setTheme: function(theme) {
+     theme = theme || "light";
+diff --git a/devtools/client/performance/panel.js b/devtools/client/performance/panel.js
+--- a/devtools/client/performance/panel.js
++++ b/devtools/client/performance/panel.js
+@@ -1,16 +1,15 @@
+ /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+ /* vim: set ft=javascript ts=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/. */
+ "use strict";
+ 
+-const { Task } = require("devtools/shared/task");
+ const defer = require("devtools/shared/defer");
+ 
+ loader.lazyRequireGetter(this, "EventEmitter",
+   "devtools/shared/old-event-emitter");
+ 
+ function PerformancePanel(iframeWindow, toolbox) {
+   this.panelWin = iframeWindow;
+   this.toolbox = toolbox;
+@@ -23,77 +22,77 @@ exports.PerformancePanel = PerformancePa
+ PerformancePanel.prototype = {
+   /**
+    * Open is effectively an asynchronous constructor.
+    *
+    * @return object
+    *         A promise that is resolved when the Performance tool
+    *         completes opening.
+    */
+-  open: Task.async(function* () {
++  async open() {
+     if (this._opening) {
+       return this._opening;
+     }
+     let deferred = defer();
+     this._opening = deferred.promise;
+ 
+     this.panelWin.gToolbox = this.toolbox;
+     this.panelWin.gTarget = this.target;
+     this._checkRecordingStatus = this._checkRecordingStatus.bind(this);
+ 
+     // Actor is already created in the toolbox; reuse
+     // the same front, and the toolbox will also initialize the front,
+     // but redo it here so we can hook into the same event to prevent race conditions
+     // in the case of the front still being in the process of opening.
+-    let front = yield this.panelWin.gToolbox.initPerformance();
++    let front = await this.panelWin.gToolbox.initPerformance();
+ 
+     // This should only happen if this is completely unsupported (when profiler
+     // does not exist), and in that case, the tool shouldn't be available,
+     // so let's ensure this assertion.
+     if (!front) {
+       console.error("No PerformanceFront found in toolbox.");
+     }
+ 
+     this.panelWin.gFront = front;
+     let { PerformanceController, EVENTS } = this.panelWin;
+     PerformanceController.on(EVENTS.RECORDING_ADDED, this._checkRecordingStatus);
+     PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE, this._checkRecordingStatus);
+-    yield this.panelWin.startupPerformance();
++    await this.panelWin.startupPerformance();
+ 
+     // Fire this once incase we have an in-progress recording (console profile)
+     // that caused this start up, and no state change yet, so we can highlight the
+     // tab if we need.
+     this._checkRecordingStatus();
+ 
+     this.isReady = true;
+     this.emit("ready");
+ 
+     deferred.resolve(this);
+     return this._opening;
+-  }),
++  },
+ 
+   // DevToolPanel API
+ 
+   get target() {
+     return this.toolbox.target;
+   },
+ 
+-  destroy: Task.async(function* () {
++  async destroy() {
+     // Make sure this panel is not already destroyed.
+     if (this._destroyed) {
+       return;
+     }
+ 
+     let { PerformanceController, EVENTS } = this.panelWin;
+     PerformanceController.off(EVENTS.RECORDING_ADDED, this._checkRecordingStatus);
+     PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE, this._checkRecordingStatus);
+-    yield this.panelWin.shutdownPerformance();
++    await this.panelWin.shutdownPerformance();
+     this.emit("destroyed");
+     this._destroyed = true;
+-  }),
++  },
+ 
+   _checkRecordingStatus: function() {
+     if (this.panelWin.PerformanceController.isRecording()) {
+       this.toolbox.highlightTool("performance");
+     } else {
+       this.toolbox.unhighlightTool("performance");
+     }
+   }
+diff --git a/devtools/client/performance/performance-controller.js b/devtools/client/performance/performance-controller.js
+--- a/devtools/client/performance/performance-controller.js
++++ b/devtools/client/performance/performance-controller.js
+@@ -7,17 +7,16 @@
+ 
+ /* exported Cc, Ci, Cu, Cr, loader, Promise */
+ var BrowserLoaderModule = {};
+ ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
+ var { loader, require } = BrowserLoaderModule.BrowserLoader({
+   baseURI: "resource://devtools/client/performance/",
+   window
+ });
+-var { Task } = require("devtools/shared/task");
+ /* exported ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout */
+ var { ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
+ var { PrefObserver } = require("devtools/client/shared/prefs");
+ /* exported extend */
+ const { extend } = require("devtools/shared/extend");
+ // Use privileged promise in panel documents to prevent having them to freeze
+ // during toolbox destruction. See bug 1402779.
+ var Promise = require("Promise");
+@@ -86,44 +85,44 @@ var BRANCH_NAME = "devtools.performance.
+ /* exported gToolbox, gTarget, gFront */
+ var gToolbox, gTarget, gFront;
+ 
+ /* exported startupPerformance, shutdownPerformance, PerformanceController */
+ 
+ /**
+  * Initializes the profiler controller and views.
+  */
+-var startupPerformance = Task.async(function* () {
+-  yield PerformanceController.initialize();
+-  yield PerformanceView.initialize();
++var startupPerformance = async function() {
++  await PerformanceController.initialize();
++  await PerformanceView.initialize();
+   PerformanceController.enableFrontEventListeners();
+-});
++};
+ 
+ /**
+  * Destroys the profiler controller and views.
+  */
+-var shutdownPerformance = Task.async(function* () {
+-  yield PerformanceController.destroy();
+-  yield PerformanceView.destroy();
++var shutdownPerformance = async function() {
++  await PerformanceController.destroy();
++  await PerformanceView.destroy();
+   PerformanceController.disableFrontEventListeners();
+-});
++};
+ 
+ /**
+  * Functions handling target-related lifetime events and
+  * UI interaction.
+  */
+ var PerformanceController = {
+   _recordings: [],
+   _currentRecording: null,
+ 
+   /**
+    * Listen for events emitted by the current tab target and
+    * main UI events.
+    */
+-  initialize: Task.async(function* () {
++  async initialize() {
+     this._telemetry = new PerformanceTelemetry(this);
+     this.startRecording = this.startRecording.bind(this);
+     this.stopRecording = this.stopRecording.bind(this);
+     this.importRecording = this.importRecording.bind(this);
+     this.exportRecording = this.exportRecording.bind(this);
+     this.clearRecordings = this.clearRecordings.bind(this);
+     this._onRecordingSelectFromView = this._onRecordingSelectFromView.bind(this);
+     this._onPrefChanged = this._onPrefChanged.bind(this);
+@@ -145,17 +144,17 @@ var PerformanceController = {
+     PerformanceView.on(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
+     PerformanceView.on(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
+     RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
+     RecordingsView.on(EVENTS.UI_RECORDING_SELECTED, this._onRecordingSelectFromView);
+     DetailsView.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this._pipe);
+ 
+     this._prefObserver = new PrefObserver("devtools.");
+     this._prefObserver.on("devtools.theme", this._onThemeChanged);
+-  }),
++  },
+ 
+   /**
+    * Remove events handled by the PerformanceController
+    */
+   destroy: function() {
+     this._telemetry.destroy();
+     this._prefs.off("pref-changed", this._onPrefChanged);
+     this._prefs.unregisterObserver();
+@@ -234,126 +233,126 @@ var PerformanceController = {
+   setPref: function(prefName, prefValue) {
+     this._prefs[prefName] = prefValue;
+   },
+ 
+   /**
+    * Checks whether or not a new recording is supported by the PerformanceFront.
+    * @return Promise:boolean
+    */
+-  canCurrentlyRecord: Task.async(function* () {
+-    let hasActor = yield gTarget.hasActor("performance");
++  async canCurrentlyRecord() {
++    let hasActor = await gTarget.hasActor("performance");
+     if (!hasActor) {
+       return true;
+     }
+-    let actorCanCheck = yield gTarget.actorHasMethod("performance", "canCurrentlyRecord");
++    let actorCanCheck = await gTarget.actorHasMethod("performance", "canCurrentlyRecord");
+     if (!actorCanCheck) {
+       return true;
+     }
+-    return (yield gFront.canCurrentlyRecord()).success;
+-  }),
++    return (await gFront.canCurrentlyRecord()).success;
++  },
+ 
+   /**
+    * Starts recording with the PerformanceFront.
+    */
+-  startRecording: Task.async(function* () {
++  async startRecording() {
+     let options = {
+       withMarkers: true,
+       withTicks: this.getOption("enable-framerate"),
+       withMemory: this.getOption("enable-memory"),
+       withFrames: true,
+       withGCEvents: true,
+       withAllocations: this.getOption("enable-allocations"),
+       allocationsSampleProbability: this.getPref("memory-sample-probability"),
+       allocationsMaxLogLength: this.getPref("memory-max-log-length"),
+       bufferSize: this.getPref("profiler-buffer-size"),
+       sampleFrequency: this.getPref("profiler-sample-frequency")
+     };
+ 
+-    let recordingStarted = yield gFront.startRecording(options);
++    let recordingStarted = await gFront.startRecording(options);
+ 
+     // In some cases, like when the target has a private browsing tab,
+     // recording is not currently supported because of the profiler module.
+     // Present a notification in this case alerting the user of this issue.
+     if (!recordingStarted) {
+       this.emit(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START);
+       PerformanceView.setState("unavailable");
+     } else {
+       this.emit(EVENTS.BACKEND_READY_AFTER_RECORDING_START);
+     }
+-  }),
++  },
+ 
+   /**
+    * Stops recording with the PerformanceFront.
+    */
+-  stopRecording: Task.async(function* () {
++  async stopRecording() {
+     let recording = this.getLatestManualRecording();
+-    yield gFront.stopRecording(recording);
++    await gFront.stopRecording(recording);
+     this.emit(EVENTS.BACKEND_READY_AFTER_RECORDING_STOP);
+-  }),
++  },
+ 
+   /**
+    * Saves the given recording to a file. Emits `EVENTS.RECORDING_EXPORTED`
+    * when the file was saved.
+    *
+    * @param PerformanceRecording recording
+    *        The model that holds the recording data.
+    * @param nsIFile file
+    *        The file to stream the data into.
+    */
+-  exportRecording: Task.async(function* (_, recording, file) {
+-    yield recording.exportRecording(file);
++  async exportRecording(_, recording, file) {
++    await recording.exportRecording(file);
+     this.emit(EVENTS.RECORDING_EXPORTED, recording, file);
+-  }),
++  },
+ 
+    /**
+    * Clears all completed recordings from the list as well as the current non-console
+    * recording. Emits `EVENTS.RECORDING_DELETED` when complete so other components can
+    * clean up.
+    */
+-  clearRecordings: Task.async(function* () {
++  async clearRecordings() {
+     for (let i = this._recordings.length - 1; i >= 0; i--) {
+       let model = this._recordings[i];
+       if (!model.isConsole() && model.isRecording()) {
+-        yield this.stopRecording();
++        await this.stopRecording();
+       }
+       // If last recording is not recording, but finalizing itself,
+       // wait for that to finish
+       if (!model.isRecording() && !model.isCompleted()) {
+-        yield this.waitForStateChangeOnRecording(model, "recording-stopped");
++        await this.waitForStateChangeOnRecording(model, "recording-stopped");
+       }
+       // If recording is completed,
+       // clean it up from UI and remove it from the _recordings array.
+       if (model.isCompleted()) {
+         this.emit(EVENTS.RECORDING_DELETED, model);
+         this._recordings.splice(i, 1);
+       }
+     }
+     if (this._recordings.length > 0) {
+       if (!this._recordings.includes(this.getCurrentRecording())) {
+         this.setCurrentRecording(this._recordings[0]);
+       }
+     } else {
+       this.setCurrentRecording(null);
+     }
+-  }),
++  },
+ 
+   /**
+    * Loads a recording from a file, adding it to the recordings list. Emits
+    * `EVENTS.RECORDING_IMPORTED` when the file was loaded.
+    *
+    * @param nsIFile file
+    *        The file to import the data from.
+    */
+-  importRecording: Task.async(function* (_, file) {
+-    let recording = yield gFront.importRecording(file);
++  async importRecording(_, file) {
++    let recording = await gFront.importRecording(file);
+     this._addRecordingIfUnknown(recording);
+ 
+     this.emit(EVENTS.RECORDING_IMPORTED, recording);
+-  }),
++  },
+ 
+   /**
+    * Sets the currently active PerformanceRecording. Should rarely be called directly,
+    * as RecordingsView handles this when manually selected a recording item. Exceptions
+    * are when clearing the view.
+    * @param PerformanceRecording recording
+    */
+   setCurrentRecording: function(recording) {
+@@ -533,26 +532,26 @@ var PerformanceController = {
+   /**
+    * Takes a PerformanceRecording and a state, and waits for
+    * the event to be emitted from the front for that recording.
+    *
+    * @param {PerformanceRecordingFront} recording
+    * @param {string} expectedState
+    * @return {Promise}
+    */
+-  waitForStateChangeOnRecording: Task.async(function* (recording, expectedState) {
++  async waitForStateChangeOnRecording(recording, expectedState) {
+     let deferred = defer();
+     this.on(EVENTS.RECORDING_STATE_CHANGE, function handler(state, model) {
+       if (state === expectedState && model === recording) {
+         this.off(EVENTS.RECORDING_STATE_CHANGE, handler);
+         deferred.resolve();
+       }
+     });
+-    yield deferred.promise;
+-  }),
++    await deferred.promise;
++  },
+ 
+   /**
+    * Called on init, sets an `e10s` attribute on the main view container with
+    * "disabled" if e10s is possible on the platform and just not on, or "unsupported"
+    * if e10s is not possible on the platform. If e10s is on, no attribute is set.
+    */
+   _setMultiprocessAttributes: function() {
+     let { enabled } = this.getMultiprocessStatus();
+diff --git a/devtools/client/performance/performance-view.js b/devtools/client/performance/performance-view.js
+--- a/devtools/client/performance/performance-view.js
++++ b/devtools/client/performance/performance-view.js
+@@ -110,17 +110,17 @@ var PerformanceView = {
+         val: () => $("#loading-notice")
+       },
+     ]
+   },
+ 
+   /**
+    * Sets up the view with event binding and main subviews.
+    */
+-  initialize: Task.async(function* () {
++  async initialize() {
+     this._onRecordButtonClick = this._onRecordButtonClick.bind(this);
+     this._onImportButtonClick = this._onImportButtonClick.bind(this);
+     this._onClearButtonClick = this._onClearButtonClick.bind(this);
+     this._onRecordingSelected = this._onRecordingSelected.bind(this);
+     this._onProfilerStatusUpdated = this._onProfilerStatusUpdated.bind(this);
+     this._onRecordingStateChange = this._onRecordingStateChange.bind(this);
+     this._onNewRecordingFailed = this._onNewRecordingFailed.bind(this);
+ 
+@@ -128,75 +128,75 @@ var PerformanceView = {
+     PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
+     PerformanceController.on(EVENTS.RECORDING_PROFILER_STATUS_UPDATE,
+                              this._onProfilerStatusUpdated);
+     PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStateChange);
+     PerformanceController.on(EVENTS.RECORDING_ADDED, this._onRecordingStateChange);
+     PerformanceController.on(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START,
+                              this._onNewRecordingFailed);
+ 
+-    if (yield PerformanceController.canCurrentlyRecord()) {
++    if (await PerformanceController.canCurrentlyRecord()) {
+       this.setState("empty");
+     } else {
+       this.setState("unavailable");
+     }
+ 
+     // Initialize the ToolbarView first, because other views may need access
+     // to the OptionsView via the controller, to read prefs.
+-    yield ToolbarView.initialize();
+-    yield RecordingsView.initialize();
+-    yield OverviewView.initialize();
+-    yield DetailsView.initialize();
++    await ToolbarView.initialize();
++    await RecordingsView.initialize();
++    await OverviewView.initialize();
++    await DetailsView.initialize();
+ 
+     // DE-XUL: Begin migrating the toolbar to React. Temporarily hold state here.
+     this._recordingControlsState = {
+       onRecordButtonClick: this._onRecordButtonClick,
+       onImportButtonClick: this._onImportButtonClick,
+       onClearButtonClick: this._onClearButtonClick,
+       isRecording: false,
+       isDisabled: false
+     };
+     // Mount to an HTML element.
+     const {createHtmlMount} = PerformanceUtils;
+     this._recordingControlsMount = createHtmlMount($("#recording-controls-mount"));
+     this._recordingButtonsMounts = Array.from($$(".recording-button-mount"))
+                                         .map(createHtmlMount);
+ 
+     this._renderRecordingControls();
+-  }),
++  },
+ 
+   /**
+    * DE-XUL: Render the recording controls and buttons using React.
+    */
+   _renderRecordingControls: function() {
+     ReactDOM.render(RecordingControls(this._recordingControlsState),
+                     this._recordingControlsMount);
+     for (let button of this._recordingButtonsMounts) {
+       ReactDOM.render(RecordingButton(this._recordingControlsState), button);
+     }
+   },
+ 
+   /**
+    * Unbinds events and destroys subviews.
+    */
+-  destroy: Task.async(function* () {
++  async destroy() {
+     PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
+     PerformanceController.off(EVENTS.RECORDING_PROFILER_STATUS_UPDATE,
+                               this._onProfilerStatusUpdated);
+     PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
+                               this._onRecordingStateChange);
+     PerformanceController.off(EVENTS.RECORDING_ADDED, this._onRecordingStateChange);
+     PerformanceController.off(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START,
+                               this._onNewRecordingFailed);
+ 
+-    yield ToolbarView.destroy();
+-    yield RecordingsView.destroy();
+-    yield OverviewView.destroy();
+-    yield DetailsView.destroy();
+-  }),
++    await ToolbarView.destroy();
++    await RecordingsView.destroy();
++    await OverviewView.destroy();
++    await DetailsView.destroy();
++  },
+ 
+   /**
+    * Sets the state of the profiler view. Possible options are "unavailable",
+    * "empty", "recording", "console-recording", "recorded".
+    */
+   setState: function(state) {
+     // Make sure that the focus isn't captured on a hidden iframe. This fixes a
+     // XUL bug where shortcuts stop working.
+diff --git a/devtools/client/performance/test/browser_aaa-run-first-leaktest.js b/devtools/client/performance/test/browser_aaa-run-first-leaktest.js
+--- a/devtools/client/performance/test/browser_aaa-run-first-leaktest.js
++++ b/devtools/client/performance/test/browser_aaa-run-first-leaktest.js
+@@ -5,24 +5,24 @@
+ /**
+  * Tests if the performance tool leaks on initialization and sudden destruction.
+  * You can also use this initialization format as a template for other tests.
+  */
+ 
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ 
+-add_task(function* () {
+-  let { target, toolbox, panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { target, toolbox, panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   ok(target, "Should have a target available.");
+   ok(toolbox, "Should have a toolbox available.");
+   ok(panel, "Should have a panel available.");
+ 
+   ok(panel.panelWin.gTarget, "Should have a target reference on the panel window.");
+   ok(panel.panelWin.gToolbox, "Should have a toolbox reference on the panel window.");
+   ok(panel.panelWin.gFront, "Should have a front reference on the panel window.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-button-states.js b/devtools/client/performance/test/browser_perf-button-states.js
+--- a/devtools/client/performance/test/browser_perf-button-states.js
++++ b/devtools/client/performance/test/browser_perf-button-states.js
+@@ -5,18 +5,18 @@
+ /**
+  * Tests that the recording button states are set as expected.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { $, $$, EVENTS, PerformanceController, PerformanceView } = panel.panelWin;
+ 
+   let recordButton = $("#main-record-button");
+ 
+@@ -28,47 +28,47 @@ add_task(function* () {
+   });
+   let backendStartReady = once(PerformanceController,
+                                EVENTS.BACKEND_READY_AFTER_RECORDING_START);
+   let uiStateRecording = once(PerformanceView, EVENTS.UI_STATE_CHANGED, {
+     expectedArgs: { "1": "recording" }
+   });
+ 
+   click(recordButton);
+-  yield uiStartClick;
++  await uiStartClick;
+ 
+   checkRecordButtonsStates(true, true);
+ 
+-  yield recordingStarted;
++  await recordingStarted;
+ 
+   checkRecordButtonsStates(true, false);
+ 
+-  yield backendStartReady;
+-  yield uiStateRecording;
++  await backendStartReady;
++  await uiStateRecording;
+ 
+   let uiStopClick = once(PerformanceView, EVENTS.UI_STOP_RECORDING);
+   let recordingStopped = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
+     expectedArgs: { "1": "recording-stopped" }
+   });
+   let backendStopReady = once(PerformanceController,
+                                EVENTS.BACKEND_READY_AFTER_RECORDING_STOP);
+   let uiStateRecorded = once(PerformanceView, EVENTS.UI_STATE_CHANGED, {
+     expectedArgs: { "1": "recorded" }
+   });
+ 
+   click(recordButton);
+-  yield uiStopClick;
+-  yield recordingStopped;
++  await uiStopClick;
++  await recordingStopped;
+ 
+   checkRecordButtonsStates(false, false);
+ 
+-  yield backendStopReady;
+-  yield uiStateRecorded;
++  await backendStopReady;
++  await uiStateRecorded;
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ 
+   function checkRecordButtonsStates(checked, locked) {
+     for (let button of $$(".record-button")) {
+       is(button.classList.contains("checked"), checked,
+          "The record button checked state should be " + checked);
+       is(button.disabled, locked,
+          "The record button locked state should be " + locked);
+     }
+diff --git a/devtools/client/performance/test/browser_perf-calltree-js-categories.js b/devtools/client/performance/test/browser_perf-calltree-js-categories.js
+--- a/devtools/client/performance/test/browser_perf-calltree-js-categories.js
++++ b/devtools/client/performance/test/browser_perf-calltree-js-categories.js
+@@ -9,50 +9,50 @@
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_SHOW_PLATFORM_DATA_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { busyWait } = require("devtools/client/performance/test/helpers/wait-utils");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, $, $$, DetailsView, JsCallTreeView } = panel.panelWin;
+ 
+   // Enable platform data to show the categories in the tree.
+   Services.prefs.setBoolPref(UI_SHOW_PLATFORM_DATA_PREF, true);
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   // To show the `Gecko` category in the tree.
+-  yield busyWait(100);
+-  yield stopRecording(panel);
++  await busyWait(100);
++  await stopRecording(panel);
+ 
+   let rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield rendered;
++  await DetailsView.selectView("js-calltree");
++  await rendered;
+ 
+   is($(".call-tree-cells-container").hasAttribute("categories-hidden"), false,
+     "The call tree cells container should show the categories now.");
+   ok(geckoCategoryPresent($$),
+     "A category node with the text `Gecko` is displayed in the tree.");
+ 
+   // Disable platform data to hide the categories.
+   Services.prefs.setBoolPref(UI_SHOW_PLATFORM_DATA_PREF, false);
+ 
+   is($(".call-tree-cells-container").getAttribute("categories-hidden"), "",
+     "The call tree cells container should hide the categories now.");
+   ok(!geckoCategoryPresent($$),
+     "A category node with the text `Gecko` doesn't exist in the tree anymore.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+ 
+ function geckoCategoryPresent($$) {
+   for (let elem of $$(".call-tree-category")) {
+     if (elem.textContent.trim() == "Gecko") {
+       return true;
+     }
+   }
+diff --git a/devtools/client/performance/test/browser_perf-calltree-js-columns.js b/devtools/client/performance/test/browser_perf-calltree-js-columns.js
+--- a/devtools/client/performance/test/browser_perf-calltree-js-columns.js
++++ b/devtools/client/performance/test/browser_perf-calltree-js-columns.js
+@@ -8,50 +8,50 @@
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_SHOW_PLATFORM_DATA_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { busyWait } = require("devtools/client/performance/test/helpers/wait-utils");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, $, $$, DetailsView, JsCallTreeView } = panel.panelWin;
+ 
+   // Enable platform data to show the platform functions in the tree.
+   Services.prefs.setBoolPref(UI_SHOW_PLATFORM_DATA_PREF, true);
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   // To show the `busyWait` function in the tree.
+-  yield busyWait(100);
+-  yield stopRecording(panel);
++  await busyWait(100);
++  await stopRecording(panel);
+ 
+   let rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield rendered;
++  await DetailsView.selectView("js-calltree");
++  await rendered;
+ 
+   ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
+ 
+   testCells($, $$, {
+     "duration": true,
+     "percentage": true,
+     "allocations": false,
+     "self-duration": true,
+     "self-percentage": true,
+     "self-allocations": false,
+     "samples": true,
+     "function": true
+   });
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+ 
+ function testCells($, $$, visibleCells) {
+   for (let cell in visibleCells) {
+     if (visibleCells[cell]) {
+       ok($(`.call-tree-cell[type=${cell}]`),
+         `At least one ${cell} column was visible in the tree.`);
+     } else {
+diff --git a/devtools/client/performance/test/browser_perf-calltree-js-events.js b/devtools/client/performance/test/browser_perf-calltree-js-events.js
+--- a/devtools/client/performance/test/browser_perf-calltree-js-events.js
++++ b/devtools/client/performance/test/browser_perf-calltree-js-events.js
+@@ -8,30 +8,30 @@
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { synthesizeProfile } = require("devtools/client/performance/test/helpers/synth-utils");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { ThreadNode } = require("devtools/client/performance/modules/logic/tree-model");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, $, DetailsView, OverviewView, JsCallTreeView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield rendered;
++  await DetailsView.selectView("js-calltree");
++  await rendered;
+ 
+   // Mock the profile used so we can get a deterministic tree created.
+   let profile = synthesizeProfile();
+   let threadNode = new ThreadNode(profile.threads[0], OverviewView.getTimeInterval());
+   JsCallTreeView._populateCallTree(threadNode);
+   JsCallTreeView.emit(EVENTS.UI_JS_CALL_TREE_RENDERED);
+ 
+   let firstTreeItem = $("#js-calltree-view .call-tree-item");
+@@ -49,10 +49,10 @@ add_task(function* () {
+   key("VK_DOWN");
+   key("VK_DOWN");
+   key("VK_DOWN");
+   key("VK_DOWN");
+ 
+   JsCallTreeView.off("focus", onFocus);
+   is(count, 4, "Several focus events are fired for the calltree.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-calltree-memory-columns.js b/devtools/client/performance/test/browser_perf-calltree-memory-columns.js
+--- a/devtools/client/performance/test/browser_perf-calltree-memory-columns.js
++++ b/devtools/client/performance/test/browser_perf-calltree-memory-columns.js
+@@ -7,33 +7,33 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_ALLOCATIONS_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, $, $$, DetailsView, MemoryCallTreeView } = panel.panelWin;
+ 
+   // Enable allocations to test.
+   Services.prefs.setBoolPref(UI_ENABLE_ALLOCATIONS_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(MemoryCallTreeView, EVENTS.UI_MEMORY_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("memory-calltree");
+-  yield rendered;
++  await DetailsView.selectView("memory-calltree");
++  await rendered;
+ 
+   ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
+ 
+   testCells($, $$, {
+     "duration": false,
+     "percentage": false,
+     "count": true,
+     "count-percentage": true,
+@@ -44,17 +44,17 @@ add_task(function* () {
+     "self-count": true,
+     "self-count-percentage": true,
+     "self-size": true,
+     "self-size-percentage": true,
+     "samples": false,
+     "function": true
+   });
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+ 
+ function testCells($, $$, visibleCells) {
+   for (let cell in visibleCells) {
+     if (visibleCells[cell]) {
+       ok($(`.call-tree-cell[type=${cell}]`),
+         `At least one ${cell} column was visible in the tree.`);
+     } else {
+diff --git a/devtools/client/performance/test/browser_perf-console-record-01.js b/devtools/client/performance/test/browser_perf-console-record-01.js
+--- a/devtools/client/performance/test/browser_perf-console-record-01.js
++++ b/devtools/client/performance/test/browser_perf-console-record-01.js
+@@ -7,37 +7,37 @@
+  * before it was opened.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInTab, initConsoleInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
+ const { getSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { target, console } = yield initConsoleInNewTab({
++add_task(async function() {
++  let { target, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+-  yield console.profile("rust");
+-  yield console.profileEnd("rust");
++  await console.profile("rust");
++  await console.profileEnd("rust");
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+   let { PerformanceController, WaterfallView } = panel.panelWin;
+ 
+-  yield waitUntil(() => PerformanceController.getRecordings().length == 1);
+-  yield waitUntil(() => WaterfallView.wasRenderedAtLeastOnce);
++  await waitUntil(() => PerformanceController.getRecordings().length == 1);
++  await waitUntil(() => WaterfallView.wasRenderedAtLeastOnce);
+ 
+   let recordings = PerformanceController.getRecordings();
+   is(recordings.length, 1, "One recording found in the performance panel.");
+   is(recordings[0].isConsole(), true, "Recording came from console.profile.");
+   is(recordings[0].getLabel(), "rust", "Correct label in the recording model.");
+ 
+   const selected = getSelectedRecording(panel);
+ 
+   is(selected, recordings[0],
+     "The profile from console should be selected as it's the only one.");
+   is(selected.getLabel(), "rust",
+     "The profile label for the first recording is correct.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-console-record-02.js b/devtools/client/performance/test/browser_perf-console-record-02.js
+--- a/devtools/client/performance/test/browser_perf-console-record-02.js
++++ b/devtools/client/performance/test/browser_perf-console-record-02.js
+@@ -10,29 +10,29 @@
+ const { Constants } = require("devtools/client/performance/modules/constants");
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInTab, initConsoleInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { waitForRecordingStoppedEvents } = require("devtools/client/performance/test/helpers/actions");
+ const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
+ const { times } = require("devtools/client/performance/test/helpers/event-utils");
+ const { getSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { target, console } = yield initConsoleInNewTab({
++add_task(async function() {
++  let { target, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+-  yield console.profile("rust");
+-  yield console.profile("rust2");
++  await console.profile("rust");
++  await console.profile("rust2");
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+   let { EVENTS, PerformanceController, OverviewView } = panel.panelWin;
+ 
+-  yield waitUntil(() => PerformanceController.getRecordings().length == 2);
++  await waitUntil(() => PerformanceController.getRecordings().length == 2);
+ 
+   let recordings = PerformanceController.getRecordings();
+   is(recordings.length, 2, "Two recordings found in the performance panel.");
+   is(recordings[0].isConsole(), true, "Recording came from console.profile (1).");
+   is(recordings[0].getLabel(), "rust", "Correct label in the recording model (1).");
+   is(recordings[0].isRecording(), true, "Recording is still recording (1).");
+   is(recordings[1].isConsole(), true, "Recording came from console.profile (2).");
+   is(recordings[1].getLabel(), "rust2", "Correct label in the recording model (2).");
+@@ -40,31 +40,31 @@ add_task(function* () {
+ 
+   const selected = getSelectedRecording(panel);
+   is(selected, recordings[0],
+     "The first console recording should be selected.");
+   is(selected.getLabel(), "rust",
+     "The profile label for the first recording is correct.");
+ 
+   // Ensure overview is still rendering.
+-  yield times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
++  await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
+     expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
+   });
+ 
+   let stopped = waitForRecordingStoppedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profileEnd("rust");
+-  yield stopped;
++  await console.profileEnd("rust");
++  await stopped;
+ 
+   stopped = waitForRecordingStoppedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when a finished recording is selected
+     skipWaitingForOverview: true,
+     skipWaitingForSubview: true,
+   });
+-  yield console.profileEnd("rust2");
+-  yield stopped;
++  await console.profileEnd("rust2");
++  await stopped;
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-console-record-03.js b/devtools/client/performance/test/browser_perf-console-record-03.js
+--- a/devtools/client/performance/test/browser_perf-console-record-03.js
++++ b/devtools/client/performance/test/browser_perf-console-record-03.js
+@@ -8,31 +8,31 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInTab, initConsoleInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { waitForRecordingStoppedEvents } = require("devtools/client/performance/test/helpers/actions");
+ const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
+ const { getSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { target, console } = yield initConsoleInNewTab({
++add_task(async function() {
++  let { target, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+-  yield console.profile("rust");
+-  yield console.profileEnd("rust");
+-  yield console.profile("rust2");
++  await console.profile("rust");
++  await console.profileEnd("rust");
++  await console.profile("rust2");
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+   let { PerformanceController, WaterfallView } = panel.panelWin;
+ 
+-  yield waitUntil(() => PerformanceController.getRecordings().length == 2);
+-  yield waitUntil(() => WaterfallView.wasRenderedAtLeastOnce);
++  await waitUntil(() => PerformanceController.getRecordings().length == 2);
++  await waitUntil(() => WaterfallView.wasRenderedAtLeastOnce);
+ 
+   let recordings = PerformanceController.getRecordings();
+   is(recordings.length, 2, "Two recordings found in the performance panel.");
+   is(recordings[0].isConsole(), true, "Recording came from console.profile (1).");
+   is(recordings[0].getLabel(), "rust", "Correct label in the recording model (1).");
+   is(recordings[0].isRecording(), false, "Recording is still recording (1).");
+   is(recordings[1].isConsole(), true, "Recording came from console.profile (2).");
+   is(recordings[1].getLabel(), "rust2", "Correct label in the recording model (2).");
+@@ -46,13 +46,13 @@ add_task(function* () {
+ 
+   let stopped = waitForRecordingStoppedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when a finished recording is selected
+     skipWaitingForOverview: true,
+     skipWaitingForSubview: true,
+   });
+-  yield console.profileEnd("rust2");
+-  yield stopped;
++  await console.profileEnd("rust2");
++  await stopped;
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-console-record-04.js b/devtools/client/performance/test/browser_perf-console-record-04.js
+--- a/devtools/client/performance/test/browser_perf-console-record-04.js
++++ b/devtools/client/performance/test/browser_perf-console-record-04.js
+@@ -9,50 +9,50 @@
+ 
+ const { Constants } = require("devtools/client/performance/modules/constants");
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInTab, initConsoleInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { waitForRecordingStartedEvents, waitForRecordingStoppedEvents } = require("devtools/client/performance/test/helpers/actions");
+ const { times } = require("devtools/client/performance/test/helpers/event-utils");
+ const { getSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { target, console } = yield initConsoleInNewTab({
++add_task(async function() {
++  let { target, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+   let { EVENTS, PerformanceController, OverviewView } = panel.panelWin;
+ 
+   let started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profile("rust");
+-  yield started;
++  await console.profile("rust");
++  await started;
+ 
+   let recordings = PerformanceController.getRecordings();
+   is(recordings.length, 1, "One recording found in the performance panel.");
+   is(recordings[0].isConsole(), true, "Recording came from console.profile.");
+   is(recordings[0].getLabel(), "rust", "Correct label in the recording model.");
+   is(recordings[0].isRecording(), true, "Recording is still recording.");
+ 
+   const selected = getSelectedRecording(panel);
+   is(selected, recordings[0],
+     "The profile from console should be selected as it's the only one.");
+   is(selected.getLabel(), "rust",
+     "The profile label for the first recording is correct.");
+ 
+   // Ensure overview is still rendering.
+-  yield times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
++  await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
+     expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
+   });
+ 
+   let stopped = waitForRecordingStoppedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profileEnd("rust");
+-  yield stopped;
++  await console.profileEnd("rust");
++  await stopped;
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-console-record-05.js b/devtools/client/performance/test/browser_perf-console-record-05.js
+--- a/devtools/client/performance/test/browser_perf-console-record-05.js
++++ b/devtools/client/performance/test/browser_perf-console-record-05.js
+@@ -9,67 +9,67 @@
+ 
+ const { Constants } = require("devtools/client/performance/modules/constants");
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInTab, initConsoleInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { waitForRecordingStartedEvents, waitForRecordingStoppedEvents } = require("devtools/client/performance/test/helpers/actions");
+ const { times } = require("devtools/client/performance/test/helpers/event-utils");
+ const { getSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { target, console } = yield initConsoleInNewTab({
++add_task(async function() {
++  let { target, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+   let { EVENTS, PerformanceController, OverviewView } = panel.panelWin;
+ 
+   let started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profile("rust");
+-  yield started;
++  await console.profile("rust");
++  await started;
+ 
+   let recordings = PerformanceController.getRecordings();
+   is(recordings.length, 1, "One recording found in the performance panel.");
+   is(recordings[0].isConsole(), true, "Recording came from console.profile (1).");
+   is(recordings[0].getLabel(), "rust", "Correct label in the recording model (1).");
+   is(recordings[0].isRecording(), true, "Recording is still recording (1).");
+ 
+   let selected = getSelectedRecording(panel);
+   is(selected, recordings[0],
+     "The profile from console should be selected as it's the only one.");
+   is(selected.getLabel(), "rust",
+     "The profile label for the first recording is correct.");
+ 
+   // Ensure overview is still rendering.
+-  yield times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
++  await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
+     expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
+   });
+ 
+   let stopped = waitForRecordingStoppedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profileEnd("rust");
+-  yield stopped;
++  await console.profileEnd("rust");
++  await stopped;
+ 
+   started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when an in-progress recording is selected
+     skipWaitingForOverview: true,
+     // the view state won't switch to "console-recording" unless the new
+     // in-progress recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profile("rust");
+-  yield started;
++  await console.profile("rust");
++  await started;
+ 
+   recordings = PerformanceController.getRecordings();
+   is(recordings.length, 2, "Two recordings found in the performance panel.");
+   is(recordings[1].isConsole(), true, "Recording came from console.profile (2).");
+   is(recordings[1].getLabel(), "rust", "Correct label in the recording model (2).");
+   is(recordings[1].isRecording(), true, "Recording is still recording (2).");
+ 
+   selected = getSelectedRecording(panel);
+@@ -80,13 +80,13 @@ add_task(function* () {
+ 
+   stopped = waitForRecordingStoppedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when a finished recording is selected
+     skipWaitingForOverview: true,
+     skipWaitingForSubview: true,
+   });
+-  yield console.profileEnd("rust");
+-  yield stopped;
++  await console.profileEnd("rust");
++  await stopped;
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-console-record-06.js b/devtools/client/performance/test/browser_perf-console-record-06.js
+--- a/devtools/client/performance/test/browser_perf-console-record-06.js
++++ b/devtools/client/performance/test/browser_perf-console-record-06.js
+@@ -8,89 +8,89 @@
+ 
+ const { Constants } = require("devtools/client/performance/modules/constants");
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInTab, initConsoleInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { waitForRecordingStartedEvents, waitForRecordingStoppedEvents } = require("devtools/client/performance/test/helpers/actions");
+ const { times } = require("devtools/client/performance/test/helpers/event-utils");
+ const { getSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { target, console } = yield initConsoleInNewTab({
++add_task(async function() {
++  let { target, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+   let { EVENTS, PerformanceController, OverviewView } = panel.panelWin;
+ 
+   let started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profile("rust");
+-  yield started;
++  await console.profile("rust");
++  await started;
+ 
+   let recordings = PerformanceController.getRecordings();
+   is(recordings.length, 1, "A recording found in the performance panel.");
+   is(getSelectedRecording(panel), recordings[0],
+     "The first console recording should be selected.");
+ 
+   // Ensure overview is still rendering.
+-  yield times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
++  await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
+     expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
+   });
+ 
+   started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when an in-progress recording is selected
+     skipWaitingForOverview: true,
+     // the view state won't switch to "console-recording" unless the new
+     // in-progress recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profile("golang");
+-  yield started;
++  await console.profile("golang");
++  await started;
+ 
+   recordings = PerformanceController.getRecordings();
+   is(recordings.length, 2, "Two recordings found in the performance panel.");
+   is(getSelectedRecording(panel), recordings[0],
+     "The first console recording should still be selected.");
+ 
+   // Ensure overview is still rendering.
+-  yield times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
++  await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
+     expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
+   });
+ 
+   let stopped = waitForRecordingStoppedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profileEnd("rust");
+-  yield stopped;
++  await console.profileEnd("rust");
++  await stopped;
+ 
+   recordings = PerformanceController.getRecordings();
+   is(recordings.length, 2, "Two recordings found in the performance panel.");
+   is(getSelectedRecording(panel), recordings[0],
+     "The first console recording should still be selected.");
+   is(recordings[0].isRecording(), false,
+     "The first console recording should no longer be recording.");
+ 
+   stopped = waitForRecordingStoppedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when a finished recording is selected
+     skipWaitingForOverview: true,
+     skipWaitingForSubview: true,
+   });
+-  yield console.profileEnd("golang");
+-  yield stopped;
++  await console.profileEnd("golang");
++  await stopped;
+ 
+   recordings = PerformanceController.getRecordings();
+   is(recordings.length, 2, "Two recordings found in the performance panel.");
+   is(getSelectedRecording(panel), recordings[0],
+     "The first console recording should still be selected.");
+   is(recordings[1].isRecording(), false,
+     "The second console recording should no longer be recording.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-console-record-07.js b/devtools/client/performance/test/browser_perf-console-record-07.js
+--- a/devtools/client/performance/test/browser_perf-console-record-07.js
++++ b/devtools/client/performance/test/browser_perf-console-record-07.js
+@@ -9,55 +9,55 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInTab, initConsoleInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { waitForRecordingStartedEvents, waitForRecordingStoppedEvents } = require("devtools/client/performance/test/helpers/actions");
+ const { idleWait } = require("devtools/client/performance/test/helpers/wait-utils");
+ const { getSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { target, console } = yield initConsoleInNewTab({
++add_task(async function() {
++  let { target, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+   let { PerformanceController } = panel.panelWin;
+ 
+   let started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profile();
+-  yield started;
++  await console.profile();
++  await started;
+ 
+   started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when an in-progress recording is selected
+     skipWaitingForOverview: true,
+     // the view state won't switch to "console-recording" unless the new
+     // in-progress recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profile("1");
+-  yield started;
++  await console.profile("1");
++  await started;
+ 
+   started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when an in-progress recording is selected
+     skipWaitingForOverview: true,
+     // the view state won't switch to "console-recording" unless the new
+     // in-progress recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profile("2");
+-  yield started;
++  await console.profile("2");
++  await started;
+ 
+   let recordings = PerformanceController.getRecordings();
+   let selected = getSelectedRecording(panel);
+   is(recordings.length, 3, "Three recordings found in the performance panel.");
+   is(recordings[0].getLabel(), "", "Checking label of recording 1");
+   is(recordings[1].getLabel(), "1", "Checking label of recording 2");
+   is(recordings[2].getLabel(), "2", "Checking label of recording 3");
+   is(selected, recordings[0],
+@@ -75,35 +75,35 @@ add_task(function* () {
+     skipWaitingForBackendReady: true,
+     // only emitted when a finished recording is selected
+     skipWaitingForOverview: true,
+     skipWaitingForSubview: true,
+     // the view state won't switch to "recorded" unless the new
+     // finished recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profileEnd();
+-  yield stopped;
++  await console.profileEnd();
++  await stopped;
+ 
+   selected = getSelectedRecording(panel);
+   recordings = PerformanceController.getRecordings();
+   is(recordings.length, 3, "Three recordings found in the performance panel.");
+   is(selected, recordings[0],
+     "The first console recording should still be selected.");
+ 
+   is(recordings[0].isRecording(), true, "The not most recent recording should not stop " +
+     "when calling console.profileEnd with no args.");
+   is(recordings[1].isRecording(), true, "The not most recent recording should not stop " +
+     "when calling console.profileEnd with no args.");
+   is(recordings[2].isRecording(), false, "Only the most recent recording should stop " +
+     "when calling console.profileEnd with no args.");
+ 
+   info("Trying to `profileEnd` a non-existent console recording.");
+   console.profileEnd("fxos");
+-  yield idleWait(1000);
++  await idleWait(1000);
+ 
+   selected = getSelectedRecording(panel);
+   recordings = PerformanceController.getRecordings();
+   is(recordings.length, 3, "Three recordings found in the performance panel.");
+   is(selected, recordings[0],
+     "The first console recording should still be selected.");
+ 
+   is(recordings[0].isRecording(), true,
+@@ -118,18 +118,18 @@ add_task(function* () {
+     skipWaitingForBackendReady: true,
+     // only emitted when a finished recording is selected
+     skipWaitingForOverview: true,
+     skipWaitingForSubview: true,
+     // the view state won't switch to "recorded" unless the new
+     // finished recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profileEnd();
+-  yield stopped;
++  await console.profileEnd();
++  await stopped;
+ 
+   selected = getSelectedRecording(panel);
+   recordings = PerformanceController.getRecordings();
+   is(recordings.length, 3, "Three recordings found in the performance panel.");
+   is(selected, recordings[0],
+     "The first console recording should still be selected.");
+ 
+   is(recordings[0].isRecording(), true,
+@@ -138,33 +138,33 @@ add_task(function* () {
+     "The second recording should not be ended yet.");
+   is(recordings[2].isRecording(), false,
+     "The third recording should still be ended.");
+ 
+   stopped = waitForRecordingStoppedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profileEnd();
+-  yield stopped;
++  await console.profileEnd();
++  await stopped;
+ 
+   selected = getSelectedRecording(panel);
+   recordings = PerformanceController.getRecordings();
+   is(recordings.length, 3, "Three recordings found in the performance panel.");
+   is(selected, recordings[0],
+     "The first console recording should be selected.");
+ 
+   is(recordings[0].isRecording(), false,
+     "All recordings should now be ended. (1)");
+   is(recordings[1].isRecording(), false,
+     "All recordings should now be ended. (2)");
+   is(recordings[2].isRecording(), false,
+     "All recordings should now be ended. (3)");
+ 
+   info("Trying to `profileEnd` with no pending recordings.");
+   console.profileEnd();
+-  yield idleWait(1000);
++  await idleWait(1000);
+ 
+   ok(true, "Calling console.profileEnd() with no argument and no pending recordings " +
+     "does not throw.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-console-record-08.js b/devtools/client/performance/test/browser_perf-console-record-08.js
+--- a/devtools/client/performance/test/browser_perf-console-record-08.js
++++ b/devtools/client/performance/test/browser_perf-console-record-08.js
+@@ -34,76 +34,76 @@ const SELECTED = 4;
+  * match for the recording state.
+  * @param {integer} expected - The expected bit values packed in an integer.
+  * @param {integer} actual - The actual bit values packed in an integer.
+  */
+ function hasBitFlag(expected, actual) {
+   return !!(expected & actual);
+ }
+ 
+-add_task(function* () {
++add_task(async function() {
+   // This test seems to take a very long time to finish on Linux VMs.
+   requestLongerTimeout(4);
+ 
+-  let { target, console } = yield initConsoleInNewTab({
++  let { target, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+   let { EVENTS, PerformanceController, OverviewView } = panel.panelWin;
+ 
+   info("Recording 1 - Starting console.profile()...");
+   let started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profile("rust");
+-  yield started;
++  await console.profile("rust");
++  await started;
+   testRecordings(PerformanceController, [
+     CONSOLE + SELECTED + RECORDING
+   ]);
+ 
+   info("Recording 2 - Starting manual recording...");
+-  yield startRecording(panel);
++  await startRecording(panel);
+   testRecordings(PerformanceController, [
+     CONSOLE + RECORDING,
+     MANUAL + RECORDING + SELECTED
+   ]);
+ 
+   info("Recording 3 - Starting console.profile(\"3\")...");
+   started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when an in-progress recording is selected
+     skipWaitingForOverview: true,
+     // the view state won't switch to "console-recording" unless the new
+     // in-progress recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profile("3");
+-  yield started;
++  await console.profile("3");
++  await started;
+   testRecordings(PerformanceController, [
+     CONSOLE + RECORDING,
+     MANUAL + RECORDING + SELECTED,
+     CONSOLE + RECORDING
+   ]);
+ 
+   info("Recording 4 - Starting console.profile(\"4\")...");
+   started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when an in-progress  recording is selected
+     skipWaitingForOverview: true,
+     // the view state won't switch to "console-recording" unless the new
+     // in-progress recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profile("4");
+-  yield started;
++  await console.profile("4");
++  await started;
+   testRecordings(PerformanceController, [
+     CONSOLE + RECORDING,
+     MANUAL + RECORDING + SELECTED,
+     CONSOLE + RECORDING,
+     CONSOLE + RECORDING
+   ]);
+ 
+   info("Recording 4 - Ending console.profileEnd()...");
+@@ -112,114 +112,114 @@ add_task(function* () {
+     skipWaitingForBackendReady: true,
+     // only emitted when a finished recording is selected
+     skipWaitingForOverview: true,
+     skipWaitingForSubview: true,
+     // the view state won't switch to "recorded" unless the new
+     // finished recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profileEnd();
+-  yield stopped;
++  await console.profileEnd();
++  await stopped;
+   testRecordings(PerformanceController, [
+     CONSOLE + RECORDING,
+     MANUAL + RECORDING + SELECTED,
+     CONSOLE + RECORDING,
+     CONSOLE
+   ]);
+ 
+   info("Recording 4 - Select last recording...");
+   let recordingSelected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 3);
+-  yield recordingSelected;
++  await recordingSelected;
+   testRecordings(PerformanceController, [
+     CONSOLE + RECORDING,
+     MANUAL + RECORDING,
+     CONSOLE + RECORDING,
+     CONSOLE + SELECTED
+   ]);
+   ok(!OverviewView.isRendering(),
+     "Stop rendering overview when a completed recording is selected.");
+ 
+   info("Recording 2 - Stop manual recording.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+   testRecordings(PerformanceController, [
+     CONSOLE + RECORDING,
+     MANUAL + SELECTED,
+     CONSOLE + RECORDING,
+     CONSOLE
+   ]);
+   ok(!OverviewView.isRendering(),
+     "Stop rendering overview when a completed recording is selected.");
+ 
+   info("Recording 1 - Select first recording.");
+   recordingSelected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 0);
+-  yield recordingSelected;
++  await recordingSelected;
+   testRecordings(PerformanceController, [
+     CONSOLE + RECORDING + SELECTED,
+     MANUAL,
+     CONSOLE + RECORDING,
+     CONSOLE
+   ]);
+   ok(OverviewView.isRendering(),
+     "Should be rendering overview a recording in progress is selected.");
+ 
+   // Ensure overview is still rendering.
+-  yield times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
++  await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
+     expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
+   });
+ 
+   info("Ending console.profileEnd()...");
+   stopped = waitForRecordingStoppedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when a finished recording is selected
+     skipWaitingForOverview: true,
+     skipWaitingForSubview: true,
+     // the view state won't switch to "recorded" unless the new
+     // finished recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profileEnd();
+-  yield stopped;
++  await console.profileEnd();
++  await stopped;
+   testRecordings(PerformanceController, [
+     CONSOLE + RECORDING + SELECTED,
+     MANUAL,
+     CONSOLE,
+     CONSOLE
+   ]);
+   ok(OverviewView.isRendering(),
+     "Should be rendering overview a recording in progress is selected.");
+ 
+   // Ensure overview is still rendering.
+-  yield times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
++  await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
+     expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
+   });
+ 
+   info("Recording 5 - Start one more manual recording.");
+-  yield startRecording(panel);
++  await startRecording(panel);
+   testRecordings(PerformanceController, [
+     CONSOLE + RECORDING,
+     MANUAL,
+     CONSOLE,
+     CONSOLE,
+     MANUAL + RECORDING + SELECTED
+   ]);
+   ok(OverviewView.isRendering(),
+     "Should be rendering overview a recording in progress is selected.");
+ 
+   // Ensure overview is still rendering.
+-  yield times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
++  await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
+     expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
+   });
+ 
+   info("Recording 5 - Stop manual recording.");
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+   testRecordings(PerformanceController, [
+     CONSOLE + RECORDING,
+     MANUAL,
+     CONSOLE,
+     CONSOLE,
+     MANUAL + SELECTED
+   ]);
+   ok(!OverviewView.isRendering(),
+@@ -231,29 +231,29 @@ add_task(function* () {
+     skipWaitingForBackendReady: true,
+     // only emitted when a finished recording is selected
+     skipWaitingForOverview: true,
+     skipWaitingForSubview: true,
+     // the view state won't switch to "recorded" unless the new
+     // in-progress recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profileEnd();
+-  yield stopped;
++  await console.profileEnd();
++  await stopped;
+   testRecordings(PerformanceController, [
+     CONSOLE,
+     MANUAL,
+     CONSOLE,
+     CONSOLE,
+     MANUAL + SELECTED
+   ]);
+   ok(!OverviewView.isRendering(),
+     "Stop rendering overview when a completed recording is selected.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+ 
+ function testRecordings(controller, expectedBitFlags) {
+   let recordings = controller.getRecordings();
+   let current = controller.getCurrentRecording();
+   is(recordings.length, expectedBitFlags.length, "Expected number of recordings.");
+ 
+   recordings.forEach((recording, i) => {
+diff --git a/devtools/client/performance/test/browser_perf-console-record-09.js b/devtools/client/performance/test/browser_perf-console-record-09.js
+--- a/devtools/client/performance/test/browser_perf-console-record-09.js
++++ b/devtools/client/performance/test/browser_perf-console-record-09.js
+@@ -9,56 +9,56 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInTab, initConsoleInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { waitForRecordingStartedEvents } = require("devtools/client/performance/test/helpers/actions");
+ const { idleWait } = require("devtools/client/performance/test/helpers/wait-utils");
+ 
+-add_task(function* () {
+-  let { target, console } = yield initConsoleInNewTab({
++add_task(async function() {
++  let { target, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+   let { PerformanceController } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   info("Starting console.profile()...");
+   let started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true,
+     // only emitted when an in-progress recording is selected
+     skipWaitingForOverview: true,
+     // the view state won't switch to "console-recording" unless the new
+     // in-progress recording is selected, which won't happen
+     skipWaitingForViewState: true,
+   });
+-  yield console.profile();
+-  yield started;
++  await console.profile();
++  await started;
+ 
+-  yield PerformanceController.clearRecordings();
++  await PerformanceController.clearRecordings();
+   let recordings = PerformanceController.getRecordings();
+   is(recordings.length, 1, "One recording found in the performance panel.");
+   is(recordings[0].isConsole(), true, "Recording came from console.profile.");
+   is(recordings[0].getLabel(), "", "Correct label in the recording model.");
+   is(PerformanceController.getCurrentRecording(), recordings[0],
+     "There current recording should be the first one.");
+ 
+   info("Attempting to end console.profileEnd()...");
+-  yield console.profileEnd();
+-  yield idleWait(1000);
++  await console.profileEnd();
++  await idleWait(1000);
+ 
+   ok(true,
+     "Stopping an in-progress console profile after clearing recordings does not throw.");
+ 
+-  yield PerformanceController.clearRecordings();
++  await PerformanceController.clearRecordings();
+   recordings = PerformanceController.getRecordings();
+   is(recordings.length, 0, "No recordings found");
+   is(PerformanceController.getCurrentRecording(), null,
+     "There should be no current recording.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-details-01-toggle.js b/devtools/client/performance/test/browser_perf-details-01-toggle.js
+--- a/devtools/client/performance/test/browser_perf-details-01-toggle.js
++++ b/devtools/client/performance/test/browser_perf-details-01-toggle.js
+@@ -7,53 +7,53 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { command } = require("devtools/client/performance/test/helpers/input-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, $, DetailsView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   info("Checking views on startup.");
+   checkViews(DetailsView, $, "waterfall");
+ 
+   // Select calltree view.
+   let viewChanged = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED,
+                          { spreadArgs: true });
+   command($("toolbarbutton[data-view='js-calltree']"));
+-  let [, viewName] = yield viewChanged;
++  let [, viewName] = await viewChanged;
+   is(viewName, "js-calltree", "UI_DETAILS_VIEW_SELECTED fired with view name");
+   checkViews(DetailsView, $, "js-calltree");
+ 
+   // Select js flamegraph view.
+   viewChanged = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED, { spreadArgs: true });
+   command($("toolbarbutton[data-view='js-flamegraph']"));
+-  [, viewName] = yield viewChanged;
++  [, viewName] = await viewChanged;
+   is(viewName, "js-flamegraph", "UI_DETAILS_VIEW_SELECTED fired with view name");
+   checkViews(DetailsView, $, "js-flamegraph");
+ 
+   // Select waterfall view.
+   viewChanged = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED, { spreadArgs: true });
+   command($("toolbarbutton[data-view='waterfall']"));
+-  [, viewName] = yield viewChanged;
++  [, viewName] = await viewChanged;
+   is(viewName, "waterfall", "UI_DETAILS_VIEW_SELECTED fired with view name");
+   checkViews(DetailsView, $, "waterfall");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+ 
+ function checkViews(DetailsView, $, currentView) {
+   for (let viewName in DetailsView.components) {
+     let button = $(`toolbarbutton[data-view="${viewName}"]`);
+ 
+     is(DetailsView.el.selectedPanel.id, DetailsView.components[currentView].id,
+       `DetailsView correctly has ${currentView} selected.`);
+diff --git a/devtools/client/performance/test/browser_perf-details-02-utility-fun.js b/devtools/client/performance/test/browser_perf-details-02-utility-fun.js
+--- a/devtools/client/performance/test/browser_perf-details-02-utility-fun.js
++++ b/devtools/client/performance/test/browser_perf-details-02-utility-fun.js
+@@ -6,54 +6,54 @@
+  * Tests that the details view utility functions work as advertised.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let {
+     EVENTS,
+     DetailsView,
+     WaterfallView,
+     JsCallTreeView,
+     JsFlameGraphView
+   } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   ok(DetailsView.isViewSelected(WaterfallView),
+     "The waterfall view is selected by default in the details view.");
+ 
+   // Select js calltree view.
+   let selected = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield selected;
++  await DetailsView.selectView("js-calltree");
++  await selected;
+ 
+   ok(DetailsView.isViewSelected(JsCallTreeView),
+     "The js calltree view is now selected in the details view.");
+ 
+   // Select js flamegraph view.
+   selected = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED);
+-  yield DetailsView.selectView("js-flamegraph");
+-  yield selected;
++  await DetailsView.selectView("js-flamegraph");
++  await selected;
+ 
+   ok(DetailsView.isViewSelected(JsFlameGraphView),
+     "The js flamegraph view is now selected in the details view.");
+ 
+   // Select waterfall view.
+   selected = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED);
+-  yield DetailsView.selectView("waterfall");
+-  yield selected;
++  await DetailsView.selectView("waterfall");
++  await selected;
+ 
+   ok(DetailsView.isViewSelected(WaterfallView),
+     "The waterfall view is now selected in the details view.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-details-03-without-allocations.js b/devtools/client/performance/test/browser_perf-details-03-without-allocations.js
+--- a/devtools/client/performance/test/browser_perf-details-03-without-allocations.js
++++ b/devtools/client/performance/test/browser_perf-details-03-without-allocations.js
+@@ -11,18 +11,18 @@
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_ALLOCATIONS_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { setSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let {
+     EVENTS,
+     $,
+     DetailsView,
+@@ -32,96 +32,96 @@ add_task(function* () {
+   } = panel.panelWin;
+ 
+   let flameBtn = $("toolbarbutton[data-view='memory-flamegraph']");
+   let callBtn = $("toolbarbutton[data-view='memory-calltree']");
+ 
+   // Disable allocations to prevent recording them.
+   Services.prefs.setBoolPref(UI_ENABLE_ALLOCATIONS_PREF, false);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   ok(DetailsView.isViewSelected(WaterfallView),
+     "The waterfall view is selected by default in the details view.");
+ 
+   // Re-enable allocations to test.
+   Services.prefs.setBoolPref(UI_ENABLE_ALLOCATIONS_PREF, true);
+ 
+   // The toolbar buttons will always be hidden when a recording isn't available,
+   // so make sure we have one that's finished.
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   ok(DetailsView.isViewSelected(WaterfallView),
+     "The waterfall view is still selected in the details view.");
+ 
+   is(callBtn.hidden, false,
+     "The `memory-calltree` button is shown when recording has memory data.");
+   is(flameBtn.hidden, false,
+     "The `memory-flamegraph` button is shown when recording has memory data.");
+ 
+   let selected = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED);
+   let rendered = once(MemoryCallTreeView, EVENTS.UI_MEMORY_CALL_TREE_RENDERED);
+   DetailsView.selectView("memory-calltree");
+-  yield selected;
+-  yield rendered;
++  await selected;
++  await rendered;
+ 
+   ok(DetailsView.isViewSelected(MemoryCallTreeView),
+     "The memory call tree view can now be selected.");
+ 
+   selected = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED);
+   rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+   DetailsView.selectView("memory-flamegraph");
+-  yield selected;
+-  yield rendered;
++  await selected;
++  await rendered;
+ 
+   ok(DetailsView.isViewSelected(MemoryFlameGraphView),
+     "The memory flamegraph view can now be selected.");
+ 
+   // Select the first recording with no memory data.
+   selected = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED);
+   rendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+   setSelectedRecording(panel, 0);
+-  yield selected;
+-  yield rendered;
++  await selected;
++  await rendered;
+ 
+   ok(DetailsView.isViewSelected(WaterfallView), "The waterfall view is now selected " +
+     "when switching back to a recording that does not have memory data.");
+ 
+   is(callBtn.hidden, true,
+     "The `memory-calltree` button is hidden when recording has no memory data.");
+   is(flameBtn.hidden, true,
+     "The `memory-flamegraph` button is hidden when recording has no memory data.");
+ 
+   // Go back to the recording with memory data.
+   rendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+   setSelectedRecording(panel, 1);
+-  yield rendered;
++  await rendered;
+ 
+   ok(DetailsView.isViewSelected(WaterfallView),
+     "The waterfall view is still selected in the details view.");
+ 
+   is(callBtn.hidden, false,
+     "The `memory-calltree` button is shown when recording has memory data.");
+   is(flameBtn.hidden, false,
+     "The `memory-flamegraph` button is shown when recording has memory data.");
+ 
+   selected = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED);
+   rendered = once(MemoryCallTreeView, EVENTS.UI_MEMORY_CALL_TREE_RENDERED);
+   DetailsView.selectView("memory-calltree");
+-  yield selected;
+-  yield rendered;
++  await selected;
++  await rendered;
+ 
+   ok(DetailsView.isViewSelected(MemoryCallTreeView), "The memory call tree view can be " +
+     "selected again after going back to the view with memory data.");
+ 
+   selected = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED);
+   rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+   DetailsView.selectView("memory-flamegraph");
+-  yield selected;
+-  yield rendered;
++  await selected;
++  await rendered;
+ 
+   ok(DetailsView.isViewSelected(MemoryFlameGraphView), "The memory flamegraph view can " +
+     "be selected again after going back to the view with memory data.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-details-04-toolbar-buttons.js b/devtools/client/performance/test/browser_perf-details-04-toolbar-buttons.js
+--- a/devtools/client/performance/test/browser_perf-details-04-toolbar-buttons.js
++++ b/devtools/client/performance/test/browser_perf-details-04-toolbar-buttons.js
+@@ -8,18 +8,18 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { setSelectedRecording, getSelectedRecordingIndex } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let {
+     EVENTS,
+     $,
+     PerformanceController,
+@@ -38,60 +38,60 @@ add_task(function* () {
+     "The `js-flamegraph` button is hidden when tool starts.");
+   is(jsCallBtn.hidden, true,
+     "The `js-calltree` button is hidden when tool starts.");
+   is(memFlameBtn.hidden, true,
+     "The `memory-flamegraph` button is hidden when tool starts.");
+   is(memCallBtn.hidden, true,
+     "The `memory-calltree` button is hidden when tool starts.");
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   is(waterfallBtn.hidden, true,
+     "The `waterfall` button is hidden when recording starts.");
+   is(jsFlameBtn.hidden, true,
+     "The `js-flamegraph` button is hidden when recording starts.");
+   is(jsCallBtn.hidden, true,
+     "The `js-calltree` button is hidden when recording starts.");
+   is(memFlameBtn.hidden, true,
+     "The `memory-flamegraph` button is hidden when recording starts.");
+   is(memCallBtn.hidden, true,
+     "The `memory-calltree` button is hidden when recording starts.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   is(waterfallBtn.hidden, false,
+     "The `waterfall` button is visible when recording ends.");
+   is(jsFlameBtn.hidden, false,
+     "The `js-flamegraph` button is visible when recording ends.");
+   is(jsCallBtn.hidden, false,
+     "The `js-calltree` button is visible when recording ends.");
+   is(memFlameBtn.hidden, true,
+     "The `memory-flamegraph` button is hidden when recording ends.");
+   is(memCallBtn.hidden, true,
+     "The `memory-calltree` button is hidden when recording ends.");
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   is(waterfallBtn.hidden, true,
+     "The `waterfall` button is hidden when another recording starts.");
+   is(jsFlameBtn.hidden, true,
+     "The `js-flamegraph` button is hidden when another recording starts.");
+   is(jsCallBtn.hidden, true,
+     "The `js-calltree` button is hidden when another recording starts.");
+   is(memFlameBtn.hidden, true,
+     "The `memory-flamegraph` button is hidden when another recording starts.");
+   is(memCallBtn.hidden, true,
+     "The `memory-calltree` button is hidden when another recording starts.");
+ 
+   let selected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   let rendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+   setSelectedRecording(panel, 0);
+-  yield selected;
+-  yield rendered;
++  await selected;
++  await rendered;
+ 
+   let selectedIndex = getSelectedRecordingIndex(panel);
+   is(selectedIndex, 0,
+     "The first recording was selected again.");
+ 
+   is(waterfallBtn.hidden, false,
+     "The `waterfall` button is visible when first recording selected.");
+   is(jsFlameBtn.hidden, false,
+@@ -100,17 +100,17 @@ add_task(function* () {
+     "The `js-calltree` button is visible when first recording selected.");
+   is(memFlameBtn.hidden, true,
+     "The `memory-flamegraph` button is hidden when first recording selected.");
+   is(memCallBtn.hidden, true,
+     "The `memory-calltree` button is hidden when first recording selected.");
+ 
+   selected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 1);
+-  yield selected;
++  await selected;
+ 
+   selectedIndex = getSelectedRecordingIndex(panel);
+   is(selectedIndex, 1,
+     "The second recording was selected again.");
+ 
+   is(waterfallBtn.hidden, true,
+     "The `waterfall button` still is hidden when second recording selected.");
+   is(jsFlameBtn.hidden, true,
+@@ -118,28 +118,28 @@ add_task(function* () {
+   is(jsCallBtn.hidden, true,
+     "The `js-calltree button` still is hidden when second recording selected.");
+   is(memFlameBtn.hidden, true,
+     "The `memory-flamegraph button` still is hidden when second recording selected.");
+   is(memCallBtn.hidden, true,
+     "The `memory-calltree button` still is hidden when second recording selected.");
+ 
+   rendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+-  yield stopRecording(panel);
+-  yield rendered;
++  await stopRecording(panel);
++  await rendered;
+ 
+   selectedIndex = getSelectedRecordingIndex(panel);
+   is(selectedIndex, 1,
+     "The second recording is still selected.");
+ 
+   is(waterfallBtn.hidden, false,
+     "The `waterfall` button is visible when second recording finished.");
+   is(jsFlameBtn.hidden, false,
+     "The `js-flamegraph` button is visible when second recording finished.");
+   is(jsCallBtn.hidden, false,
+     "The `js-calltree` button is visible when second recording finished.");
+   is(memFlameBtn.hidden, true,
+     "The `memory-flamegraph` button is hidden when second recording finished.");
+   is(memCallBtn.hidden, true,
+     "The `memory-calltree` button is hidden when second recording finished.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-details-05-preserve-view.js b/devtools/client/performance/test/browser_perf-details-05-preserve-view.js
+--- a/devtools/client/performance/test/browser_perf-details-05-preserve-view.js
++++ b/devtools/client/performance/test/browser_perf-details-05-preserve-view.js
+@@ -7,44 +7,44 @@
+  * and a new recording starts.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, PerformanceController, DetailsView, JsCallTreeView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let selected = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED);
+   let rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield selected;
+-  yield rendered;
++  await DetailsView.selectView("js-calltree");
++  await selected;
++  await rendered;
+ 
+   ok(DetailsView.isViewSelected(JsCallTreeView),
+     "The js calltree view is now selected in the details view.");
+ 
+   let cleared = once(PerformanceController, EVENTS.RECORDING_SELECTED,
+                      { expectedArgs: { "1": null } });
+-  yield PerformanceController.clearRecordings();
+-  yield cleared;
++  await PerformanceController.clearRecordings();
++  await cleared;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel, {
++  await startRecording(panel);
++  await stopRecording(panel, {
+     expectedViewClass: "JsCallTreeView",
+     expectedViewEvent: "UI_JS_CALL_TREE_RENDERED"
+   });
+ 
+   ok(DetailsView.isViewSelected(JsCallTreeView),
+     "The js calltree view is still selected in the details view.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-details-06-rerender-on-selection.js b/devtools/client/performance/test/browser_perf-details-06-rerender-on-selection.js
+--- a/devtools/client/performance/test/browser_perf-details-06-rerender-on-selection.js
++++ b/devtools/client/performance/test/browser_perf-details-06-rerender-on-selection.js
+@@ -8,49 +8,49 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { scrollCanvasGraph, HORIZONTAL_AXIS } = require("devtools/client/performance/test/helpers/input-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let {
+     EVENTS,
+     OverviewView,
+     DetailsView,
+     WaterfallView,
+     JsCallTreeView,
+     JsFlameGraphView
+   } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let waterfallRendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+   OverviewView.setTimeInterval({ startTime: 10, endTime: 20 });
+-  yield waterfallRendered;
++  await waterfallRendered;
+ 
+   // Select the call tree to make sure it's initialized and ready to receive
+   // redrawing requests once reselected.
+   let callTreeRendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield callTreeRendered;
++  await DetailsView.selectView("js-calltree");
++  await callTreeRendered;
+ 
+   // Switch to the flamegraph and perform a scroll over the visualization.
+   // The waterfall and call tree should get rerendered when reselected.
+   let flamegraphRendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+-  yield DetailsView.selectView("js-flamegraph");
+-  yield flamegraphRendered;
++  await DetailsView.selectView("js-flamegraph");
++  await flamegraphRendered;
+ 
+   let overviewRangeSelected = once(OverviewView, EVENTS.UI_OVERVIEW_RANGE_SELECTED);
+   let waterfallRerendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+   let callTreeRerendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+ 
+   once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED).then(() => {
+     ok(false, "FlameGraphView should not publicly rerender, the internal state " +
+               "and drawing should be handled by the underlying widget.");
+@@ -59,21 +59,21 @@ add_task(function* () {
+   // Reset the range to full view, trigger a "selection" event as if
+   // our mouse has done this
+   scrollCanvasGraph(JsFlameGraphView.graph, {
+     axis: HORIZONTAL_AXIS,
+     wheel: 200,
+     x: 10
+   });
+ 
+-  yield overviewRangeSelected;
++  await overviewRangeSelected;
+   ok(true, "Overview range was changed.");
+ 
+-  yield DetailsView.selectView("waterfall");
+-  yield waterfallRerendered;
++  await DetailsView.selectView("waterfall");
++  await waterfallRerendered;
+   ok(true, "Waterfall rerendered by flame graph changing interval.");
+ 
+-  yield DetailsView.selectView("js-calltree");
+-  yield callTreeRerendered;
++  await DetailsView.selectView("js-calltree");
++  await callTreeRerendered;
+   ok(true, "CallTree rerendered by flame graph changing interval.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-details-07-bleed-events.js b/devtools/client/performance/test/browser_perf-details-07-bleed-events.js
+--- a/devtools/client/performance/test/browser_perf-details-07-bleed-events.js
++++ b/devtools/client/performance/test/browser_perf-details-07-bleed-events.js
+@@ -6,43 +6,43 @@
+  * Tests that events don't bleed between detail views.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   // The waterfall should render by default, and we want to make
+   // sure that the render events don't bleed between detail views
+   // so test that's the case after both views have been created.
+   let callTreeRendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield callTreeRendered;
++  await DetailsView.selectView("js-calltree");
++  await callTreeRendered;
+ 
+   let waterfallSelected = once(DetailsView, EVENTS.UI_DETAILS_VIEW_SELECTED);
+-  yield DetailsView.selectView("waterfall");
+-  yield waterfallSelected;
++  await DetailsView.selectView("waterfall");
++  await waterfallSelected;
+ 
+   once(JsCallTreeView, EVENTS.UI_WATERFALL_RENDERED).then(() =>
+     ok(false, "JsCallTreeView should not receive UI_WATERFALL_RENDERED event."));
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let callTreeRerendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield callTreeRerendered;
++  await DetailsView.selectView("js-calltree");
++  await callTreeRerendered;
+ 
+   ok(true, "Test passed.");
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-details-render-00-waterfall.js b/devtools/client/performance/test/browser_perf-details-render-00-waterfall.js
+--- a/devtools/client/performance/test/browser_perf-details-render-00-waterfall.js
++++ b/devtools/client/performance/test/browser_perf-details-render-00-waterfall.js
+@@ -5,36 +5,36 @@
+ /**
+  * Tests that the waterfall view renders content after recording.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { DetailsView, WaterfallView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   // Already waits for EVENTS.UI_WATERFALL_RENDERED.
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   ok(DetailsView.isViewSelected(WaterfallView),
+     "The waterfall view is selected by default in the details view.");
+ 
+   ok(true, "WaterfallView rendered after recording is stopped.");
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   // Already waits for EVENTS.UI_WATERFALL_RENDERED.
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   ok(DetailsView.isViewSelected(WaterfallView),
+     "The waterfall view is still selected in the details view.");
+ 
+   ok(true, "WaterfallView rendered again after recording completed a second time.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-details-render-01-js-calltree.js b/devtools/client/performance/test/browser_perf-details-render-01-js-calltree.js
+--- a/devtools/client/performance/test/browser_perf-details-render-01-js-calltree.js
++++ b/devtools/client/performance/test/browser_perf-details-render-01-js-calltree.js
+@@ -6,35 +6,35 @@
+  * Tests that the js call tree view renders content after recording.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield rendered;
++  await DetailsView.selectView("js-calltree");
++  await rendered;
+ 
+   ok(true, "JsCallTreeView rendered after recording is stopped.");
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel, {
++  await startRecording(panel);
++  await stopRecording(panel, {
+     expectedViewClass: "JsCallTreeView",
+     expectedViewEvent: "UI_JS_CALL_TREE_RENDERED"
+   });
+ 
+   ok(true, "JsCallTreeView rendered again after recording completed a second time.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-details-render-02-js-flamegraph.js b/devtools/client/performance/test/browser_perf-details-render-02-js-flamegraph.js
+--- a/devtools/client/performance/test/browser_perf-details-render-02-js-flamegraph.js
++++ b/devtools/client/performance/test/browser_perf-details-render-02-js-flamegraph.js
+@@ -6,35 +6,35 @@
+  * Tests that the js flamegraph view renders content after recording.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, JsFlameGraphView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+-  yield DetailsView.selectView("js-flamegraph");
+-  yield rendered;
++  await DetailsView.selectView("js-flamegraph");
++  await rendered;
+ 
+   ok(true, "JsFlameGraphView rendered after recording is stopped.");
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel, {
++  await startRecording(panel);
++  await stopRecording(panel, {
+     expectedViewClass: "JsFlameGraphView",
+     expectedViewEvent: "UI_JS_FLAMEGRAPH_RENDERED"
+   });
+ 
+   ok(true, "JsFlameGraphView rendered again after recording completed a second time.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-details-render-03-memory-calltree.js b/devtools/client/performance/test/browser_perf-details-render-03-memory-calltree.js
+--- a/devtools/client/performance/test/browser_perf-details-render-03-memory-calltree.js
++++ b/devtools/client/performance/test/browser_perf-details-render-03-memory-calltree.js
+@@ -7,38 +7,38 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_ALLOCATIONS_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, MemoryCallTreeView } = panel.panelWin;
+ 
+   // Enable allocations to test.
+   Services.prefs.setBoolPref(UI_ENABLE_ALLOCATIONS_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(MemoryCallTreeView, EVENTS.UI_MEMORY_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("memory-calltree");
+-  yield rendered;
++  await DetailsView.selectView("memory-calltree");
++  await rendered;
+ 
+   ok(true, "MemoryCallTreeView rendered after recording is stopped.");
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel, {
++  await startRecording(panel);
++  await stopRecording(panel, {
+     expectedViewClass: "MemoryCallTreeView",
+     expectedViewEvent: "UI_MEMORY_CALL_TREE_RENDERED"
+   });
+ 
+   ok(true, "MemoryCallTreeView rendered again after recording completed a second time.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-details-render-04-memory-flamegraph.js b/devtools/client/performance/test/browser_perf-details-render-04-memory-flamegraph.js
+--- a/devtools/client/performance/test/browser_perf-details-render-04-memory-flamegraph.js
++++ b/devtools/client/performance/test/browser_perf-details-render-04-memory-flamegraph.js
+@@ -7,39 +7,39 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_ALLOCATIONS_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, MemoryFlameGraphView } = panel.panelWin;
+ 
+   // Enable allocations to test.
+   Services.prefs.setBoolPref(UI_ENABLE_ALLOCATIONS_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+-  yield DetailsView.selectView("memory-flamegraph");
+-  yield rendered;
++  await DetailsView.selectView("memory-flamegraph");
++  await rendered;
+ 
+   ok(true, "MemoryFlameGraphView rendered after recording is stopped.");
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel, {
++  await startRecording(panel);
++  await stopRecording(panel, {
+     expectedViewClass: "MemoryFlameGraphView",
+     expectedViewEvent: "UI_MEMORY_FLAMEGRAPH_RENDERED"
+   });
+ 
+   ok(true,
+     "MemoryFlameGraphView rendered again after recording completed a second time.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-docload.js b/devtools/client/performance/test/browser_perf-docload.js
+--- a/devtools/client/performance/test/browser_perf-docload.js
++++ b/devtools/client/performance/test/browser_perf-docload.js
+@@ -6,38 +6,38 @@
+  * Tests if the sidebar is updated with "DOMContentLoaded" and "load" markers.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording, reload } = require("devtools/client/performance/test/helpers/actions");
+ const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
+ 
+-add_task(function* () {
+-  let { panel, target } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel, target } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { PerformanceController } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield reload(target);
++  await startRecording(panel);
++  await reload(target);
+ 
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     // Wait until we get the necessary markers.
+     let markers = PerformanceController.getCurrentRecording().getMarkers();
+     if (!markers.some(m => m.name == "document::DOMContentLoaded") ||
+         !markers.some(m => m.name == "document::Load")) {
+       return false;
+     }
+ 
+     ok(markers.filter(m => m.name == "document::DOMContentLoaded").length == 1,
+       "There should only be one `DOMContentLoaded` marker.");
+     ok(markers.filter(m => m.name == "document::Load").length == 1,
+       "There should only be one `load` marker.");
+ 
+     return true;
+   });
+ 
+-  yield stopRecording(panel);
+-  yield teardownToolboxAndRemoveTab(panel);
++  await stopRecording(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-gc-snap.js b/devtools/client/performance/test/browser_perf-gc-snap.js
+--- a/devtools/client/performance/test/browser_perf-gc-snap.js
++++ b/devtools/client/performance/test/browser_perf-gc-snap.js
+@@ -1,123 +1,123 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ /* eslint-disable */
+ /**
+  * Tests that the marker details on GC markers displays allocation
+  * buttons and snaps to the correct range
+  */
+-function* spawnTest() {
+-  let { panel } = yield initPerformance(ALLOCS_URL);
++async function spawnTest() {
++  let { panel } = await initPerformance(ALLOCS_URL);
+   let { $, $$, EVENTS, PerformanceController, OverviewView, DetailsView, WaterfallView, MemoryCallTreeView } = panel.panelWin;
+   let EPSILON = 0.00001;
+ 
+   Services.prefs.setBoolPref(ALLOCATIONS_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield idleWait(1000);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await idleWait(1000);
++  await stopRecording(panel);
+ 
+   injectGCMarkers(PerformanceController, WaterfallView);
+ 
+   // Select everything
+   let rendered = WaterfallView.once(EVENTS.UI_WATERFALL_RENDERED);
+   OverviewView.setTimeInterval({ startTime: 0, endTime: Number.MAX_VALUE });
+-  yield rendered;
++  await rendered;
+ 
+   let bars = $$(".waterfall-marker-bar");
+   let gcMarkers = PerformanceController.getCurrentRecording().getMarkers();
+   ok(gcMarkers.length === 9, "should have 9 GC markers");
+   ok(bars.length === 9, "should have 9 GC markers rendered");
+ 
+   /**
+    * Check when it's the second marker of the first GC cycle.
+    */
+ 
+   let targetMarker = gcMarkers[1];
+   let targetBar = bars[1];
+   info(`Clicking GC Marker of type ${targetMarker.causeName} ${targetMarker.start}:${targetMarker.end}`);
+   EventUtils.sendMouseEvent({ type: "mousedown" }, targetBar);
+   let showAllocsButton;
+   // On slower machines this can not be found immediately?
+-  yield waitUntil(() => showAllocsButton = $("#waterfall-details .custom-button[type='show-allocations']"));
++  await waitUntil(() => showAllocsButton = $("#waterfall-details .custom-button[type='show-allocations']"));
+   ok(showAllocsButton, "GC buttons when allocations are enabled");
+ 
+   rendered = once(MemoryCallTreeView, EVENTS.UI_MEMORY_CALL_TREE_RENDERED);
+   EventUtils.sendMouseEvent({ type: "click" }, showAllocsButton);
+-  yield rendered;
++  await rendered;
+ 
+   is(OverviewView.getTimeInterval().startTime, 0, "When clicking first GC, should use 0 as start time");
+   within(OverviewView.getTimeInterval().endTime, targetMarker.start, EPSILON, "Correct end time range");
+ 
+   let duration = PerformanceController.getCurrentRecording().getDuration();
+   rendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+   OverviewView.setTimeInterval({ startTime: 0, endTime: duration });
+-  yield DetailsView.selectView("waterfall");
+-  yield rendered;
++  await DetailsView.selectView("waterfall");
++  await rendered;
+ 
+   /**
+    * Check when there is a previous GC cycle
+    */
+ 
+   bars = $$(".waterfall-marker-bar");
+   targetMarker = gcMarkers[4];
+   targetBar = bars[4];
+ 
+   info(`Clicking GC Marker of type ${targetMarker.causeName} ${targetMarker.start}:${targetMarker.end}`);
+   EventUtils.sendMouseEvent({ type: "mousedown" }, targetBar);
+   // On slower machines this can not be found immediately?
+-  yield waitUntil(() => showAllocsButton = $("#waterfall-details .custom-button[type='show-allocations']"));
++  await waitUntil(() => showAllocsButton = $("#waterfall-details .custom-button[type='show-allocations']"));
+   ok(showAllocsButton, "GC buttons when allocations are enabled");
+ 
+   rendered = once(MemoryCallTreeView, EVENTS.UI_MEMORY_CALL_TREE_RENDERED);
+   EventUtils.sendMouseEvent({ type: "click" }, showAllocsButton);
+-  yield rendered;
++  await rendered;
+ 
+   within(OverviewView.getTimeInterval().startTime, gcMarkers[2].end, EPSILON,
+     "selection start range is last marker from previous GC cycle.");
+   within(OverviewView.getTimeInterval().endTime, targetMarker.start, EPSILON,
+     "selection end range is current GC marker's start time");
+ 
+   /**
+    * Now with allocations disabled
+    */
+ 
+   // Reselect the entire recording -- due to bug 1196945, the new recording
+   // won't reset the selection
+   duration = PerformanceController.getCurrentRecording().getDuration();
+   rendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+   OverviewView.setTimeInterval({ startTime: 0, endTime: duration });
+-  yield rendered;
++  await rendered;
+ 
+   Services.prefs.setBoolPref(ALLOCATIONS_PREF, false);
+-  yield startRecording(panel);
++  await startRecording(panel);
+   rendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+-  yield stopRecording(panel);
+-  yield rendered;
++  await stopRecording(panel);
++  await rendered;
+ 
+   injectGCMarkers(PerformanceController, WaterfallView);
+ 
+   // Select everything
+   rendered = WaterfallView.once(EVENTS.UI_WATERFALL_RENDERED);
+   OverviewView.setTimeInterval({ startTime: 0, endTime: Number.MAX_VALUE });
+-  yield rendered;
++  await rendered;
+ 
+   ok(true, "WaterfallView rendered after recording is stopped.");
+ 
+   bars = $$(".waterfall-marker-bar");
+   gcMarkers = PerformanceController.getCurrentRecording().getMarkers();
+ 
+   EventUtils.sendMouseEvent({ type: "mousedown" }, bars[0]);
+   showAllocsButton = $("#waterfall-details .custom-button[type='show-allocations']");
+   ok(!showAllocsButton, "No GC buttons when allocations are disabled");
+ 
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ 
+ function injectGCMarkers(controller, waterfall) {
+   // Push some fake GC markers into the recording
+   let realMarkers = controller.getCurrentRecording().getMarkers();
+   // Invalidate marker cache
+   waterfall._cache.delete(realMarkers);
+diff --git a/devtools/client/performance/test/browser_perf-highlighted.js b/devtools/client/performance/test/browser_perf-highlighted.js
+--- a/devtools/client/performance/test/browser_perf-highlighted.js
++++ b/devtools/client/performance/test/browser_perf-highlighted.js
+@@ -7,42 +7,42 @@
+  * whether already loaded, or via console.profile with an unloaded performance tools.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInTab, initConsoleInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
+ 
+-add_task(function* () {
+-  let { target, toolbox, console } = yield initConsoleInNewTab({
++add_task(async function() {
++  let { target, toolbox, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let tab = toolbox.doc.getElementById("toolbox-tab-performance");
+ 
+-  yield console.profile("rust");
+-  yield waitUntil(() => tab.classList.contains("highlighted"));
++  await console.profile("rust");
++  await waitUntil(() => tab.classList.contains("highlighted"));
+ 
+   ok(tab.classList.contains("highlighted"), "Performance tab is highlighted during " +
+     "recording from console.profile when unloaded.");
+ 
+-  yield console.profileEnd("rust");
+-  yield waitUntil(() => !tab.classList.contains("highlighted"));
++  await console.profileEnd("rust");
++  await waitUntil(() => !tab.classList.contains("highlighted"));
+ 
+   ok(!tab.classList.contains("highlighted"),
+     "Performance tab is no longer highlighted when console.profile recording finishes.");
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   ok(tab.classList.contains("highlighted"),
+     "Performance tab is highlighted during recording while in performance tool.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   ok(!tab.classList.contains("highlighted"),
+     "Performance tab is no longer highlighted when recording finishes.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-loading-01.js b/devtools/client/performance/test/browser_perf-loading-01.js
+--- a/devtools/client/performance/test/browser_perf-loading-01.js
++++ b/devtools/client/performance/test/browser_perf-loading-01.js
+@@ -8,45 +8,45 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { getSelectedRecording, getDurationLabelText } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, L10N, PerformanceController } = panel.panelWin;
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   is(getDurationLabelText(panel, 0),
+     L10N.getStr("recordingsList.recordingLabel"),
+     "The duration node should show the 'recording' message while recording");
+ 
+   let recordingStopping = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
+     expectedArgs: { "1": "recording-stopping" }
+   });
+   let recordingStopped = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
+     expectedArgs: { "1": "recording-stopped" }
+   });
+   let everythingStopped = stopRecording(panel);
+ 
+-  yield recordingStopping;
++  await recordingStopping;
+   is(getDurationLabelText(panel, 0),
+     L10N.getStr("recordingsList.loadingLabel"),
+     "The duration node should show the 'loading' message while stopping");
+ 
+-  yield recordingStopped;
++  await recordingStopped;
+   const selected = getSelectedRecording(panel);
+   is(getDurationLabelText(panel, 0),
+     L10N.getFormatStr("recordingsList.durationLabel",
+     selected.getDuration().toFixed(0)),
+     "The duration node should show the duration after the record has stopped");
+ 
+-  yield everythingStopped;
+-  yield teardownToolboxAndRemoveTab(panel);
++  await everythingStopped;
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-loading-02.js b/devtools/client/performance/test/browser_perf-loading-02.js
+--- a/devtools/client/performance/test/browser_perf-loading-02.js
++++ b/devtools/client/performance/test/browser_perf-loading-02.js
+@@ -10,73 +10,73 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { getSelectedRecordingIndex, setSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, $, PerformanceController } = panel.panelWin;
+   let detailsContainer = $("#details-pane-container");
+   let recordingNotice = $("#recording-notice");
+   let loadingNotice = $("#loading-notice");
+   let detailsPane = $("#details-pane");
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   is(detailsContainer.selectedPanel, recordingNotice,
+     "The recording-notice is shown while recording.");
+ 
+   let recordingStopping = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
+     expectedArgs: { "1": "recording-stopping" }
+   });
+   let recordingStopped = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
+     expectedArgs: { "1": "recording-stopped" }
+   });
+   let everythingStopped = stopRecording(panel);
+ 
+-  yield recordingStopping;
++  await recordingStopping;
+   is(detailsContainer.selectedPanel, loadingNotice,
+     "The loading-notice is shown while the record is stopping.");
+ 
+-  yield recordingStopped;
++  await recordingStopped;
+   is(detailsContainer.selectedPanel, detailsPane,
+     "The details panel is shown after the record has stopped.");
+ 
+-  yield everythingStopped;
+-  yield startRecording(panel);
++  await everythingStopped;
++  await startRecording(panel);
+ 
+   info("While the 2nd record is still going, switch to the first one.");
+   let recordingSelected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 0);
+-  yield recordingSelected;
++  await recordingSelected;
+ 
+   recordingStopping = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
+     expectedArgs: { "1": "recording-stopping" }
+   });
+   recordingStopped = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
+     expectedArgs: { "1": "recording-stopped" }
+   });
+   everythingStopped = stopRecording(panel);
+ 
+-  yield recordingStopping;
++  await recordingStopping;
+   is(detailsContainer.selectedPanel, detailsPane,
+     "The details panel is still shown while the 2nd record is being stopped.");
+   is(getSelectedRecordingIndex(panel), 0,
+     "The first record is still selected.");
+ 
+-  yield recordingStopped;
++  await recordingStopped;
+ 
+   is(detailsContainer.selectedPanel, detailsPane,
+     "The details panel is still shown after the 2nd record has stopped.");
+   is(getSelectedRecordingIndex(panel), 1,
+     "The second record is now selected.");
+ 
+-  yield everythingStopped;
+-  yield teardownToolboxAndRemoveTab(panel);
++  await everythingStopped;
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-marker-details.js b/devtools/client/performance/test/browser_perf-marker-details.js
+--- a/devtools/client/performance/test/browser_perf-marker-details.js
++++ b/devtools/client/performance/test/browser_perf-marker-details.js
+@@ -2,42 +2,42 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ /* eslint-disable */
+ /**
+  * Tests if the Marker Details view renders all properties expected
+  * for each marker.
+  */
+ 
+-function* spawnTest() {
+-  let { target, panel } = yield initPerformance(MARKERS_URL);
++async function spawnTest() {
++  let { target, panel } = await initPerformance(MARKERS_URL);
+   let { $, $$, EVENTS, PerformanceController, OverviewView, WaterfallView } = panel.panelWin;
+ 
+   // Hijack the markers massaging part of creating the waterfall view,
+   // to prevent collapsing markers and allowing this test to verify
+   // everything individually. A better solution would be to just expand
+   // all markers first and then skip the meta nodes, but I'm lazy.
+   WaterfallView._prepareWaterfallTree = markers => {
+     return { submarkers: markers };
+   };
+ 
+   const MARKER_TYPES = [
+     "Styles", "Reflow", "ConsoleTime", "TimeStamp"
+   ];
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   ok(true, "Recording has started.");
+ 
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     // Wait until we get all the different markers.
+     let markers = PerformanceController.getCurrentRecording().getMarkers();
+     return MARKER_TYPES.every(type => markers.some(m => m.name === type));
+   });
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+   ok(true, "Recording has ended.");
+ 
+   info("No need to select everything in the timeline.");
+   info("All the markers should be displayed by default.");
+ 
+   let bars = Array.prototype.filter.call($$(".waterfall-marker-bar"),
+              (bar) => MARKER_TYPES.includes(bar.getAttribute("type")));
+   let markers = PerformanceController.getCurrentRecording().getMarkers()
+@@ -122,17 +122,17 @@ function* spawnTest() {
+       throw new Error(`No tests for ${m.name} -- should be filtered out.`);
+     }
+ 
+     if (testsDone.length === Object.keys(tests).length) {
+       break;
+     }
+   }
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ 
+ function shouldHaveStack($, type, marker) {
+   ok($(`#waterfall-details .marker-details-stack[type=${type}]`), `${marker.name} has a stack: ${type}`);
+ }
+ 
+ function shouldHaveLabel($, name, value, marker) {
+diff --git a/devtools/client/performance/test/browser_perf-options-01-toggle-throw.js b/devtools/client/performance/test/browser_perf-options-01-toggle-throw.js
+--- a/devtools/client/performance/test/browser_perf-options-01-toggle-throw.js
++++ b/devtools/client/performance/test/browser_perf-options-01-toggle-throw.js
+@@ -4,28 +4,28 @@
+ 
+ /**
+  * Tests that toggling preferences before there are any recordings does not throw.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { DetailsView, JsCallTreeView } = panel.panelWin;
+ 
+-  yield DetailsView.selectView("js-calltree");
++  await DetailsView.selectView("js-calltree");
+ 
+   // Manually call the _onPrefChanged function so we can catch an error.
+   try {
+     JsCallTreeView._onPrefChanged(null, "invert-call-tree", true);
+     ok(true, "Toggling preferences before there are any recordings should not fail.");
+   } catch (e) {
+     ok(false, "Toggling preferences before there are any recordings should not fail.");
+   }
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-02-toggle-throw-alt.js b/devtools/client/performance/test/browser_perf-options-02-toggle-throw-alt.js
+--- a/devtools/client/performance/test/browser_perf-options-02-toggle-throw-alt.js
++++ b/devtools/client/performance/test/browser_perf-options-02-toggle-throw-alt.js
+@@ -5,34 +5,34 @@
+ /**
+  * Tests that toggling preferences during a recording does not throw.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { DetailsView, JsCallTreeView } = panel.panelWin;
+ 
+-  yield DetailsView.selectView("js-calltree");
+-  yield startRecording(panel);
++  await DetailsView.selectView("js-calltree");
++  await startRecording(panel);
+ 
+   // Manually call the _onPrefChanged function so we can catch an error.
+   try {
+     JsCallTreeView._onPrefChanged(null, "invert-call-tree", true);
+     ok(true, "Toggling preferences during a recording should not fail.");
+   } catch (e) {
+     ok(false, "Toggling preferences during a recording should not fail.");
+   }
+ 
+-  yield stopRecording(panel, {
++  await stopRecording(panel, {
+     expectedViewClass: "JsCallTreeView",
+     expectedViewEvent: "UI_JS_CALL_TREE_RENDERED"
+   });
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-03-toggle-meta.js b/devtools/client/performance/test/browser_perf-options-03-toggle-meta.js
+--- a/devtools/client/performance/test/browser_perf-options-03-toggle-meta.js
++++ b/devtools/client/performance/test/browser_perf-options-03-toggle-meta.js
+@@ -5,20 +5,20 @@
+ /**
+  * Tests that toggling meta option prefs change visibility of other options.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_EXPERIMENTAL_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ 
+-add_task(function* () {
++add_task(async function() {
+   Services.prefs.setBoolPref(UI_EXPERIMENTAL_PREF, false);
+ 
+-  let { panel } = yield initPerformanceInNewTab({
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { $ } = panel.panelWin;
+   let $body = $(".theme-body");
+   let $menu = $("#performance-options-menupopup");
+ 
+@@ -29,10 +29,10 @@ add_task(function* () {
+ 
+   Services.prefs.setBoolPref(UI_EXPERIMENTAL_PREF, true);
+ 
+   ok($body.classList.contains("experimental-enabled"),
+     "The body node has `experimental-enabled` after toggle.");
+   ok($menu.classList.contains("experimental-enabled"),
+     "The menu popup has `experimental-enabled` after toggle.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-enable-framerate-01.js b/devtools/client/performance/test/browser_perf-options-enable-framerate-01.js
+--- a/devtools/client/performance/test/browser_perf-options-enable-framerate-01.js
++++ b/devtools/client/performance/test/browser_perf-options-enable-framerate-01.js
+@@ -8,45 +8,45 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_FRAMERATE_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { isVisible } = require("devtools/client/performance/test/helpers/dom-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { $, PerformanceController } = panel.panelWin;
+ 
+   // Disable framerate to test.
+   Services.prefs.setBoolPref(UI_ENABLE_FRAMERATE_PREF, false);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   is(PerformanceController.getCurrentRecording().getConfiguration().withTicks, false,
+     "PerformanceFront started without ticks recording.");
+   ok(!isVisible($("#time-framerate")),
+     "The fps graph is hidden when ticks disabled.");
+ 
+   // Re-enable framerate.
+   Services.prefs.setBoolPref(UI_ENABLE_FRAMERATE_PREF, true);
+ 
+   is(PerformanceController.getCurrentRecording().getConfiguration().withTicks, false,
+     "PerformanceFront still marked without ticks recording.");
+   ok(!isVisible($("#time-framerate")),
+     "The fps graph is still hidden if recording does not contain ticks.");
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   is(PerformanceController.getCurrentRecording().getConfiguration().withTicks, true,
+     "PerformanceFront started with ticks recording.");
+   ok(isVisible($("#time-framerate")),
+     "The fps graph is not hidden when ticks enabled before recording.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-enable-framerate-02.js b/devtools/client/performance/test/browser_perf-options-enable-framerate-02.js
+--- a/devtools/client/performance/test/browser_perf-options-enable-framerate-02.js
++++ b/devtools/client/performance/test/browser_perf-options-enable-framerate-02.js
+@@ -7,37 +7,37 @@
+  * recording's state and does not break.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_FRAMERATE_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { PerformanceController } = panel.panelWin;
+ 
+   // Test starting without framerate, and stopping with it.
+   Services.prefs.setBoolPref(UI_ENABLE_FRAMERATE_PREF, false);
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   Services.prefs.setBoolPref(UI_ENABLE_FRAMERATE_PREF, true);
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   is(PerformanceController.getCurrentRecording().getConfiguration().withTicks, false,
+     "The recording finished without tracking framerate.");
+ 
+   // Test starting with framerate, and stopping without it.
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   Services.prefs.setBoolPref(UI_ENABLE_FRAMERATE_PREF, false);
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   is(PerformanceController.getCurrentRecording().getConfiguration().withTicks, true,
+     "The recording finished with tracking framerate.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-enable-memory-01.js b/devtools/client/performance/test/browser_perf-options-enable-memory-01.js
+--- a/devtools/client/performance/test/browser_perf-options-enable-memory-01.js
++++ b/devtools/client/performance/test/browser_perf-options-enable-memory-01.js
+@@ -8,29 +8,29 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_MEMORY_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { isVisible } = require("devtools/client/performance/test/helpers/dom-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { $, PerformanceController } = panel.panelWin;
+ 
+   // Disable memory to test.
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, false);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   is(PerformanceController.getCurrentRecording().getConfiguration().withMemory, false,
+     "PerformanceFront started without memory recording.");
+   is(PerformanceController.getCurrentRecording().getConfiguration().withAllocations,
+     false, "PerformanceFront started without allocations recording.");
+   ok(!isVisible($("#memory-overview")),
+     "The memory graph is hidden when memory disabled.");
+ 
+@@ -39,20 +39,20 @@ add_task(function* () {
+ 
+   is(PerformanceController.getCurrentRecording().getConfiguration().withMemory, false,
+     "PerformanceFront still marked without memory recording.");
+   is(PerformanceController.getCurrentRecording().getConfiguration().withAllocations,
+     false, "PerformanceFront still marked without allocations recording.");
+   ok(!isVisible($("#memory-overview")), "memory graph is still hidden after enabling " +
+                                         "if recording did not start recording memory");
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   is(PerformanceController.getCurrentRecording().getConfiguration().withMemory, true,
+     "PerformanceFront started with memory recording.");
+   is(PerformanceController.getCurrentRecording().getConfiguration().withAllocations,
+     false, "PerformanceFront did not record with allocations.");
+   ok(isVisible($("#memory-overview")),
+     "The memory graph is not hidden when memory enabled before recording.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-enable-memory-02.js b/devtools/client/performance/test/browser_perf-options-enable-memory-02.js
+--- a/devtools/client/performance/test/browser_perf-options-enable-memory-02.js
++++ b/devtools/client/performance/test/browser_perf-options-enable-memory-02.js
+@@ -7,43 +7,43 @@
+  * recording's state and does not break.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_MEMORY_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { PerformanceController } = panel.panelWin;
+ 
+   // Test starting without memory, and stopping with it.
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, false);
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, true);
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   is(PerformanceController.getCurrentRecording().getConfiguration().withMemory, false,
+     "The recording finished without tracking memory.");
+   is(PerformanceController.getCurrentRecording().getConfiguration().withAllocations,
+     false,
+     "The recording finished without tracking allocations.");
+ 
+   // Test starting with memory, and stopping without it.
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, false);
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   is(PerformanceController.getCurrentRecording().getConfiguration().withMemory, true,
+     "The recording finished with tracking memory.");
+   is(PerformanceController.getCurrentRecording().getConfiguration().withAllocations,
+     false,
+     "The recording still is not recording allocations.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-flatten-tree-recursion-01.js b/devtools/client/performance/test/browser_perf-options-flatten-tree-recursion-01.js
+--- a/devtools/client/performance/test/browser_perf-options-flatten-tree-recursion-01.js
++++ b/devtools/client/performance/test/browser_perf-options-flatten-tree-recursion-01.js
+@@ -7,67 +7,67 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_FLATTEN_RECURSION_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let {
+     EVENTS,
+     PerformanceController,
+     DetailsView,
+     JsFlameGraphView,
+     FlameGraphUtils
+   } = panel.panelWin;
+ 
+   Services.prefs.setBoolPref(UI_FLATTEN_RECURSION_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+-  yield DetailsView.selectView("js-flamegraph");
+-  yield rendered;
++  await DetailsView.selectView("js-flamegraph");
++  await rendered;
+ 
+   let thread1 = PerformanceController.getCurrentRecording().getProfile().threads[0];
+   let rendering1 = FlameGraphUtils._cache.get(thread1);
+ 
+   ok(thread1,
+     "The samples were retrieved from the controller.");
+   ok(rendering1,
+     "The rendering data was cached.");
+ 
+   rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_FLATTEN_RECURSION_PREF, false);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsFlameGraphView rerendered when toggling flatten-tree-recursion.");
+ 
+   let thread2 = PerformanceController.getCurrentRecording().getProfile().threads[0];
+   let rendering2 = FlameGraphUtils._cache.get(thread2);
+ 
+   is(thread1, thread2,
+     "The same samples data should be retrieved from the controller (1).");
+   isnot(rendering1, rendering2,
+     "The rendering data should be different because other options were used (1).");
+ 
+   rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_FLATTEN_RECURSION_PREF, true);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsFlameGraphView rerendered when toggling back flatten-tree-recursion.");
+ 
+   let thread3 = PerformanceController.getCurrentRecording().getProfile().threads[0];
+   let rendering3 = FlameGraphUtils._cache.get(thread3);
+ 
+   is(thread2, thread3,
+     "The same samples data should be retrieved from the controller (2).");
+   isnot(rendering2, rendering3,
+     "The rendering data should be different because other options were used (2).");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-flatten-tree-recursion-02.js b/devtools/client/performance/test/browser_perf-options-flatten-tree-recursion-02.js
+--- a/devtools/client/performance/test/browser_perf-options-flatten-tree-recursion-02.js
++++ b/devtools/client/performance/test/browser_perf-options-flatten-tree-recursion-02.js
+@@ -8,18 +8,18 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_FLATTEN_RECURSION_PREF, UI_ENABLE_ALLOCATIONS_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let {
+     EVENTS,
+     PerformanceController,
+     DetailsView,
+@@ -27,60 +27,60 @@ add_task(function* () {
+     RecordingUtils,
+     FlameGraphUtils
+   } = panel.panelWin;
+ 
+   // Enable memory to test
+   Services.prefs.setBoolPref(UI_ENABLE_ALLOCATIONS_PREF, true);
+   Services.prefs.setBoolPref(UI_FLATTEN_RECURSION_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+-  yield DetailsView.selectView("memory-flamegraph");
+-  yield rendered;
++  await DetailsView.selectView("memory-flamegraph");
++  await rendered;
+ 
+   let allocations1 = PerformanceController.getCurrentRecording().getAllocations();
+   let thread1 = RecordingUtils.getProfileThreadFromAllocations(allocations1);
+   let rendering1 = FlameGraphUtils._cache.get(thread1);
+ 
+   ok(allocations1,
+     "The allocations were retrieved from the controller.");
+   ok(thread1,
+     "The allocations profile was synthesized by the utility funcs.");
+   ok(rendering1,
+     "The rendering data was cached.");
+ 
+   rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_FLATTEN_RECURSION_PREF, false);
+-  yield rendered;
++  await rendered;
+   ok(true, "MemoryFlameGraphView rerendered when toggling flatten-tree-recursion.");
+ 
+   let allocations2 = PerformanceController.getCurrentRecording().getAllocations();
+   let thread2 = RecordingUtils.getProfileThreadFromAllocations(allocations2);
+   let rendering2 = FlameGraphUtils._cache.get(thread2);
+ 
+   is(allocations1, allocations2,
+     "The same allocations data should be retrieved from the controller (1).");
+   is(thread1, thread2,
+     "The same allocations profile should be retrieved from the utility funcs. (1).");
+   isnot(rendering1, rendering2,
+     "The rendering data should be different because other options were used (1).");
+ 
+   rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_FLATTEN_RECURSION_PREF, true);
+-  yield rendered;
++  await rendered;
+   ok(true, "MemoryFlameGraphView rerendered when toggling back flatten-tree-recursion.");
+ 
+   let allocations3 = PerformanceController.getCurrentRecording().getAllocations();
+   let thread3 = RecordingUtils.getProfileThreadFromAllocations(allocations3);
+   let rendering3 = FlameGraphUtils._cache.get(thread3);
+ 
+   is(allocations2, allocations3,
+     "The same allocations data should be retrieved from the controller (2).");
+   is(thread2, thread3,
+     "The same allocations profile should be retrieved from the utility funcs. (2).");
+   isnot(rendering2, rendering3,
+     "The rendering data should be different because other options were used (2).");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-invert-call-tree-01.js b/devtools/client/performance/test/browser_perf-options-invert-call-tree-01.js
+--- a/devtools/client/performance/test/browser_perf-options-invert-call-tree-01.js
++++ b/devtools/client/performance/test/browser_perf-options-invert-call-tree-01.js
+@@ -7,37 +7,37 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_INVERT_CALL_TREE_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
+ 
+   Services.prefs.setBoolPref(UI_INVERT_CALL_TREE_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield rendered;
++  await DetailsView.selectView("js-calltree");
++  await rendered;
+ 
+   rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+   Services.prefs.setBoolPref(UI_INVERT_CALL_TREE_PREF, false);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsCallTreeView rerendered when toggling invert-call-tree.");
+ 
+   rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+   Services.prefs.setBoolPref(UI_INVERT_CALL_TREE_PREF, true);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsCallTreeView rerendered when toggling back invert-call-tree.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-invert-call-tree-02.js b/devtools/client/performance/test/browser_perf-options-invert-call-tree-02.js
+--- a/devtools/client/performance/test/browser_perf-options-invert-call-tree-02.js
++++ b/devtools/client/performance/test/browser_perf-options-invert-call-tree-02.js
+@@ -7,39 +7,39 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_ALLOCATIONS_PREF, UI_INVERT_CALL_TREE_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, MemoryCallTreeView } = panel.panelWin;
+ 
+   // Enable allocations to test.
+   Services.prefs.setBoolPref(UI_ENABLE_ALLOCATIONS_PREF, true);
+   Services.prefs.setBoolPref(UI_INVERT_CALL_TREE_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(MemoryCallTreeView, EVENTS.UI_MEMORY_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("memory-calltree");
+-  yield rendered;
++  await DetailsView.selectView("memory-calltree");
++  await rendered;
+ 
+   rendered = once(MemoryCallTreeView, EVENTS.UI_MEMORY_CALL_TREE_RENDERED);
+   Services.prefs.setBoolPref(UI_INVERT_CALL_TREE_PREF, false);
+-  yield rendered;
++  await rendered;
+   ok(true, "MemoryCallTreeView rerendered when toggling invert-call-tree.");
+ 
+   rendered = once(MemoryCallTreeView, EVENTS.UI_MEMORY_CALL_TREE_RENDERED);
+   Services.prefs.setBoolPref(UI_INVERT_CALL_TREE_PREF, true);
+-  yield rendered;
++  await rendered;
+   ok(true, "MemoryCallTreeView rerendered when toggling back invert-call-tree.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-invert-flame-graph-01.js b/devtools/client/performance/test/browser_perf-options-invert-flame-graph-01.js
+--- a/devtools/client/performance/test/browser_perf-options-invert-flame-graph-01.js
++++ b/devtools/client/performance/test/browser_perf-options-invert-flame-graph-01.js
+@@ -7,37 +7,37 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_INVERT_FLAME_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, JsFlameGraphView } = panel.panelWin;
+ 
+   Services.prefs.setBoolPref(UI_INVERT_FLAME_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+-  yield DetailsView.selectView("js-flamegraph");
+-  yield rendered;
++  await DetailsView.selectView("js-flamegraph");
++  await rendered;
+ 
+   rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_INVERT_FLAME_PREF, false);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsFlameGraphView rerendered when toggling invert-call-tree.");
+ 
+   rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_INVERT_FLAME_PREF, true);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsFlameGraphView rerendered when toggling back invert-call-tree.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-invert-flame-graph-02.js b/devtools/client/performance/test/browser_perf-options-invert-flame-graph-02.js
+--- a/devtools/client/performance/test/browser_perf-options-invert-flame-graph-02.js
++++ b/devtools/client/performance/test/browser_perf-options-invert-flame-graph-02.js
+@@ -8,39 +8,39 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_ALLOCATIONS_PREF, UI_INVERT_FLAME_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, MemoryFlameGraphView } = panel.panelWin;
+ 
+   // Enable allocations to test.
+   Services.prefs.setBoolPref(UI_ENABLE_ALLOCATIONS_PREF, true);
+   Services.prefs.setBoolPref(UI_INVERT_FLAME_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+-  yield DetailsView.selectView("memory-flamegraph");
+-  yield rendered;
++  await DetailsView.selectView("memory-flamegraph");
++  await rendered;
+ 
+   rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_INVERT_FLAME_PREF, false);
+-  yield rendered;
++  await rendered;
+   ok(true, "MemoryFlameGraphView rerendered when toggling invert-call-tree.");
+ 
+   rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_INVERT_FLAME_PREF, true);
+-  yield rendered;
++  await rendered;
+   ok(true, "MemoryFlameGraphView rerendered when toggling back invert-call-tree.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-propagate-allocations.js b/devtools/client/performance/test/browser_perf-options-propagate-allocations.js
+--- a/devtools/client/performance/test/browser_perf-options-propagate-allocations.js
++++ b/devtools/client/performance/test/browser_perf-options-propagate-allocations.js
+@@ -7,30 +7,30 @@
+  * the memory actor.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { MEMORY_SAMPLE_PROB_PREF, MEMORY_MAX_LOG_LEN_PREF, UI_ENABLE_ALLOCATIONS_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel, toolbox } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel, toolbox } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   // Enable allocations to test.
+   Services.prefs.setBoolPref(UI_ENABLE_ALLOCATIONS_PREF, true);
+   Services.prefs.setCharPref(MEMORY_SAMPLE_PROB_PREF, "0.213");
+   Services.prefs.setIntPref(MEMORY_MAX_LOG_LEN_PREF, 777777);
+ 
+-  yield startRecording(panel);
+-  let { probability, maxLogLength } = yield toolbox.performance.getConfiguration();
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  let { probability, maxLogLength } = await toolbox.performance.getConfiguration();
++  await stopRecording(panel);
+ 
+   is(probability, 0.213,
+     "The allocations probability option is set on memory actor.");
+   is(maxLogLength, 777777,
+     "The allocations max log length option is set on memory actor.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-propagate-profiler.js b/devtools/client/performance/test/browser_perf-options-propagate-profiler.js
+--- a/devtools/client/performance/test/browser_perf-options-propagate-profiler.js
++++ b/devtools/client/performance/test/browser_perf-options-propagate-profiler.js
+@@ -7,26 +7,26 @@
+  * to the profiler actor.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { PROFILER_BUFFER_SIZE_PREF, PROFILER_SAMPLE_RATE_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel, toolbox } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel, toolbox } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   Services.prefs.setIntPref(PROFILER_BUFFER_SIZE_PREF, 1000);
+   Services.prefs.setIntPref(PROFILER_SAMPLE_RATE_PREF, 2);
+ 
+-  yield startRecording(panel);
+-  let { entries, interval } = yield toolbox.performance.getConfiguration();
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  let { entries, interval } = await toolbox.performance.getConfiguration();
++  await stopRecording(panel);
+ 
+   is(entries, 1000, "profiler entries option is set on profiler");
+   is(interval, 0.5, "profiler interval option is set on profiler");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-show-idle-blocks-01.js b/devtools/client/performance/test/browser_perf-options-show-idle-blocks-01.js
+--- a/devtools/client/performance/test/browser_perf-options-show-idle-blocks-01.js
++++ b/devtools/client/performance/test/browser_perf-options-show-idle-blocks-01.js
+@@ -7,37 +7,37 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_SHOW_IDLE_BLOCKS_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, JsFlameGraphView } = panel.panelWin;
+ 
+   Services.prefs.setBoolPref(UI_SHOW_IDLE_BLOCKS_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+-  yield DetailsView.selectView("js-flamegraph");
+-  yield rendered;
++  await DetailsView.selectView("js-flamegraph");
++  await rendered;
+ 
+   rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_SHOW_IDLE_BLOCKS_PREF, false);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsFlameGraphView rerendered when toggling show-idle-blocks.");
+ 
+   rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_SHOW_IDLE_BLOCKS_PREF, true);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsFlameGraphView rerendered when toggling back show-idle-blocks.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-show-idle-blocks-02.js b/devtools/client/performance/test/browser_perf-options-show-idle-blocks-02.js
+--- a/devtools/client/performance/test/browser_perf-options-show-idle-blocks-02.js
++++ b/devtools/client/performance/test/browser_perf-options-show-idle-blocks-02.js
+@@ -7,39 +7,39 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_ALLOCATIONS_PREF, UI_SHOW_IDLE_BLOCKS_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, MemoryFlameGraphView } = panel.panelWin;
+ 
+   // Enable allocations to test.
+   Services.prefs.setBoolPref(UI_ENABLE_ALLOCATIONS_PREF, true);
+   Services.prefs.setBoolPref(UI_SHOW_IDLE_BLOCKS_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+-  yield DetailsView.selectView("memory-flamegraph");
+-  yield rendered;
++  await DetailsView.selectView("memory-flamegraph");
++  await rendered;
+ 
+   rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_SHOW_IDLE_BLOCKS_PREF, false);
+-  yield rendered;
++  await rendered;
+   ok(true, "MemoryFlameGraphView rerendered when toggling show-idle-blocks.");
+ 
+   rendered = once(MemoryFlameGraphView, EVENTS.UI_MEMORY_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_SHOW_IDLE_BLOCKS_PREF, true);
+-  yield rendered;
++  await rendered;
+   ok(true, "MemoryFlameGraphView rerendered when toggling back show-idle-blocks.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-show-jit-optimizations.js b/devtools/client/performance/test/browser_perf-options-show-jit-optimizations.js
+--- a/devtools/client/performance/test/browser_perf-options-show-jit-optimizations.js
++++ b/devtools/client/performance/test/browser_perf-options-show-jit-optimizations.js
+@@ -7,112 +7,112 @@ requestLongerTimeout(2);
+ 
+ /**
+  * Tests that the JIT Optimizations view renders optimization data
+  * if on, and displays selected frames on focus.
+  */
+  const { setSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ Services.prefs.setBoolPref(INVERT_PREF, false);
+ 
+-function* spawnTest() {
+-  let { panel } = yield initPerformance(SIMPLE_URL);
++async function spawnTest() {
++  let { panel } = await initPerformance(SIMPLE_URL);
+   let { EVENTS, $, $$, window, PerformanceController } = panel.panelWin;
+   let { OverviewView, DetailsView, OptimizationsListView, JsCallTreeView } = panel.panelWin;
+ 
+   let profilerData = { threads: [gThread] };
+ 
+   is(Services.prefs.getBoolPref(JIT_PREF), false, "record JIT Optimizations pref off by default");
+   Services.prefs.setBoolPref(JIT_PREF, true);
+   is(Services.prefs.getBoolPref(JIT_PREF), true, "toggle on record JIT Optimizations");
+ 
+   // Make two recordings, so we have one to switch to later, as the
+   // second one will have fake sample data
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield DetailsView.selectView("js-calltree");
++  await DetailsView.selectView("js-calltree");
+ 
+-  yield injectAndRenderProfilerData();
++  await injectAndRenderProfilerData();
+ 
+   is($("#jit-optimizations-view").classList.contains("hidden"), true,
+     "JIT Optimizations should be hidden when pref is on, but no frame selected");
+ 
+   // A is never a leaf, so it's optimizations should not be shown.
+-  yield checkFrame(1);
++  await checkFrame(1);
+ 
+   // gRawSite2 and gRawSite3 are both optimizations on B, so they'll have
+   // indices in descending order of # of samples.
+-  yield checkFrame(2, true);
++  await checkFrame(2, true);
+ 
+   // Leaf node (C) with no optimizations should not display any opts.
+-  yield checkFrame(3);
++  await checkFrame(3);
+ 
+   // Select the node with optimizations and change to a new recording
+   // to ensure the opts view is cleared
+   let rendered = once(JsCallTreeView, "focus");
+   mousedown(window, $$(".call-tree-item")[2]);
+-  yield rendered;
++  await rendered;
+   let isHidden = $("#jit-optimizations-view").classList.contains("hidden");
+   ok(!isHidden, "opts view should be visible when selecting a frame with opts");
+ 
+   let select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+   setSelectedRecording(panel, 0);
+-  yield Promise.all([select, rendered]);
++  await Promise.all([select, rendered]);
+ 
+   isHidden = $("#jit-optimizations-view").classList.contains("hidden");
+   ok(isHidden, "opts view is hidden when switching recordings");
+ 
+   rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+   setSelectedRecording(panel, 1);
+-  yield rendered;
++  await rendered;
+ 
+   rendered = once(JsCallTreeView, "focus");
+   mousedown(window, $$(".call-tree-item")[2]);
+-  yield rendered;
++  await rendered;
+   isHidden = $("#jit-optimizations-view").classList.contains("hidden");
+   ok(!isHidden, "opts view should be visible when selecting a frame with opts");
+ 
+   rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+   Services.prefs.setBoolPref(JIT_PREF, false);
+-  yield rendered;
++  await rendered;
+   ok(true, "call tree rerendered when JIT pref changes");
+   isHidden = $("#jit-optimizations-view").classList.contains("hidden");
+   ok(isHidden, "opts view hidden when toggling off jit pref");
+ 
+   rendered = once(JsCallTreeView, "focus");
+   mousedown(window, $$(".call-tree-item")[2]);
+-  yield rendered;
++  await rendered;
+   isHidden = $("#jit-optimizations-view").classList.contains("hidden");
+   ok(isHidden, "opts view hidden when jit pref off and selecting a frame with opts");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ 
+-  function* injectAndRenderProfilerData() {
++  async function injectAndRenderProfilerData() {
+     // Get current recording and inject our mock data
+     info("Injecting mock profile data");
+     let recording = PerformanceController.getCurrentRecording();
+     recording._profile = profilerData;
+ 
+     // Force a rerender
+     let rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+     JsCallTreeView.render(OverviewView.getTimeInterval());
+-    yield rendered;
++    await rendered;
+   }
+ 
+-  function* checkFrame(frameIndex, hasOpts) {
++  async function checkFrame(frameIndex, hasOpts) {
+     info(`Checking frame ${frameIndex}`);
+     // Click the frame
+     let rendered = once(JsCallTreeView, "focus");
+     mousedown(window, $$(".call-tree-item")[frameIndex]);
+-    yield rendered;
++    await rendered;
+ 
+     let isHidden = $("#jit-optimizations-view").classList.contains("hidden");
+     if (hasOpts) {
+       ok(!isHidden, "JIT Optimizations view is not hidden if current frame has opts.");
+     } else {
+       ok(isHidden, "JIT Optimizations view is hidden if current frame does not have opts");
+     }
+   }
+diff --git a/devtools/client/performance/test/browser_perf-options-show-platform-data-01.js b/devtools/client/performance/test/browser_perf-options-show-platform-data-01.js
+--- a/devtools/client/performance/test/browser_perf-options-show-platform-data-01.js
++++ b/devtools/client/performance/test/browser_perf-options-show-platform-data-01.js
+@@ -7,37 +7,37 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_SHOW_PLATFORM_DATA_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
+ 
+   Services.prefs.setBoolPref(UI_SHOW_PLATFORM_DATA_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield rendered;
++  await DetailsView.selectView("js-calltree");
++  await rendered;
+ 
+   rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+   Services.prefs.setBoolPref(UI_SHOW_PLATFORM_DATA_PREF, false);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsCallTreeView rerendered when toggling show-idle-blocks.");
+ 
+   rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+   Services.prefs.setBoolPref(UI_SHOW_PLATFORM_DATA_PREF, true);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsCallTreeView rerendered when toggling back show-idle-blocks.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-options-show-platform-data-02.js b/devtools/client/performance/test/browser_perf-options-show-platform-data-02.js
+--- a/devtools/client/performance/test/browser_perf-options-show-platform-data-02.js
++++ b/devtools/client/performance/test/browser_perf-options-show-platform-data-02.js
+@@ -7,37 +7,37 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_SHOW_PLATFORM_DATA_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, DetailsView, JsFlameGraphView } = panel.panelWin;
+ 
+   Services.prefs.setBoolPref(UI_SHOW_PLATFORM_DATA_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+-  yield DetailsView.selectView("js-flamegraph");
+-  yield rendered;
++  await DetailsView.selectView("js-flamegraph");
++  await rendered;
+ 
+   rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_SHOW_PLATFORM_DATA_PREF, false);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsFlameGraphView rerendered when toggling show-idle-blocks.");
+ 
+   rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+   Services.prefs.setBoolPref(UI_SHOW_PLATFORM_DATA_PREF, true);
+-  yield rendered;
++  await rendered;
+   ok(true, "JsFlameGraphView rerendered when toggling back show-idle-blocks.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-overview-render-01.js b/devtools/client/performance/test/browser_perf-overview-render-01.js
+--- a/devtools/client/performance/test/browser_perf-overview-render-01.js
++++ b/devtools/client/performance/test/browser_perf-overview-render-01.js
+@@ -7,28 +7,28 @@
+  */
+ 
+ const { Constants } = require("devtools/client/performance/modules/constants");
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { times } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, OverviewView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   // Ensure overview keeps rendering.
+-  yield times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
++  await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
+     expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
+   });
+ 
+   ok(true, "Overview was rendered while recording.");
+ 
+-  yield stopRecording(panel);
+-  yield teardownToolboxAndRemoveTab(panel);
++  await stopRecording(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-overview-render-02.js b/devtools/client/performance/test/browser_perf-overview-render-02.js
+--- a/devtools/client/performance/test/browser_perf-overview-render-02.js
++++ b/devtools/client/performance/test/browser_perf-overview-render-02.js
+@@ -9,28 +9,28 @@
+ 
+ const { Constants } = require("devtools/client/performance/modules/constants");
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_MEMORY_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { times } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, OverviewView } = panel.panelWin;
+ 
+   // Enable memory to test.
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, true);
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   let framerate = OverviewView.graphs.get("framerate");
+   let markers = OverviewView.graphs.get("timeline");
+   let memory = OverviewView.graphs.get("memory");
+ 
+   ok("selectionEnabled" in framerate,
+     "The selection should not be enabled for the framerate overview (1).");
+   is(framerate.selectionEnabled, false,
+@@ -48,17 +48,17 @@ add_task(function* () {
+   ok("selectionEnabled" in memory,
+     "The selection should not be enabled for the memory overview (1).");
+   is(memory.selectionEnabled, false,
+     "The selection should not be enabled for the memory overview (2).");
+   is(memory.hasSelection(), false,
+     "The memory overview shouldn't have a selection before recording.");
+ 
+   // Ensure overview keeps rendering.
+-  yield times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
++  await times(OverviewView, EVENTS.UI_OVERVIEW_RENDERED, 3, {
+     expectedArgs: { "1": Constants.FRAMERATE_GRAPH_LOW_RES_INTERVAL }
+   });
+ 
+   ok("selectionEnabled" in framerate,
+     "The selection should still not be enabled for the framerate overview (1).");
+   is(framerate.selectionEnabled, false,
+     "The selection should still not be enabled for the framerate overview (2).");
+   is(framerate.hasSelection(), false,
+@@ -73,19 +73,19 @@ add_task(function* () {
+ 
+   ok("selectionEnabled" in memory,
+     "The selection should still not be enabled for the memory overview (1).");
+   is(memory.selectionEnabled, false,
+     "The selection should still not be enabled for the memory overview (2).");
+   is(memory.hasSelection(), false,
+     "The memory overview still shouldn't have a selection before recording.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   is(framerate.selectionEnabled, true,
+     "The selection should now be enabled for the framerate overview.");
+   is(markers.selectionEnabled, true,
+     "The selection should now be enabled for the markers overview.");
+   is(memory.selectionEnabled, true,
+     "The selection should now be enabled for the memory overview.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-overview-render-03.js b/devtools/client/performance/test/browser_perf-overview-render-03.js
+--- a/devtools/client/performance/test/browser_perf-overview-render-03.js
++++ b/devtools/client/performance/test/browser_perf-overview-render-03.js
+@@ -7,18 +7,18 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_MEMORY_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { PerformanceController, OverviewView } = panel.panelWin;
+ 
+   // Enable memory to test.
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, true);
+@@ -56,21 +56,21 @@ add_task(function* () {
+       "The memory and framerate graphs data duration are the same.");
+ 
+     is(markers.dataScaleX, memory.dataScaleX,
+       "The markers and memory graphs data scale are the same.");
+     is(markers.dataScaleX, framerate.dataScaleX,
+       "The markers and framerate graphs data scale are the same.");
+   };
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   doChecks();
+ 
+-  yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
+-  yield waitUntil(() => PerformanceController.getCurrentRecording().getMemory().length);
+-  yield waitUntil(() => PerformanceController.getCurrentRecording().getTicks().length);
++  await waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
++  await waitUntil(() => PerformanceController.getCurrentRecording().getMemory().length);
++  await waitUntil(() => PerformanceController.getCurrentRecording().getTicks().length);
+   doChecks();
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+   doChecks();
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-overview-render-04.js b/devtools/client/performance/test/browser_perf-overview-render-04.js
+--- a/devtools/client/performance/test/browser_perf-overview-render-04.js
++++ b/devtools/client/performance/test/browser_perf-overview-render-04.js
+@@ -10,65 +10,65 @@
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_MEMORY_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
+ const { isVisible } = require("devtools/client/performance/test/helpers/dom-utils");
+ const { setSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { $, EVENTS, PerformanceController, OverviewView } = panel.panelWin;
+ 
+   // Enable memory to test.
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, true);
+ 
+   // Set realtime rendering off.
+   OverviewView.isRealtimeRenderingEnabled = () => false;
+ 
+   let updated = 0;
+   OverviewView.on(EVENTS.UI_OVERVIEW_RENDERED, () => updated++);
+ 
+-  yield startRecording(panel, { skipWaitingForOverview: true });
++  await startRecording(panel, { skipWaitingForOverview: true });
+ 
+   is(isVisible($("#overview-pane")), false, "Overview graphs hidden.");
+   is(updated, 0, "Overview graphs have not been updated");
+ 
+-  yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
+-  yield waitUntil(() => PerformanceController.getCurrentRecording().getMemory().length);
+-  yield waitUntil(() => PerformanceController.getCurrentRecording().getTicks().length);
++  await waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
++  await waitUntil(() => PerformanceController.getCurrentRecording().getMemory().length);
++  await waitUntil(() => PerformanceController.getCurrentRecording().getTicks().length);
+   is(isVisible($("#overview-pane")), false, "Overview graphs still hidden.");
+   is(updated, 0, "Overview graphs have still not been updated");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   is(isVisible($("#overview-pane")), true, "Overview graphs no longer hidden.");
+   is(updated, 1, "Overview graphs rendered upon completion.");
+ 
+-  yield startRecording(panel, { skipWaitingForOverview: true });
++  await startRecording(panel, { skipWaitingForOverview: true });
+ 
+   is(isVisible($("#overview-pane")), false,
+      "Overview graphs hidden again when starting new recording.");
+   is(updated, 1, "Overview graphs have not been updated again.");
+ 
+   setSelectedRecording(panel, 0);
+   is(isVisible($("#overview-pane")), true,
+      "Overview graphs no longer hidden when switching back to complete recording.");
+   is(updated, 1, "Overview graphs have not been updated again.");
+ 
+   setSelectedRecording(panel, 1);
+   is(isVisible($("#overview-pane")), false,
+      "Overview graphs hidden again when going back to inprogress recording.");
+   is(updated, 1, "Overview graphs have not been updated again.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   is(isVisible($("#overview-pane")), true,
+      "overview graphs no longer hidden when recording finishes");
+   is(updated, 2, "Overview graphs rendered again upon completion.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-overview-selection-01.js b/devtools/client/performance/test/browser_perf-overview-selection-01.js
+--- a/devtools/client/performance/test/browser_perf-overview-selection-01.js
++++ b/devtools/client/performance/test/browser_perf-overview-selection-01.js
+@@ -7,42 +7,42 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { dragStartCanvasGraph, dragStopCanvasGraph, clickCanvasGraph } = require("devtools/client/performance/test/helpers/input-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, PerformanceController, OverviewView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let duration = PerformanceController.getCurrentRecording().getDuration();
+   let graph = OverviewView.graphs.get("timeline");
+ 
+   // Select the first half of the graph.
+ 
+   let rangeSelected = once(OverviewView, EVENTS.UI_OVERVIEW_RANGE_SELECTED,
+                            { spreadArgs: true });
+   dragStartCanvasGraph(graph, { x: 0 });
+-  let [, { startTime, endTime }] = yield rangeSelected;
++  let [, { startTime, endTime }] = await rangeSelected;
+   is(endTime, duration, "The selected range is the entire graph, for now.");
+ 
+   rangeSelected = once(OverviewView, EVENTS.UI_OVERVIEW_RANGE_SELECTED,
+                        { spreadArgs: true });
+   dragStopCanvasGraph(graph, { x: graph.width / 2 });
+-  [, { startTime, endTime }] = yield rangeSelected;
++  [, { startTime, endTime }] = await rangeSelected;
+   is(endTime, duration / 2, "The selected range is half of the graph.");
+ 
+   is(graph.hasSelection(), true,
+     "A selection exists on the graph.");
+   is(startTime, 0,
+     "The UI_OVERVIEW_RANGE_SELECTED event fired with 0 as a `startTime`.");
+   is(endTime, duration / 2,
+     `The UI_OVERVIEW_RANGE_SELECTED event fired with ${duration / 2} as \`endTime\`.`);
+@@ -53,19 +53,19 @@ add_task(function* () {
+   is(actual.min, 0, "Graph selection starts at 0.");
+   is(actual.max, duration / 2, `Graph selection ends at ${duration / 2}.`);
+ 
+   // Listen to deselection.
+ 
+   rangeSelected = once(OverviewView, EVENTS.UI_OVERVIEW_RANGE_SELECTED,
+                        { spreadArgs: true });
+   clickCanvasGraph(graph, { x: 3 * graph.width / 4 });
+-  [, { startTime, endTime }] = yield rangeSelected;
++  [, { startTime, endTime }] = await rangeSelected;
+ 
+   is(graph.hasSelection(), false,
+     "A selection no longer on the graph.");
+   is(startTime, 0,
+     "The UI_OVERVIEW_RANGE_SELECTED event fired with 0 as a `startTime`.");
+   is(endTime, duration,
+     "The UI_OVERVIEW_RANGE_SELECTED event fired with duration as `endTime`.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-overview-selection-02.js b/devtools/client/performance/test/browser_perf-overview-selection-02.js
+--- a/devtools/client/performance/test/browser_perf-overview-selection-02.js
++++ b/devtools/client/performance/test/browser_perf-overview-selection-02.js
+@@ -6,28 +6,28 @@
+  * Tests that the graphs' selection is correctly disabled or enabled.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_MEMORY_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { OverviewView } = panel.panelWin;
+ 
+   // Enable memory to test.
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, true);
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   let markersOverview = OverviewView.graphs.get("timeline");
+   let memoryGraph = OverviewView.graphs.get("memory");
+   let framerateGraph = OverviewView.graphs.get("framerate");
+ 
+   ok(markersOverview,
+     "The markers graph should have been created now.");
+   ok(memoryGraph,
+@@ -37,37 +37,37 @@ add_task(function* () {
+ 
+   ok(!markersOverview.selectionEnabled,
+     "Selection shouldn't be enabled when the first recording started (2).");
+   ok(!memoryGraph.selectionEnabled,
+     "Selection shouldn't be enabled when the first recording started (3).");
+   ok(!framerateGraph.selectionEnabled,
+     "Selection shouldn't be enabled when the first recording started (1).");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   ok(markersOverview.selectionEnabled,
+     "Selection should be enabled when the first recording finishes (2).");
+   ok(memoryGraph.selectionEnabled,
+     "Selection should be enabled when the first recording finishes (3).");
+   ok(framerateGraph.selectionEnabled,
+     "Selection should be enabled when the first recording finishes (1).");
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   ok(!markersOverview.selectionEnabled,
+     "Selection shouldn't be enabled when the second recording started (2).");
+   ok(!memoryGraph.selectionEnabled,
+     "Selection shouldn't be enabled when the second recording started (3).");
+   ok(!framerateGraph.selectionEnabled,
+     "Selection shouldn't be enabled when the second recording started (1).");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   ok(markersOverview.selectionEnabled,
+     "Selection should be enabled when the first second finishes (2).");
+   ok(memoryGraph.selectionEnabled,
+     "Selection should be enabled when the first second finishes (3).");
+   ok(framerateGraph.selectionEnabled,
+     "Selection should be enabled when the first second finishes (1).");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-overview-selection-03.js b/devtools/client/performance/test/browser_perf-overview-selection-03.js
+--- a/devtools/client/performance/test/browser_perf-overview-selection-03.js
++++ b/devtools/client/performance/test/browser_perf-overview-selection-03.js
+@@ -8,75 +8,75 @@
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_MEMORY_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { times } = require("devtools/client/performance/test/helpers/event-utils");
+ const { dragStartCanvasGraph, dragStopCanvasGraph } = require("devtools/client/performance/test/helpers/input-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, OverviewView } = panel.panelWin;
+ 
+   // Enable memory to test.
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let markersOverview = OverviewView.graphs.get("timeline");
+   let memoryGraph = OverviewView.graphs.get("memory");
+   let framerateGraph = OverviewView.graphs.get("framerate");
+   let width = framerateGraph.width;
+ 
+   // Perform a selection inside the framerate graph.
+ 
+   let rangeSelected = times(OverviewView, EVENTS.UI_OVERVIEW_RANGE_SELECTED, 2);
+   dragStartCanvasGraph(framerateGraph, { x: 0 });
+   dragStopCanvasGraph(framerateGraph, { x: width / 2 });
+-  yield rangeSelected;
++  await rangeSelected;
+ 
+   is(markersOverview.getSelection().toSource(), framerateGraph.getSelection().toSource(),
+     "The markers overview has a correct selection.");
+   is(memoryGraph.getSelection().toSource(), framerateGraph.getSelection().toSource(),
+     "The memory overview has a correct selection.");
+   is(framerateGraph.getSelection().toSource(), "({start:0, end:" + (width / 2) + "})",
+     "The framerate graph has a correct selection.");
+ 
+   // Perform a selection inside the markers overview.
+ 
+   markersOverview.dropSelection();
+ 
+   rangeSelected = times(OverviewView, EVENTS.UI_OVERVIEW_RANGE_SELECTED, 2);
+   dragStartCanvasGraph(markersOverview, { x: 0 });
+   dragStopCanvasGraph(markersOverview, { x: width / 4 });
+-  yield rangeSelected;
++  await rangeSelected;
+ 
+   is(markersOverview.getSelection().toSource(), framerateGraph.getSelection().toSource(),
+     "The markers overview has a correct selection.");
+   is(memoryGraph.getSelection().toSource(), framerateGraph.getSelection().toSource(),
+     "The memory overview has a correct selection.");
+   is(framerateGraph.getSelection().toSource(), "({start:0, end:" + (width / 4) + "})",
+     "The framerate graph has a correct selection.");
+ 
+   // Perform a selection inside the memory overview.
+ 
+   markersOverview.dropSelection();
+ 
+   rangeSelected = times(OverviewView, EVENTS.UI_OVERVIEW_RANGE_SELECTED, 2);
+   dragStartCanvasGraph(memoryGraph, { x: 0 });
+   dragStopCanvasGraph(memoryGraph, { x: width / 10 });
+-  yield rangeSelected;
++  await rangeSelected;
+ 
+   is(markersOverview.getSelection().toSource(), framerateGraph.getSelection().toSource(),
+     "The markers overview has a correct selection.");
+   is(memoryGraph.getSelection().toSource(), framerateGraph.getSelection().toSource(),
+     "The memory overview has a correct selection.");
+   is(framerateGraph.getSelection().toSource(), "({start:0, end:" + (width / 10) + "})",
+     "The framerate graph has a correct selection.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-overview-time-interval.js b/devtools/client/performance/test/browser_perf-overview-time-interval.js
+--- a/devtools/client/performance/test/browser_perf-overview-time-interval.js
++++ b/devtools/client/performance/test/browser_perf-overview-time-interval.js
+@@ -7,18 +7,18 @@
+  * work properly.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, OverviewView } = panel.panelWin;
+ 
+   try {
+     OverviewView.setTimeInterval({ starTime: 0, endTime: 1 });
+@@ -29,24 +29,24 @@ add_task(function* () {
+ 
+   try {
+     OverviewView.getTimeInterval();
+     ok(false, "Getting the time interval shouldn't have worked.");
+   } catch (e) {
+     ok(true, "Getting the time interval didn't work, as expected.");
+   }
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   // Get/set the time interval and wait for the event propagation.
+ 
+   let rangeSelected = once(OverviewView, EVENTS.UI_OVERVIEW_RANGE_SELECTED);
+   OverviewView.setTimeInterval({ startTime: 10, endTime: 20 });
+-  yield rangeSelected;
++  await rangeSelected;
+ 
+   let firstInterval = OverviewView.getTimeInterval();
+   info("First interval start time: " + firstInterval.startTime);
+   info("First interval end time: " + firstInterval.endTime);
+   is(Math.round(firstInterval.startTime), 10,
+     "The interval's start time was properly set.");
+   is(Math.round(firstInterval.endTime), 20,
+     "The interval's end time was properly set.");
+@@ -64,10 +64,10 @@ add_task(function* () {
+   let secondInterval = OverviewView.getTimeInterval();
+   info("Second interval start time: " + secondInterval.startTime);
+   info("Second interval end time: " + secondInterval.endTime);
+   is(Math.round(secondInterval.startTime), 30,
+     "The interval's start time was properly set again.");
+   is(Math.round(secondInterval.endTime), 40,
+     "The interval's end time was properly set again.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-private-browsing.js b/devtools/client/performance/test/browser_perf-private-browsing.js
+--- a/devtools/client/performance/test/browser_perf-private-browsing.js
++++ b/devtools/client/performance/test/browser_perf-private-browsing.js
+@@ -8,107 +8,106 @@
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { addWindow } = require("devtools/client/performance/test/helpers/tab-utils");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+ let gPanelWinTuples = [];
+-
+-add_task(function* () {
+-  yield testNormalWindow();
+-  yield testPrivateWindow();
+-  yield testRecordingFailingInWindow(0);
+-  yield testRecordingFailingInWindow(1);
+-  yield teardownPerfInWindow(1, { shouldCloseWindow: true });
+-  yield testRecordingSucceedingInWindow(0);
+-  yield teardownPerfInWindow(0, { shouldCloseWindow: false });
++add_task(async function() {
++  await testNormalWindow();
++  await testPrivateWindow();
++  await testRecordingFailingInWindow(0);
++  await testRecordingFailingInWindow(1);
++  await teardownPerfInWindow(1, { shouldCloseWindow: true });
++  await testRecordingSucceedingInWindow(0);
++  await teardownPerfInWindow(0, { shouldCloseWindow: false });
+ 
+   gPanelWinTuples = null;
+ });
+ 
+-function* createPanelInNewWindow(options) {
+-  let win = yield addWindow(options);
+-  return yield createPanelInWindow(options, win);
++async function createPanelInNewWindow(options) {
++  let win = await addWindow(options);
++  return createPanelInWindow(options, win);
+ }
+ 
+-function* createPanelInWindow(options, win = window) {
+-  let { panel } = yield initPerformanceInNewTab({
++async function createPanelInWindow(options, win = window) {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: win
+   }, options);
+ 
+   gPanelWinTuples.push({ panel, win });
+   return { panel, win };
+ }
+ 
+-function* testNormalWindow() {
+-  let { panel } = yield createPanelInWindow({
++async function testNormalWindow() {
++  let { panel } = await createPanelInWindow({
+     private: false
+   });
+ 
+   let { PerformanceView } = panel.panelWin;
+ 
+   is(PerformanceView.getState(), "empty",
+     "The initial state of the performance panel view is correct (1).");
+ }
+ 
+-function* testPrivateWindow() {
+-  let { panel } = yield createPanelInNewWindow({
++async function testPrivateWindow() {
++  let { panel } = await createPanelInNewWindow({
+     private: true,
+     // The add-on SDK can't seem to be able to listen to "ready" or "close"
+     // events for private tabs. Don't really absolutely need to though.
+     dontWaitForTabReady: true
+   });
+ 
+   let { PerformanceView } = panel.panelWin;
+ 
+   is(PerformanceView.getState(), "unavailable",
+     "The initial state of the performance panel view is correct (2).");
+ }
+ 
+-function* testRecordingFailingInWindow(index) {
++async function testRecordingFailingInWindow(index) {
+   let { panel } = gPanelWinTuples[index];
+   let { EVENTS, PerformanceController } = panel.panelWin;
+ 
+   let onRecordingStarted = () => {
+     ok(false, "Recording should not start while a private window is present.");
+   };
+ 
+   PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE, onRecordingStarted);
+ 
+   let whenFailed = once(PerformanceController,
+                         EVENTS.BACKEND_FAILED_AFTER_RECORDING_START);
+   PerformanceController.startRecording();
+-  yield whenFailed;
++  await whenFailed;
+   ok(true, "Recording has failed.");
+ 
+   PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE, onRecordingStarted);
+ }
+ 
+-function* testRecordingSucceedingInWindow(index) {
++async function testRecordingSucceedingInWindow(index) {
+   let { panel } = gPanelWinTuples[index];
+   let { EVENTS, PerformanceController } = panel.panelWin;
+ 
+   let onRecordingFailed = () => {
+     ok(false, "Recording should start while now private windows are present.");
+   };
+ 
+   PerformanceController.on(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START,
+                            onRecordingFailed);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+   ok(true, "Recording has succeeded.");
+ 
+   PerformanceController.off(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START,
+                            onRecordingFailed);
+ }
+ 
+-function* teardownPerfInWindow(index, options) {
++async function teardownPerfInWindow(index, options) {
+   let { panel, win } = gPanelWinTuples[index];
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ 
+   if (options.shouldCloseWindow) {
+     win.close();
+   }
+ }
+diff --git a/devtools/client/performance/test/browser_perf-range-changed-render.js b/devtools/client/performance/test/browser_perf-range-changed-render.js
+--- a/devtools/client/performance/test/browser_perf-range-changed-render.js
++++ b/devtools/client/performance/test/browser_perf-range-changed-render.js
+@@ -6,18 +6,18 @@
+  * Tests that the detail views are rerendered after the range changes.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let {
+     EVENTS,
+     OverviewView,
+     DetailsView,
+@@ -31,51 +31,51 @@ add_task(function* () {
+   let updatedFlameGraph = 0;
+   let updateWaterfall = () => updatedWaterfall++;
+   let updateCallTree = () => updatedCallTree++;
+   let updateFlameGraph = () => updatedFlameGraph++;
+   WaterfallView.on(EVENTS.UI_WATERFALL_RENDERED, updateWaterfall);
+   JsCallTreeView.on(EVENTS.UI_JS_CALL_TREE_RENDERED, updateCallTree);
+   JsFlameGraphView.on(EVENTS.UI_JS_FLAMEGRAPH_RENDERED, updateFlameGraph);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+   OverviewView.emit(EVENTS.UI_OVERVIEW_RANGE_SELECTED, { startTime: 0, endTime: 10 });
+-  yield rendered;
++  await rendered;
+   ok(true, "Waterfall rerenders when a range in the overview graph is selected.");
+ 
+   rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield rendered;
++  await DetailsView.selectView("js-calltree");
++  await rendered;
+   ok(true, "Call tree rerenders after its corresponding pane is shown.");
+ 
+   rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+-  yield DetailsView.selectView("js-flamegraph");
+-  yield rendered;
++  await DetailsView.selectView("js-flamegraph");
++  await rendered;
+   ok(true, "Flamegraph rerenders after its corresponding pane is shown.");
+ 
+   rendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+   OverviewView.emit(EVENTS.UI_OVERVIEW_RANGE_SELECTED);
+-  yield rendered;
++  await rendered;
+   ok(true, "Flamegraph rerenders when a range in the overview graph is removed.");
+ 
+   rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+-  yield DetailsView.selectView("js-calltree");
+-  yield rendered;
++  await DetailsView.selectView("js-calltree");
++  await rendered;
+   ok(true, "Call tree rerenders after its corresponding pane is shown.");
+ 
+   rendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+-  yield DetailsView.selectView("waterfall");
+-  yield rendered;
++  await DetailsView.selectView("waterfall");
++  await rendered;
+   ok(true, "Waterfall rerenders after its corresponding pane is shown.");
+ 
+   is(updatedWaterfall, 3, "WaterfallView rerendered 3 times.");
+   is(updatedCallTree, 2, "JsCallTreeView rerendered 2 times.");
+   is(updatedFlameGraph, 2, "JsFlameGraphView rerendered 2 times.");
+ 
+   WaterfallView.off(EVENTS.UI_WATERFALL_RENDERED, updateWaterfall);
+   JsCallTreeView.off(EVENTS.UI_JS_CALL_TREE_RENDERED, updateCallTree);
+   JsFlameGraphView.off(EVENTS.UI_JS_FLAMEGRAPH_RENDERED, updateFlameGraph);
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recording-notices-01.js b/devtools/client/performance/test/browser_perf-recording-notices-01.js
+--- a/devtools/client/performance/test/browser_perf-recording-notices-01.js
++++ b/devtools/client/performance/test/browser_perf-recording-notices-01.js
+@@ -6,40 +6,40 @@
+  * Tests that the recording notice panes are toggled in correct scenarios
+  * for initialization and a single recording.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { $, PerformanceView } = panel.panelWin;
+ 
+   let MAIN_CONTAINER = $("#performance-view");
+   let EMPTY = $("#empty-notice");
+   let CONTENT = $("#performance-view-content");
+   let DETAILS_CONTAINER = $("#details-pane-container");
+   let RECORDING = $("#recording-notice");
+   let DETAILS = $("#details-pane");
+ 
+   is(PerformanceView.getState(), "empty", "Correct default state.");
+   is(MAIN_CONTAINER.selectedPanel, EMPTY, "Showing empty panel on load.");
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   is(PerformanceView.getState(), "recording", "Correct state during recording.");
+   is(MAIN_CONTAINER.selectedPanel, CONTENT, "Showing main view with timeline.");
+   is(DETAILS_CONTAINER.selectedPanel, RECORDING, "Showing recording panel.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   is(PerformanceView.getState(), "recorded", "Correct state after recording.");
+   is(MAIN_CONTAINER.selectedPanel, CONTENT, "Showing main view with timeline.");
+   is(DETAILS_CONTAINER.selectedPanel, DETAILS, "Showing rendered graphs.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recording-notices-02.js b/devtools/client/performance/test/browser_perf-recording-notices-02.js
+--- a/devtools/client/performance/test/browser_perf-recording-notices-02.js
++++ b/devtools/client/performance/test/browser_perf-recording-notices-02.js
+@@ -8,18 +8,18 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { setSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let {
+     EVENTS,
+     $,
+     PerformanceController,
+@@ -27,39 +27,39 @@ add_task(function* () {
+   } = panel.panelWin;
+ 
+   let MAIN_CONTAINER = $("#performance-view");
+   let CONTENT = $("#performance-view-content");
+   let DETAILS_CONTAINER = $("#details-pane-container");
+   let RECORDING = $("#recording-notice");
+   let DETAILS = $("#details-pane");
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   is(PerformanceView.getState(), "recording", "Correct state during recording.");
+   is(MAIN_CONTAINER.selectedPanel, CONTENT, "Showing main view with timeline.");
+   is(DETAILS_CONTAINER.selectedPanel, RECORDING, "Showing recording panel.");
+ 
+   let selected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 0);
+-  yield selected;
++  await selected;
+ 
+   is(PerformanceView.getState(), "recorded",
+      "Correct state during recording but selecting a completed recording.");
+   is(MAIN_CONTAINER.selectedPanel, CONTENT, "Showing main view with timeline.");
+   is(DETAILS_CONTAINER.selectedPanel, DETAILS, "Showing recorded panel.");
+ 
+   selected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 1);
+-  yield selected;
++  await selected;
+ 
+   is(PerformanceView.getState(), "recording",
+      "Correct state when switching back to recording in progress.");
+   is(MAIN_CONTAINER.selectedPanel, CONTENT, "Showing main view with timeline.");
+   is(DETAILS_CONTAINER.selectedPanel, RECORDING, "Showing recording panel.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recording-notices-03.js b/devtools/client/performance/test/browser_perf-recording-notices-03.js
+--- a/devtools/client/performance/test/browser_perf-recording-notices-03.js
++++ b/devtools/client/performance/test/browser_perf-recording-notices-03.js
+@@ -12,71 +12,71 @@ const { SIMPLE_URL } = require("devtools
+ const { PROFILER_BUFFER_SIZE_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { pmmLoadFrameScripts, pmmStopProfiler, pmmClearFrameScripts } = require("devtools/client/performance/test/helpers/profiler-mm-utils");
+ const { initPerformanceInTab, initConsoleInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { setSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Make sure the profiler module is stopped so we can set a new buffer limit.
+   pmmLoadFrameScripts(gBrowser);
+-  yield pmmStopProfiler();
++  await pmmStopProfiler();
+ 
+   // Keep the profiler's buffer large, but still get to 1% relatively quick.
+   Services.prefs.setIntPref(PROFILER_BUFFER_SIZE_PREF, 1000000);
+ 
+-  let { target, console } = yield initConsoleInNewTab({
++  let { target, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+   let {
+     gFront,
+     EVENTS,
+     $,
+     PerformanceController,
+     PerformanceView,
+   } = panel.panelWin;
+ 
+   // Set a fast profiler-status update interval.
+-  yield gFront.setProfilerStatusInterval(10);
++  await gFront.setProfilerStatusInterval(10);
+ 
+   let DETAILS_CONTAINER = $("#details-pane-container");
+   let NORMAL_BUFFER_STATUS_MESSAGE = $("#recording-notice .buffer-status-message");
+   let CONSOLE_BUFFER_STATUS_MESSAGE =
+     $("#console-recording-notice .buffer-status-message");
+   let gPercent;
+ 
+   // Start a manual recording.
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+-  yield waitUntil(function* () {
+-    [, gPercent] = yield once(PerformanceView,
++  await waitUntil(async function() {
++    [, gPercent] = await once(PerformanceView,
+                               EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
+                               { spreadArgs: true });
+     return gPercent > 0;
+   });
+ 
+   ok(true, "Buffer percentage increased in display (1).");
+ 
+   let bufferUsage = PerformanceController.getBufferUsageForRecording(
+     PerformanceController.getCurrentRecording());
+   either(DETAILS_CONTAINER.getAttribute("buffer-status"), "in-progress", "full",
+     "Container has [buffer-status=in-progress] or [buffer-status=full].");
+   ok(NORMAL_BUFFER_STATUS_MESSAGE.value.includes(gPercent + "%"),
+     "Buffer status text has correct percentage.");
+ 
+   // Start a console profile.
+-  yield console.profile("rust");
++  await console.profile("rust");
+ 
+-  yield waitUntil(function* () {
+-    [, gPercent] = yield once(PerformanceView,
++  await waitUntil(async function() {
++    [, gPercent] = await once(PerformanceView,
+                               EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
+                               { spreadArgs: true });
+     return gPercent > Math.floor(bufferUsage * 100);
+   });
+ 
+   ok(true, "Buffer percentage increased in display (2).");
+ 
+   bufferUsage = PerformanceController.getBufferUsageForRecording(
+@@ -84,52 +84,52 @@ add_task(function* () {
+   either(DETAILS_CONTAINER.getAttribute("buffer-status"), "in-progress", "full",
+     "Container has [buffer-status=in-progress] or [buffer-status=full].");
+   ok(NORMAL_BUFFER_STATUS_MESSAGE.value.includes(gPercent + "%"),
+     "Buffer status text has correct percentage.");
+ 
+   // Select the console recording.
+   let selected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 1);
+-  yield selected;
++  await selected;
+ 
+-  yield waitUntil(function* () {
+-    [, gPercent] = yield once(PerformanceView,
++  await waitUntil(async function() {
++    [, gPercent] = await once(PerformanceView,
+                               EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
+                               { spreadArgs: true });
+     return gPercent > 0;
+   });
+ 
+   ok(true, "Percentage updated for newly selected recording.");
+ 
+   either(DETAILS_CONTAINER.getAttribute("buffer-status"), "in-progress", "full",
+     "Container has [buffer-status=in-progress] or [buffer-status=full].");
+   ok(CONSOLE_BUFFER_STATUS_MESSAGE.value.includes(gPercent + "%"),
+     "Buffer status text has correct percentage for console recording.");
+ 
+   // Stop the console profile, then select the original manual recording.
+-  yield console.profileEnd("rust");
++  await console.profileEnd("rust");
+ 
+   selected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 0);
+-  yield selected;
++  await selected;
+ 
+-  yield waitUntil(function* () {
+-    [, gPercent] = yield once(PerformanceView,
++  await waitUntil(async function() {
++    [, gPercent] = await once(PerformanceView,
+                               EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
+                               { spreadArgs: true });
+     return gPercent > Math.floor(bufferUsage * 100);
+   });
+ 
+   ok(true, "Buffer percentage increased in display (3).");
+ 
+   either(DETAILS_CONTAINER.getAttribute("buffer-status"), "in-progress", "full",
+     "Container has [buffer-status=in-progress] or [buffer-status=full].");
+   ok(NORMAL_BUFFER_STATUS_MESSAGE.value.includes(gPercent + "%"),
+     "Buffer status text has correct percentage.");
+ 
+   // Stop the manual recording.
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ 
+   pmmClearFrameScripts();
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recording-notices-04.js b/devtools/client/performance/test/browser_perf-recording-notices-04.js
+--- a/devtools/client/performance/test/browser_perf-recording-notices-04.js
++++ b/devtools/client/performance/test/browser_perf-recording-notices-04.js
+@@ -10,57 +10,57 @@
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { PROFILER_BUFFER_SIZE_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { pmmLoadFrameScripts, pmmStopProfiler, pmmClearFrameScripts } = require("devtools/client/performance/test/helpers/profiler-mm-utils");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Make sure the profiler module is stopped so we can set a new buffer limit.
+   pmmLoadFrameScripts(gBrowser);
+-  yield pmmStopProfiler();
++  await pmmStopProfiler();
+ 
+   // Keep the profiler's buffer small, to get to 100% really quickly.
+   Services.prefs.setIntPref(PROFILER_BUFFER_SIZE_PREF, 10000);
+ 
+-  let { panel } = yield initPerformanceInNewTab({
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { gFront, EVENTS, $, PerformanceController, PerformanceView } = panel.panelWin;
+ 
+   // Set a fast profiler-status update interval
+-  yield gFront.setProfilerStatusInterval(10);
++  await gFront.setProfilerStatusInterval(10);
+ 
+   let DETAILS_CONTAINER = $("#details-pane-container");
+   let NORMAL_BUFFER_STATUS_MESSAGE = $("#recording-notice .buffer-status-message");
+   let gPercent;
+ 
+   // Start a manual recording.
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+-  yield waitUntil(function* () {
+-    [, gPercent] = yield once(PerformanceView,
++  await waitUntil(async function() {
++    [, gPercent] = await once(PerformanceView,
+                               EVENTS.UI_RECORDING_PROFILER_STATUS_RENDERED,
+                               { spreadArgs: true });
+     return gPercent == 100;
+   });
+ 
+   ok(true, "Buffer percentage increased in display.");
+ 
+   let bufferUsage = PerformanceController.getBufferUsageForRecording(
+     PerformanceController.getCurrentRecording());
+   ok(bufferUsage, 1, "Buffer is full for this recording.");
+   ok(DETAILS_CONTAINER.getAttribute("buffer-status"), "full",
+     "Container has [buffer-status=full].");
+   ok(NORMAL_BUFFER_STATUS_MESSAGE.value.includes(gPercent + "%"),
+     "Buffer status text has correct percentage.");
+ 
+   // Stop the manual recording.
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ 
+   pmmClearFrameScripts();
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recording-notices-05.js b/devtools/client/performance/test/browser_perf-recording-notices-05.js
+--- a/devtools/client/performance/test/browser_perf-recording-notices-05.js
++++ b/devtools/client/performance/test/browser_perf-recording-notices-05.js
+@@ -4,26 +4,26 @@
+ 
+ /**
+  * Tests that the circular buffer notices work when e10s is on/off.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { gFront, $, PerformanceController } = panel.panelWin;
+ 
+   // Set a fast profiler-status update interval
+-  yield gFront.setProfilerStatusInterval(10);
++  await gFront.setProfilerStatusInterval(10);
+ 
+   let enabled = false;
+ 
+   PerformanceController.getMultiprocessStatus = () => {
+     return { enabled };
+   };
+ 
+   PerformanceController._setMultiprocessAttributes();
+@@ -31,10 +31,10 @@ add_task(function* () {
+     "When e10s is disabled, container has [e10s=disabled].");
+ 
+   enabled = true;
+ 
+   PerformanceController._setMultiprocessAttributes();
+   ok($("#performance-view").getAttribute("e10s"), "",
+     "When e10s is enabled, there should be no e10s attribute.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recording-selected-01.js b/devtools/client/performance/test/browser_perf-recording-selected-01.js
+--- a/devtools/client/performance/test/browser_perf-recording-selected-01.js
++++ b/devtools/client/performance/test/browser_perf-recording-selected-01.js
+@@ -8,38 +8,38 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { setSelectedRecording, getRecordingsCount, getSelectedRecordingIndex } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, PerformanceController } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   is(getRecordingsCount(panel), 2,
+     "There should be two recordings visible.");
+   is(getSelectedRecordingIndex(panel), 1,
+     "The second recording item should be selected.");
+ 
+   let selected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 0);
+-  yield selected;
++  await selected;
+ 
+   is(getRecordingsCount(panel), 2,
+     "There should still be two recordings visible.");
+   is(getSelectedRecordingIndex(panel), 0,
+     "The first recording item should be selected.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recording-selected-02.js b/devtools/client/performance/test/browser_perf-recording-selected-02.js
+--- a/devtools/client/performance/test/browser_perf-recording-selected-02.js
++++ b/devtools/client/performance/test/browser_perf-recording-selected-02.js
+@@ -8,51 +8,51 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { getSelectedRecordingIndex, setSelectedRecording, getRecordingsCount } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
++add_task(async function() {
+   // This test seems to take a very long time to finish on Linux VMs.
+   requestLongerTimeout(4);
+ 
+-  let { panel } = yield initPerformanceInNewTab({
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, PerformanceController } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   is(getRecordingsCount(panel), 2,
+     "There should be two recordings visible.");
+   is(getSelectedRecordingIndex(panel), 1,
+     "The new recording item should be selected.");
+ 
+   let selected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 0);
+-  yield selected;
++  await selected;
+ 
+   is(getRecordingsCount(panel), 2,
+     "There should still be two recordings visible.");
+   is(getSelectedRecordingIndex(panel), 0,
+     "The first recording item should be selected now.");
+ 
+   selected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 1);
+-  yield selected;
++  await selected;
+ 
+   is(getRecordingsCount(panel), 2,
+     "There should still be two recordings visible.");
+   is(getSelectedRecordingIndex(panel), 1,
+     "The second recording item should be selected again.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recording-selected-03.js b/devtools/client/performance/test/browser_perf-recording-selected-03.js
+--- a/devtools/client/performance/test/browser_perf-recording-selected-03.js
++++ b/devtools/client/performance/test/browser_perf-recording-selected-03.js
+@@ -9,36 +9,36 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { setSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { $, EVENTS, PerformanceController } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   info("Selecting recording #0 and waiting for it to be displayed.");
+ 
+   let selected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+   setSelectedRecording(panel, 0);
+-  yield selected;
++  await selected;
+ 
+   ok($("#main-record-button").classList.contains("checked"),
+     "Button is still checked after selecting another item.");
+   ok(!$("#main-record-button").hasAttribute("disabled"),
+     "Button is not locked after selecting another item.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recording-selected-04.js b/devtools/client/performance/test/browser_perf-recording-selected-04.js
+--- a/devtools/client/performance/test/browser_perf-recording-selected-04.js
++++ b/devtools/client/performance/test/browser_perf-recording-selected-04.js
+@@ -7,53 +7,53 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_MEMORY_PREF, UI_ENABLE_ALLOCATIONS_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording, waitForAllWidgetsRendered } = require("devtools/client/performance/test/helpers/actions");
+ const { setSelectedRecording } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { DetailsView, DetailsSubview } = panel.panelWin;
+ 
+   // Enable memory to test the memory overview.
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, true);
+ 
+   // Enable allocations to test the memory-calltree and memory-flamegraph.
+   Services.prefs.setBoolPref(UI_ENABLE_ALLOCATIONS_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   // Ållow widgets to be updated while hidden, to make testing easier.
+   DetailsSubview.canUpdateWhileHidden = true;
+ 
+   // Cycle through all the views to initialize them. The waterfall is shown
+   // by default, but all the other views are created lazily, so won't emit
+   // any events.
+-  yield DetailsView.selectView("js-calltree");
+-  yield DetailsView.selectView("js-flamegraph");
+-  yield DetailsView.selectView("memory-calltree");
+-  yield DetailsView.selectView("memory-flamegraph");
++  await DetailsView.selectView("js-calltree");
++  await DetailsView.selectView("js-flamegraph");
++  await DetailsView.selectView("memory-calltree");
++  await DetailsView.selectView("memory-flamegraph");
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let rerender = waitForAllWidgetsRendered(panel);
+   setSelectedRecording(panel, 0);
+-  yield rerender;
++  await rerender;
+ 
+   ok(true, "All widgets were rendered when selecting the first recording.");
+ 
+   rerender = waitForAllWidgetsRendered(panel);
+   setSelectedRecording(panel, 1);
+-  yield rerender;
++  await rerender;
+ 
+   ok(true, "All widgets were rendered when selecting the second recording.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recordings-clear-01.js b/devtools/client/performance/test/browser_perf-recordings-clear-01.js
+--- a/devtools/client/performance/test/browser_perf-recordings-clear-01.js
++++ b/devtools/client/performance/test/browser_perf-recordings-clear-01.js
+@@ -7,48 +7,48 @@
+  * the empty notice state.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPanelInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { getRecordingsCount } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPanelInNewTab({
++add_task(async function() {
++  let { panel } = await initPanelInNewTab({
+     tool: "performance",
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { PerformanceController, PerformanceView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   is(getRecordingsCount(panel), 1,
+     "The recordings list should have one recording.");
+   isnot(PerformanceView.getState(), "empty",
+     "PerformanceView should not be in an empty state.");
+   isnot(PerformanceController.getCurrentRecording(), null,
+     "There should be a current recording.");
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   is(getRecordingsCount(panel), 2,
+     "The recordings list should have two recordings.");
+   isnot(PerformanceView.getState(), "empty",
+     "PerformanceView should not be in an empty state.");
+   isnot(PerformanceController.getCurrentRecording(), null,
+     "There should be a current recording.");
+ 
+-  yield PerformanceController.clearRecordings();
++  await PerformanceController.clearRecordings();
+ 
+   is(getRecordingsCount(panel), 0,
+     "The recordings list should be empty.");
+   is(PerformanceView.getState(), "empty",
+     "PerformanceView should be in an empty state.");
+   is(PerformanceController.getCurrentRecording(), null,
+     "There should be no current recording.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recordings-clear-02.js b/devtools/client/performance/test/browser_perf-recordings-clear-02.js
+--- a/devtools/client/performance/test/browser_perf-recordings-clear-02.js
++++ b/devtools/client/performance/test/browser_perf-recordings-clear-02.js
+@@ -8,62 +8,62 @@
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPanelInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { times, once } = require("devtools/client/performance/test/helpers/event-utils");
+ const { getRecordingsCount } = require("devtools/client/performance/test/helpers/recording-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPanelInNewTab({
++add_task(async function() {
++  let { panel } = await initPanelInNewTab({
+     tool: "performance",
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, PerformanceController, PerformanceView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   is(getRecordingsCount(panel), 1,
+     "The recordings list should have one recording.");
+   isnot(PerformanceView.getState(), "empty",
+     "PerformanceView should not be in an empty state.");
+   isnot(PerformanceController.getCurrentRecording(), null,
+     "There should be a current recording.");
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   is(getRecordingsCount(panel), 2,
+     "The recordings list should have two recordings.");
+   isnot(PerformanceView.getState(), "empty",
+     "PerformanceView should not be in an empty state.");
+   isnot(PerformanceController.getCurrentRecording(), null,
+     "There should be a current recording.");
+ 
+   let recordingDeleted = times(PerformanceController, EVENTS.RECORDING_DELETED, 2);
+   let recordingStopped = once(PerformanceController, EVENTS.RECORDING_STATE_CHANGE, {
+     expectedArgs: { "1": "recording-stopped" }
+   });
+ 
+   PerformanceController.clearRecordings();
+ 
+-  yield recordingDeleted;
+-  yield recordingStopped;
++  await recordingDeleted;
++  await recordingStopped;
+ 
+   is(getRecordingsCount(panel), 0,
+     "The recordings list should be empty.");
+   is(PerformanceView.getState(), "empty",
+     "PerformanceView should be in an empty state.");
+   is(PerformanceController.getCurrentRecording(), null,
+     "There should be no current recording.");
+ 
+   // Bug 1169146: Try another recording after clearing mid-recording.
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   is(getRecordingsCount(panel), 1,
+     "The recordings list should have one recording.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-recordings-io-01.js b/devtools/client/performance/test/browser_perf-recordings-io-01.js
+--- a/devtools/client/performance/test/browser_perf-recordings-io-01.js
++++ b/devtools/client/performance/test/browser_perf-recordings-io-01.js
+@@ -1,73 +1,73 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ /* eslint-disable */
+ /**
+  * Tests if the performance tool is able to save and load recordings.
+  */
+ 
+-var test = Task.async(function* () {
+-  var { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
++var test = async function () {
++  var { target, panel, toolbox } = await initPerformance(SIMPLE_URL);
+   var { $, EVENTS, PerformanceController, PerformanceView, DetailsView, DetailsSubview } = panel.panelWin;
+ 
+   // Enable allocations to test the memory-calltree and memory-flamegraph.
+   Services.prefs.setBoolPref(ALLOCATIONS_PREF, true);
+   Services.prefs.setBoolPref(MEMORY_PREF, true);
+   Services.prefs.setBoolPref(FRAMERATE_PREF, true);
+ 
+   // Need to allow widgets to be updated while hidden, otherwise we can't use
+   // `waitForWidgetsRendered`.
+   DetailsSubview.canUpdateWhileHidden = true;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   // Cycle through all the views to initialize them, otherwise we can't use
+   // `waitForWidgetsRendered`. The waterfall is shown by default, but all the
+   // other views are created lazily, so won't emit any events.
+-  yield DetailsView.selectView("js-calltree");
+-  yield DetailsView.selectView("js-flamegraph");
+-  yield DetailsView.selectView("memory-calltree");
+-  yield DetailsView.selectView("memory-flamegraph");
++  await DetailsView.selectView("js-calltree");
++  await DetailsView.selectView("js-flamegraph");
++  await DetailsView.selectView("memory-calltree");
++  await DetailsView.selectView("memory-flamegraph");
+ 
+   // Verify original recording.
+ 
+   let originalData = PerformanceController.getCurrentRecording().getAllData();
+   ok(originalData, "The original recording is not empty.");
+ 
+   // Save recording.
+ 
+   let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
+   file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
+ 
+   let exported = once(PerformanceController, EVENTS.RECORDING_EXPORTED);
+-  yield PerformanceController.exportRecording("", PerformanceController.getCurrentRecording(), file);
++  await PerformanceController.exportRecording("", PerformanceController.getCurrentRecording(), file);
+ 
+-  yield exported;
++  await exported;
+   ok(true, "The recording data appears to have been successfully saved.");
+ 
+  //  Check if the imported file name has tmpprofile in it as the file
+  //  names also has different suffix to avoid conflict
+ 
+   let displayedName = $(".recording-item-title").getAttribute("value");
+   ok(/^tmpprofile/.test(displayedName), "File has expected display name after import");
+   ok(!/\.json$/.test(displayedName), "Display name does not have .json in it");
+ 
+   // Import recording.
+ 
+   let rerendered = waitForWidgetsRendered(panel);
+   let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
+   PerformanceView.emit(EVENTS.UI_IMPORT_RECORDING, file);
+ 
+-  yield imported;
++  await imported;
+   ok(true, "The recording data appears to have been successfully imported.");
+ 
+-  yield rerendered;
++  await rerendered;
+   ok(true, "The imported data was re-rendered.");
+ 
+   // Verify imported recording.
+ 
+   let importedData = PerformanceController.getCurrentRecording().getAllData();
+ 
+   ok(/^tmpprofile/.test(importedData.label),
+     "The imported data label is identical to the filename without its extension.");
+@@ -83,12 +83,12 @@ var test = Task.async(function* () {
+     "The imported data is identical to the original data (5).");
+   is(importedData.profile.toSource(), originalData.profile.toSource(),
+     "The imported data is identical to the original data (6).");
+   is(importedData.configuration.withTicks, originalData.configuration.withTicks,
+     "The imported data is identical to the original data (7).");
+   is(importedData.configuration.withMemory, originalData.configuration.withMemory,
+     "The imported data is identical to the original data (8).");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+-});
++};
+ /* eslint-enable */
+diff --git a/devtools/client/performance/test/browser_perf-recordings-io-02.js b/devtools/client/performance/test/browser_perf-recordings-io-02.js
+--- a/devtools/client/performance/test/browser_perf-recordings-io-02.js
++++ b/devtools/client/performance/test/browser_perf-recordings-io-02.js
+@@ -1,26 +1,26 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ /* eslint-disable */
+ /**
+  * Tests if the performance tool gracefully handles loading bogus files.
+  */
+ 
+-var test = Task.async(function* () {
+-  let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
++var test = async function () {
++  let { target, panel, toolbox } = await initPerformance(SIMPLE_URL);
+   let { EVENTS, PerformanceController } = panel.panelWin;
+ 
+   let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
+   file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
+ 
+   try {
+-    yield PerformanceController.importRecording("", file);
++    await PerformanceController.importRecording("", file);
+     ok(false, "The recording succeeded unexpectedly.");
+   } catch (e) {
+     is(e.message, "Could not read recording data file.", "Message is correct.");
+     ok(true, "The recording was cancelled.");
+   }
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+-});
++};
+diff --git a/devtools/client/performance/test/browser_perf-recordings-io-03.js b/devtools/client/performance/test/browser_perf-recordings-io-03.js
+--- a/devtools/client/performance/test/browser_perf-recordings-io-03.js
++++ b/devtools/client/performance/test/browser_perf-recordings-io-03.js
+@@ -5,35 +5,35 @@
+ /**
+  * Tests if the performance tool gracefully handles loading files that are JSON,
+  * but don't contain the appropriate recording data.
+  */
+ 
+ var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {});
+ var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", {});
+ 
+-var test = Task.async(function* () {
+-  let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
++var test = async function () {
++  let { target, panel, toolbox } = await initPerformance(SIMPLE_URL);
+   let { EVENTS, PerformanceController } = panel.panelWin;
+ 
+   let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
+   file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
+-  yield asyncCopy({ bogus: "data" }, file);
++  await asyncCopy({ bogus: "data" }, file);
+ 
+   try {
+-    yield PerformanceController.importRecording("", file);
++    await PerformanceController.importRecording("", file);
+     ok(false, "The recording succeeded unexpectedly.");
+   } catch (e) {
+     is(e.message, "Unrecognized recording data file.", "Message is correct.");
+     ok(true, "The recording was cancelled.");
+   }
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+-});
++};
+ 
+ function getUnicodeConverter() {
+   let className = "@mozilla.org/intl/scriptableunicodeconverter";
+   let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
+   converter.charset = "UTF-8";
+   return converter;
+ }
+ 
+diff --git a/devtools/client/performance/test/browser_perf-recordings-io-04.js b/devtools/client/performance/test/browser_perf-recordings-io-04.js
+--- a/devtools/client/performance/test/browser_perf-recordings-io-04.js
++++ b/devtools/client/performance/test/browser_perf-recordings-io-04.js
+@@ -64,18 +64,18 @@ var PROFILER_DATA = (function () {
+   let meta = data.meta = {};
+   meta.version = 2;
+   meta.interval = 1;
+   meta.stackwalk = 0;
+   meta.product = "Firefox";
+   return data;
+ })();
+ 
+-var test = Task.async(function* () {
+-  let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
++var test = async function () {
++  let { target, panel, toolbox } = await initPerformance(SIMPLE_URL);
+   let { $, EVENTS, PerformanceController, DetailsView, OverviewView, JsCallTreeView } = panel.panelWin;
+ 
+   // Enable memory to test the memory-calltree and memory-flamegraph.
+   Services.prefs.setBoolPref(ALLOCATIONS_PREF, true);
+ 
+   // Create a structure from the data that mimics the old profiler's data.
+   // Different name for `ticks`, different way of storing time,
+   // and no memory, markers data.
+@@ -85,30 +85,30 @@ var test = Task.async(function* () {
+     recordingDuration: 10000,
+     fileType: "Recorded Performance Data",
+     version: 1
+   };
+ 
+   // Save recording as an old profiler data.
+   let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
+   file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
+-  yield asyncCopy(oldProfilerData, file);
++  await asyncCopy(oldProfilerData, file);
+ 
+   // Import recording.
+ 
+   let calltreeRendered = once(OverviewView, EVENTS.UI_FRAMERATE_GRAPH_RENDERED);
+   let fpsRendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+   let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
+-  yield PerformanceController.importRecording("", file);
++  await PerformanceController.importRecording("", file);
+ 
+-  yield imported;
++  await imported;
+   ok(true, "The original profiler data appears to have been successfully imported.");
+ 
+-  yield calltreeRendered;
+-  yield fpsRendered;
++  await calltreeRendered;
++  await fpsRendered;
+   ok(true, "The imported data was re-rendered.");
+ 
+   // Ensure that only framerate and js calltree/flamegraph view are available
+   is(isVisible($("#overview-pane")), true, "overview graph container still shown");
+   is(isVisible($("#memory-overview")), false, "memory graph hidden");
+   is(isVisible($("#markers-overview")), false, "markers overview graph hidden");
+   is(isVisible($("#time-framerate")), true, "fps graph shown");
+   is($("#select-waterfall-view").hidden, true, "waterfall button hidden");
+@@ -143,19 +143,19 @@ var test = Task.async(function* () {
+       is(importedData.profile.meta.version, 3, "Updated meta version to 3.");
+     } else {
+       let data = importedData[field];
+       is(typeof data === "object" ? data.toSource() : data, expected[field],
+         `${field} successfully converted in legacy import.`);
+     }
+   }
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+-});
++};
+ 
+ function getUnicodeConverter() {
+   let className = "@mozilla.org/intl/scriptableunicodeconverter";
+   let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
+   converter.charset = "UTF-8";
+   return converter;
+ }
+ 
+diff --git a/devtools/client/performance/test/browser_perf-recordings-io-05.js b/devtools/client/performance/test/browser_perf-recordings-io-05.js
+--- a/devtools/client/performance/test/browser_perf-recordings-io-05.js
++++ b/devtools/client/performance/test/browser_perf-recordings-io-05.js
+@@ -2,42 +2,42 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ /* eslint-disable */
+ /**
+  * Test that when importing and no graphs rendered yet, we do not get a
+  * `getMappedSelection` error.
+  */
+ 
+-var test = Task.async(function* () {
+-  var { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
++var test = async function () {
++  var { target, panel, toolbox } = await initPerformance(SIMPLE_URL);
+   var { EVENTS, PerformanceController, WaterfallView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   // Save recording.
+ 
+   let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
+   file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
+ 
+   let exported = once(PerformanceController, EVENTS.RECORDING_EXPORTED);
+-  yield PerformanceController.exportRecording("", PerformanceController.getCurrentRecording(), file);
++  await PerformanceController.exportRecording("", PerformanceController.getCurrentRecording(), file);
+ 
+-  yield exported;
++  await exported;
+   ok(true, "The recording data appears to have been successfully saved.");
+ 
+   // Clear and re-import.
+ 
+-  yield PerformanceController.clearRecordings();
++  await PerformanceController.clearRecordings();
+ 
+   let rendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+   let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
+-  yield PerformanceController.importRecording("", file);
+-  yield imported;
+-  yield rendered;
++  await PerformanceController.importRecording("", file);
++  await imported;
++  await rendered;
+ 
+   ok(true, "No error was thrown.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+-});
++};
+ /* eslint-enable */
+diff --git a/devtools/client/performance/test/browser_perf-recordings-io-06.js b/devtools/client/performance/test/browser_perf-recordings-io-06.js
+--- a/devtools/client/performance/test/browser_perf-recordings-io-06.js
++++ b/devtools/client/performance/test/browser_perf-recordings-io-06.js
+@@ -79,47 +79,47 @@ var PROFILER_DATA = (function () {
+   let meta = data.meta = {};
+   meta.version = 2;
+   meta.interval = 1;
+   meta.stackwalk = 0;
+   meta.product = "Firefox";
+   return data;
+ })();
+ 
+-var test = Task.async(function* () {
+-  let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
++var test = async function () {
++  let { target, panel, toolbox } = await initPerformance(SIMPLE_URL);
+   let { $, EVENTS, PerformanceController, DetailsView, JsCallTreeView } = panel.panelWin;
+ 
+   let profilerData = {
+     profile: PROFILER_DATA,
+     duration: 10000,
+     configuration: {},
+     fileType: "Recorded Performance Data",
+     version: 2
+   };
+ 
+   let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
+   file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
+-  yield asyncCopy(profilerData, file);
++  await asyncCopy(profilerData, file);
+ 
+   // Import recording.
+ 
+   let calltreeRendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+   let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
+-  yield PerformanceController.importRecording("", file);
++  await PerformanceController.importRecording("", file);
+ 
+-  yield imported;
++  await imported;
+   ok(true, "The profiler data appears to have been successfully imported.");
+ 
+-  yield calltreeRendered;
++  await calltreeRendered;
+   ok(true, "The imported data was re-rendered.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+-});
++};
+ 
+ function getUnicodeConverter() {
+   let className = "@mozilla.org/intl/scriptableunicodeconverter";
+   let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
+   converter.charset = "UTF-8";
+   return converter;
+ }
+ 
+diff --git a/devtools/client/performance/test/browser_perf-refresh.js b/devtools/client/performance/test/browser_perf-refresh.js
+--- a/devtools/client/performance/test/browser_perf-refresh.js
++++ b/devtools/client/performance/test/browser_perf-refresh.js
+@@ -6,31 +6,31 @@
+  * Rough test that the recording still continues after a refresh.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording, reload } = require("devtools/client/performance/test/helpers/actions");
+ const { waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
+ 
+-add_task(function* () {
+-  let { panel, target } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel, target } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { PerformanceController } = panel.panelWin;
+ 
+-  yield startRecording(panel);
+-  yield reload(target);
++  await startRecording(panel);
++  await reload(target);
+ 
+   let recording = PerformanceController.getCurrentRecording();
+   let markersLength = recording.getAllData().markers.length;
+ 
+   ok(recording.isRecording(),
+     "RecordingModel should still be recording after reload");
+ 
+-  yield waitUntil(() => recording.getMarkers().length > markersLength);
++  await waitUntil(() => recording.getMarkers().length > markersLength);
+   ok("Markers continue after reload.");
+ 
+-  yield stopRecording(panel);
+-  yield teardownToolboxAndRemoveTab(panel);
++  await stopRecording(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-states.js b/devtools/client/performance/test/browser_perf-states.js
+--- a/devtools/client/performance/test/browser_perf-states.js
++++ b/devtools/client/performance/test/browser_perf-states.js
+@@ -6,18 +6,18 @@
+  * Tests that view states and lazy component intialization works.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_MEMORY_PREF, UI_ENABLE_ALLOCATIONS_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { PerformanceView, OverviewView, DetailsView } = panel.panelWin;
+ 
+   is(PerformanceView.getState(), "empty",
+     "The intial state of the performance panel view is correct.");
+@@ -45,58 +45,58 @@ add_task(function* () {
+ 
+   ok(!(OverviewView.graphs.get("timeline")),
+     "The markers graph should still not have been created yet.");
+   ok(!(OverviewView.graphs.get("memory")),
+     "The memory graph should still not have been created yet.");
+   ok(!(OverviewView.graphs.get("framerate")),
+     "The framerate graph should still not have been created yet.");
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+   is(PerformanceView.getState(), "recording",
+     "The current state of the performance panel view is 'recording'.");
+   ok(OverviewView.graphs.get("memory"),
+     "The memory graph should have been created now.");
+   ok(OverviewView.graphs.get("framerate"),
+     "The framerate graph should have been created now.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   is(PerformanceView.getState(), "recorded",
+     "The current state of the performance panel view is 'recorded'.");
+   ok(!DetailsView.components["js-calltree"].initialized,
+     "The js-calltree detail view should still not have been created yet.");
+   ok(!DetailsView.components["js-flamegraph"].initialized,
+     "The js-flamegraph detail view should still not have been created yet.");
+   ok(!DetailsView.components["memory-calltree"].initialized,
+     "The memory-calltree detail view should still not have been created yet.");
+   ok(!DetailsView.components["memory-flamegraph"].initialized,
+     "The memory-flamegraph detail view should still not have been created yet.");
+ 
+-  yield DetailsView.selectView("js-calltree");
++  await DetailsView.selectView("js-calltree");
+ 
+   is(PerformanceView.getState(), "recorded",
+     "The current state of the performance panel view is still 'recorded'.");
+   ok(DetailsView.components["js-calltree"].initialized,
+     "The js-calltree detail view should still have been created now.");
+   ok(!DetailsView.components["js-flamegraph"].initialized,
+     "The js-flamegraph detail view should still not have been created yet.");
+   ok(!DetailsView.components["memory-calltree"].initialized,
+     "The memory-calltree detail view should still not have been created yet.");
+   ok(!DetailsView.components["memory-flamegraph"].initialized,
+     "The memory-flamegraph detail view should still not have been created yet.");
+ 
+-  yield DetailsView.selectView("memory-calltree");
++  await DetailsView.selectView("memory-calltree");
+ 
+   is(PerformanceView.getState(), "recorded",
+     "The current state of the performance panel view is still 'recorded'.");
+   ok(DetailsView.components["js-calltree"].initialized,
+     "The js-calltree detail view should still register as being created.");
+   ok(!DetailsView.components["js-flamegraph"].initialized,
+     "The js-flamegraph detail view should still not have been created yet.");
+   ok(DetailsView.components["memory-calltree"].initialized,
+     "The memory-calltree detail view should still have been created now.");
+   ok(!DetailsView.components["memory-flamegraph"].initialized,
+     "The memory-flamegraph detail view should still not have been created yet.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-telemetry-01.js b/devtools/client/performance/test/browser_perf-telemetry-01.js
+--- a/devtools/client/performance/test/browser_perf-telemetry-01.js
++++ b/devtools/client/performance/test/browser_perf-telemetry-01.js
+@@ -7,47 +7,47 @@
+  * Specificaly the state about a recording process.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { UI_ENABLE_MEMORY_PREF } = require("devtools/client/performance/test/helpers/prefs");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { PerformanceController } = panel.panelWin;
+ 
+   let telemetry = PerformanceController._telemetry;
+   let logs = telemetry.getLogs();
+   let DURATION = "DEVTOOLS_PERFTOOLS_RECORDING_DURATION_MS";
+   let COUNT = "DEVTOOLS_PERFTOOLS_RECORDING_COUNT";
+   let CONSOLE_COUNT = "DEVTOOLS_PERFTOOLS_CONSOLE_RECORDING_COUNT";
+   let FEATURES = "DEVTOOLS_PERFTOOLS_RECORDING_FEATURES_USED";
+ 
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, false);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   Services.prefs.setBoolPref(UI_ENABLE_MEMORY_PREF, true);
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   is(logs[DURATION].length, 2, `There are two entries for ${DURATION}.`);
+   ok(logs[DURATION].every(d => typeof d === "number"),
+      `Every ${DURATION} entry is a number.`);
+   is(logs[COUNT].length, 2, `There are two entries for ${COUNT}.`);
+   is(logs[CONSOLE_COUNT], void 0, `There are no entries for ${CONSOLE_COUNT}.`);
+   is(logs[FEATURES].length, 8,
+      `There are two recordings worth of entries for ${FEATURES}.`);
+   ok(logs[FEATURES].find(r => r[0] === "withMemory" && r[1] === true),
+      "One feature entry for memory enabled.");
+   ok(logs[FEATURES].find(r => r[0] === "withMemory" && r[1] === false),
+     "One feature entry for memory disabled.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-telemetry-02.js b/devtools/client/performance/test/browser_perf-telemetry-02.js
+--- a/devtools/client/performance/test/browser_perf-telemetry-02.js
++++ b/devtools/client/performance/test/browser_perf-telemetry-02.js
+@@ -7,42 +7,42 @@
+  * Specifically export/import.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { EVENTS, PerformanceController } = panel.panelWin;
+ 
+   let telemetry = PerformanceController._telemetry;
+   let logs = telemetry.getLogs();
+   let EXPORTED = "DEVTOOLS_PERFTOOLS_RECORDING_EXPORT_FLAG";
+   let IMPORTED = "DEVTOOLS_PERFTOOLS_RECORDING_IMPORT_FLAG";
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
+   file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
+ 
+   let exported = once(PerformanceController, EVENTS.RECORDING_EXPORTED);
+-  yield PerformanceController.exportRecording("",
++  await PerformanceController.exportRecording("",
+     PerformanceController.getCurrentRecording(), file);
+-  yield exported;
++  await exported;
+ 
+   ok(logs[EXPORTED], `A telemetry entry for ${EXPORTED} exists after exporting.`);
+ 
+   let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
+-  yield PerformanceController.importRecording(null, file);
+-  yield imported;
++  await PerformanceController.importRecording(null, file);
++  await imported;
+ 
+   ok(logs[IMPORTED], `A telemetry entry for ${IMPORTED} exists after importing.`);
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-telemetry-03.js b/devtools/client/performance/test/browser_perf-telemetry-03.js
+--- a/devtools/client/performance/test/browser_perf-telemetry-03.js
++++ b/devtools/client/performance/test/browser_perf-telemetry-03.js
+@@ -7,48 +7,48 @@
+  * Specifically the destruction of certain views.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let {
+     EVENTS,
+     PerformanceController,
+     DetailsView,
+     JsCallTreeView,
+     JsFlameGraphView
+   } = panel.panelWin;
+ 
+   let telemetry = PerformanceController._telemetry;
+   let logs = telemetry.getLogs();
+   let VIEWS = "DEVTOOLS_PERFTOOLS_SELECTED_VIEW_MS";
+ 
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+   let calltreeRendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+   let flamegraphRendered = once(JsFlameGraphView, EVENTS.UI_JS_FLAMEGRAPH_RENDERED);
+ 
+   // Go through some views to check later.
+-  yield DetailsView.selectView("js-calltree");
+-  yield calltreeRendered;
++  await DetailsView.selectView("js-calltree");
++  await calltreeRendered;
+ 
+-  yield DetailsView.selectView("js-flamegraph");
+-  yield flamegraphRendered;
++  await DetailsView.selectView("js-flamegraph");
++  await flamegraphRendered;
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ 
+   // Check views after destruction to ensure `js-flamegraph` gets called
+   // with a time during destruction.
+   ok(logs[VIEWS].find(r => r[0] === "waterfall" && typeof r[1] === "number"),
+      `${VIEWS} for waterfall view and time.`);
+   ok(logs[VIEWS].find(r => r[0] === "js-calltree" && typeof r[1] === "number"),
+      `${VIEWS} for js-calltree view and time.`);
+   ok(logs[VIEWS].find(r => r[0] === "js-flamegraph" && typeof r[1] === "number"),
+diff --git a/devtools/client/performance/test/browser_perf-telemetry-04.js b/devtools/client/performance/test/browser_perf-telemetry-04.js
+--- a/devtools/client/performance/test/browser_perf-telemetry-04.js
++++ b/devtools/client/performance/test/browser_perf-telemetry-04.js
+@@ -5,46 +5,46 @@
+ /**
+  * Tests that the performance telemetry module records events at appropriate times.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInTab, initConsoleInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { waitForRecordingStartedEvents, waitForRecordingStoppedEvents } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { target, console } = yield initConsoleInNewTab({
++add_task(async function() {
++  let { target, console } = await initConsoleInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+-  let { panel } = yield initPerformanceInTab({ tab: target.tab });
++  let { panel } = await initPerformanceInTab({ tab: target.tab });
+   let { PerformanceController } = panel.panelWin;
+ 
+   let telemetry = PerformanceController._telemetry;
+   let logs = telemetry.getLogs();
+   let DURATION = "DEVTOOLS_PERFTOOLS_RECORDING_DURATION_MS";
+   let CONSOLE_COUNT = "DEVTOOLS_PERFTOOLS_CONSOLE_RECORDING_COUNT";
+   let FEATURES = "DEVTOOLS_PERFTOOLS_RECORDING_FEATURES_USED";
+ 
+   let started = waitForRecordingStartedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profile("rust");
+-  yield started;
++  await console.profile("rust");
++  await started;
+ 
+   let stopped = waitForRecordingStoppedEvents(panel, {
+     // only emitted for manual recordings
+     skipWaitingForBackendReady: true
+   });
+-  yield console.profileEnd("rust");
+-  yield stopped;
++  await console.profileEnd("rust");
++  await stopped;
+ 
+   is(logs[DURATION].length, 1, `There is one entry for ${DURATION}.`);
+   ok(logs[DURATION].every(d => typeof d === "number"),
+      `Every ${DURATION} entry is a number.`);
+   is(logs[CONSOLE_COUNT].length, 1, `There is one entry for ${CONSOLE_COUNT}.`);
+   is(logs[FEATURES].length, 4,
+      `There is one recording worth of entries for ${FEATURES}.`);
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-theme-toggle.js b/devtools/client/performance/test/browser_perf-theme-toggle.js
+--- a/devtools/client/performance/test/browser_perf-theme-toggle.js
++++ b/devtools/client/performance/test/browser_perf-theme-toggle.js
+@@ -12,67 +12,67 @@ const { setTheme } = require("devtools/c
+ const LIGHT_BG = "white";
+ const DARK_BG = "#14171a";
+ 
+ setTheme("dark");
+ Services.prefs.setBoolPref(MEMORY_PREF, false);
+ 
+ requestLongerTimeout(2);
+ 
+-function* spawnTest() {
+-  let { panel } = yield initPerformance(SIMPLE_URL);
++async function spawnTest() {
++  let { panel } = await initPerformance(SIMPLE_URL);
+   let { EVENTS, $, OverviewView, document: doc } = panel.panelWin;
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   let markers = OverviewView.graphs.get("timeline");
+   is(markers.backgroundColor, DARK_BG,
+     "correct theme on load for markers.");
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   let refreshed = once(markers, "refresh");
+   setTheme("light");
+-  yield refreshed;
++  await refreshed;
+ 
+   ok(true, "markers were rerendered after theme change.");
+   is(markers.backgroundColor, LIGHT_BG,
+     "correct theme on after toggle for markers.");
+ 
+   // reset back to dark
+   refreshed = once(markers, "refresh");
+   setTheme("dark");
+-  yield refreshed;
++  await refreshed;
+ 
+   info("Testing with memory overview");
+ 
+   Services.prefs.setBoolPref(MEMORY_PREF, true);
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   let memory = OverviewView.graphs.get("memory");
+   is(memory.backgroundColor, DARK_BG,
+     "correct theme on load for memory.");
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   refreshed = Promise.all([
+     once(markers, "refresh"),
+     once(memory, "refresh"),
+   ]);
+   setTheme("light");
+-  yield refreshed;
++  await refreshed;
+ 
+   ok(true, "Both memory and markers were rerendered after theme change.");
+   is(markers.backgroundColor, LIGHT_BG,
+     "correct theme on after toggle for markers.");
+   is(memory.backgroundColor, LIGHT_BG,
+     "correct theme on after toggle for memory.");
+ 
+   refreshed = Promise.all([
+     once(markers, "refresh"),
+     once(memory, "refresh"),
+   ]);
+ 
+   // Set theme back to light
+   setTheme("light");
+-  yield refreshed;
++  await refreshed;
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ /* eslint-enable */
+diff --git a/devtools/client/performance/test/browser_perf-tree-abstract-01.js b/devtools/client/performance/test/browser_perf-tree-abstract-01.js
+--- a/devtools/client/performance/test/browser_perf-tree-abstract-01.js
++++ b/devtools/client/performance/test/browser_perf-tree-abstract-01.js
+@@ -6,21 +6,21 @@
+  * Tests if the abstract tree base class for the profiler's tree view
+  * works as advertised.
+  */
+ 
+ const { appendAndWaitForPaint } = require("devtools/client/performance/test/helpers/dom-utils");
+ const { synthesizeCustomTreeClass } = require("devtools/client/performance/test/helpers/synth-utils");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let { MyCustomTreeItem, myDataSrc } = synthesizeCustomTreeClass();
+ 
+   let container = document.createElement("vbox");
+-  yield appendAndWaitForPaint(gBrowser.selectedBrowser.parentNode, container);
++  await appendAndWaitForPaint(gBrowser.selectedBrowser.parentNode, container);
+ 
+   // Populate the tree and test the root item...
+ 
+   let treeRoot = new MyCustomTreeItem(myDataSrc, { parent: null });
+   treeRoot.attachTo(container);
+ 
+   ok(!treeRoot.expanded,
+     "The root node should not be expanded yet.");
+@@ -46,21 +46,21 @@ add_task(function* () {
+     "The root node's container is correct.");
+ 
+   // Expand the root and test the child items...
+ 
+   let receivedExpandEvent = once(treeRoot, "expand", { spreadArgs: true });
+   let receivedFocusEvent = once(treeRoot, "focus");
+   mousedown(treeRoot.target.querySelector(".arrow"));
+ 
+-  let [eventItem] = yield receivedExpandEvent;
++  let [eventItem] = await receivedExpandEvent;
+   is(eventItem, treeRoot,
+     "The 'expand' event target is correct (1).");
+ 
+-  yield receivedFocusEvent;
++  await receivedFocusEvent;
+   is(document.commandDispatcher.focusedElement, treeRoot.target,
+     "The root node is now focused.");
+ 
+   let fooItem = treeRoot.getChild(0);
+   let barItem = treeRoot.getChild(1);
+ 
+   is(container.childNodes.length, 3,
+     "The container node should now have three children available.");
+@@ -97,33 +97,33 @@ add_task(function* () {
+   is(barItem.container, container,
+     "The 'bar' node's container is correct.");
+ 
+   // Test clicking on the `foo` node...
+ 
+   receivedFocusEvent = once(treeRoot, "focus", { spreadArgs: true });
+   mousedown(fooItem.target);
+ 
+-  [eventItem] = yield receivedFocusEvent;
++  [eventItem] = await receivedFocusEvent;
+   is(eventItem, fooItem,
+     "The 'focus' event target is correct (2).");
+   is(document.commandDispatcher.focusedElement, fooItem.target,
+     "The 'foo' node is now focused.");
+ 
+   // Test double clicking on the `bar` node...
+ 
+   receivedExpandEvent = once(treeRoot, "expand", { spreadArgs: true });
+   receivedFocusEvent = once(treeRoot, "focus");
+   dblclick(barItem.target);
+ 
+-  [eventItem] = yield receivedExpandEvent;
++  [eventItem] = await receivedExpandEvent;
+   is(eventItem, barItem,
+     "The 'expand' event target is correct (3).");
+ 
+-  yield receivedFocusEvent;
++  await receivedFocusEvent;
+   is(document.commandDispatcher.focusedElement, barItem.target,
+     "The 'foo' node is now focused.");
+ 
+   // A child item got expanded, test the descendants...
+ 
+   let bazItem = barItem.getChild(0);
+ 
+   is(container.childNodes.length, 4,
+diff --git a/devtools/client/performance/test/browser_perf-tree-abstract-02.js b/devtools/client/performance/test/browser_perf-tree-abstract-02.js
+--- a/devtools/client/performance/test/browser_perf-tree-abstract-02.js
++++ b/devtools/client/performance/test/browser_perf-tree-abstract-02.js
+@@ -5,21 +5,21 @@
+ /**
+  * Tests if the abstract tree base class for the profiler's tree view
+  * has a functional public API.
+  */
+ 
+ const { appendAndWaitForPaint } = require("devtools/client/performance/test/helpers/dom-utils");
+ const { synthesizeCustomTreeClass } = require("devtools/client/performance/test/helpers/synth-utils");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let { MyCustomTreeItem, myDataSrc } = synthesizeCustomTreeClass();
+ 
+   let container = document.createElement("vbox");
+-  yield appendAndWaitForPaint(gBrowser.selectedBrowser.parentNode, container);
++  await appendAndWaitForPaint(gBrowser.selectedBrowser.parentNode, container);
+ 
+   // Populate the tree and test the root item...
+ 
+   let treeRoot = new MyCustomTreeItem(myDataSrc, { parent: null });
+   treeRoot.autoExpandDepth = 1;
+   treeRoot.attachTo(container);
+ 
+   ok(treeRoot.expanded,
+diff --git a/devtools/client/performance/test/browser_perf-tree-abstract-03.js b/devtools/client/performance/test/browser_perf-tree-abstract-03.js
+--- a/devtools/client/performance/test/browser_perf-tree-abstract-03.js
++++ b/devtools/client/performance/test/browser_perf-tree-abstract-03.js
+@@ -5,21 +5,21 @@
+ /**
+  * Tests if the abstract tree base class for the profiler's tree view
+  * is keyboard accessible.
+  */
+ 
+ const { appendAndWaitForPaint } = require("devtools/client/performance/test/helpers/dom-utils");
+ const { synthesizeCustomTreeClass } = require("devtools/client/performance/test/helpers/synth-utils");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let { MyCustomTreeItem, myDataSrc } = synthesizeCustomTreeClass();
+ 
+   let container = document.createElement("vbox");
+-  yield appendAndWaitForPaint(gBrowser.selectedBrowser.parentNode, container);
++  await appendAndWaitForPaint(gBrowser.selectedBrowser.parentNode, container);
+ 
+   // Populate the tree by pressing RIGHT...
+ 
+   let treeRoot = new MyCustomTreeItem(myDataSrc, { parent: null });
+   treeRoot.attachTo(container);
+   treeRoot.focus();
+ 
+   key("VK_RIGHT");
+diff --git a/devtools/client/performance/test/browser_perf-tree-abstract-04.js b/devtools/client/performance/test/browser_perf-tree-abstract-04.js
+--- a/devtools/client/performance/test/browser_perf-tree-abstract-04.js
++++ b/devtools/client/performance/test/browser_perf-tree-abstract-04.js
+@@ -5,31 +5,31 @@
+ /**
+  * Tests that the treeview expander arrow doesn't react to dblclick events.
+  */
+ 
+ const { appendAndWaitForPaint } = require("devtools/client/performance/test/helpers/dom-utils");
+ const { synthesizeCustomTreeClass } = require("devtools/client/performance/test/helpers/synth-utils");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let { MyCustomTreeItem, myDataSrc } = synthesizeCustomTreeClass();
+ 
+   let container = document.createElement("vbox");
+-  yield appendAndWaitForPaint(gBrowser.selectedBrowser.parentNode, container);
++  await appendAndWaitForPaint(gBrowser.selectedBrowser.parentNode, container);
+ 
+   // Populate the tree and test the root item...
+ 
+   let treeRoot = new MyCustomTreeItem(myDataSrc, { parent: null });
+   treeRoot.attachTo(container);
+ 
+   let originalTreeRootExpandedState = treeRoot.expanded;
+   info("Double clicking on the root item arrow and waiting for focus event.");
+ 
+   let receivedFocusEvent = once(treeRoot, "focus");
+   dblclick(treeRoot.target.querySelector(".arrow"));
+-  yield receivedFocusEvent;
++  await receivedFocusEvent;
+ 
+   is(treeRoot.expanded, originalTreeRootExpandedState,
+     "A double click on the arrow was ignored.");
+ 
+   container.remove();
+ });
+diff --git a/devtools/client/performance/test/browser_perf-tree-abstract-05.js b/devtools/client/performance/test/browser_perf-tree-abstract-05.js
+--- a/devtools/client/performance/test/browser_perf-tree-abstract-05.js
++++ b/devtools/client/performance/test/browser_perf-tree-abstract-05.js
+@@ -5,23 +5,23 @@
+ /**
+  * Tests if the abstract tree base class for the profiler's tree view
+  * supports PageUp/PageDown/Home/End keys.
+  */
+ 
+ const { appendAndWaitForPaint } = require("devtools/client/performance/test/helpers/dom-utils");
+ const { synthesizeCustomTreeClass } = require("devtools/client/performance/test/helpers/synth-utils");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let { MyCustomTreeItem } = synthesizeCustomTreeClass();
+ 
+   let container = document.createElement("vbox");
+   container.style.height = "100%";
+   container.style.overflow = "scroll";
+-  yield appendAndWaitForPaint(gBrowser.selectedBrowser.parentNode, container);
++  await appendAndWaitForPaint(gBrowser.selectedBrowser.parentNode, container);
+ 
+   let myDataSrc = {
+     label: "root",
+     children: []
+   };
+ 
+   for (let i = 0; i < 1000; i++) {
+     myDataSrc.children.push({
+diff --git a/devtools/client/performance/test/browser_perf-tree-view-06.js b/devtools/client/performance/test/browser_perf-tree-view-06.js
+--- a/devtools/client/performance/test/browser_perf-tree-view-06.js
++++ b/devtools/client/performance/test/browser_perf-tree-view-06.js
+@@ -7,17 +7,17 @@
+  * correctly emits events when certain DOM nodes are clicked.
+  */
+ 
+ const { ThreadNode } = require("devtools/client/performance/modules/logic/tree-model");
+ const { CallView } = require("devtools/client/performance/modules/widgets/tree-view");
+ const { synthesizeProfile } = require("devtools/client/performance/test/helpers/synth-utils");
+ const { idleWait, waitUntil } = require("devtools/client/performance/test/helpers/wait-utils");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let profile = synthesizeProfile();
+   let threadNode = new ThreadNode(profile.threads[0], { startTime: 0, endTime: 20 });
+ 
+   // Don't display the synthesized (root) and the real (root) node twice.
+   threadNode.calls = threadNode.calls[0].calls;
+ 
+   let treeRoot = new CallView({ frame: threadNode });
+   let container = document.createElement("vbox");
+@@ -33,20 +33,20 @@ add_task(function* () {
+   };
+ 
+   treeRoot.on("link", handler);
+ 
+   // Fire right click.
+   rightMousedown(D.target.querySelector(".call-tree-url"));
+ 
+   // Ensure link was not called for right click.
+-  yield idleWait(100);
++  await idleWait(100);
+   ok(!linkEvent, "The `link` event not fired for right click.");
+ 
+   // Fire left click.
+   mousedown(D.target.querySelector(".call-tree-url"));
+ 
+   // Ensure link was called for left click.
+-  yield waitUntil(() => linkEvent);
++  await waitUntil(() => linkEvent);
+   is(linkEvent, D, "The `link` event target is correct.");
+ 
+   treeRoot.off("link", handler);
+ });
+diff --git a/devtools/client/performance/test/browser_perf-tree-view-11.js b/devtools/client/performance/test/browser_perf-tree-view-11.js
+--- a/devtools/client/performance/test/browser_perf-tree-view-11.js
++++ b/devtools/client/performance/test/browser_perf-tree-view-11.js
+@@ -4,35 +4,35 @@
+ /* eslint-disable */
+ /**
+  * Tests that if `show-jit-optimizations` is true, then an
+  * icon is next to the frame with optimizations
+  */
+ 
+ var { CATEGORY_MASK } = require("devtools/client/performance/modules/categories");
+ 
+-function* spawnTest() {
+-  let { panel } = yield initPerformance(SIMPLE_URL);
++async function spawnTest() {
++  let { panel } = await initPerformance(SIMPLE_URL);
+   let { EVENTS, $, $$, window, PerformanceController } = panel.panelWin;
+   let { OverviewView, DetailsView, JsCallTreeView } = panel.panelWin;
+ 
+   let profilerData = { threads: [gThread] };
+ 
+   Services.prefs.setBoolPref(JIT_PREF, true);
+   Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false);
+   Services.prefs.setBoolPref(INVERT_PREF, false);
+ 
+   // Make two recordings, so we have one to switch to later, as the
+   // second one will have fake sample data
+-  yield startRecording(panel);
+-  yield stopRecording(panel);
++  await startRecording(panel);
++  await stopRecording(panel);
+ 
+-  yield DetailsView.selectView("js-calltree");
++  await DetailsView.selectView("js-calltree");
+ 
+-  yield injectAndRenderProfilerData();
++  await injectAndRenderProfilerData();
+ 
+   let rows = $$("#js-calltree-view .call-tree-item");
+   is(rows.length, 4, "4 call tree rows exist");
+   for (let row of rows) {
+     let name = $(".call-tree-name", row).textContent.trim();
+     switch (name) {
+       case "A":
+         ok($(".opt-icon", row), "found an opt icon on a leaf node with opt data");
+@@ -47,29 +47,29 @@ function* spawnTest() {
+         ok(!$(".opt-icon", row), "root frame certainly does not have opt data");
+         break;
+       default:
+         ok(false, `Unidentified frame: ${name}`);
+         break;
+     }
+   }
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ 
+-  function* injectAndRenderProfilerData() {
++  async function injectAndRenderProfilerData() {
+     // Get current recording and inject our mock data
+     info("Injecting mock profile data");
+     let recording = PerformanceController.getCurrentRecording();
+     recording._profile = profilerData;
+ 
+     // Force a rerender
+     let rendered = once(JsCallTreeView, EVENTS.UI_JS_CALL_TREE_RENDERED);
+     JsCallTreeView.render(OverviewView.getTimeInterval());
+-    yield rendered;
++    await rendered;
+   }
+ }
+ 
+ var gUniqueStacks = new RecordingUtils.UniqueStacks();
+ 
+ function uniqStr(s) {
+   return gUniqueStacks.getOrAddStringIndex(s);
+ }
+diff --git a/devtools/client/performance/test/browser_perf-ui-recording.js b/devtools/client/performance/test/browser_perf-ui-recording.js
+--- a/devtools/client/performance/test/browser_perf-ui-recording.js
++++ b/devtools/client/performance/test/browser_perf-ui-recording.js
+@@ -7,33 +7,33 @@
+  * in the UI.
+  */
+ 
+ const { pmmLoadFrameScripts, pmmIsProfilerActive, pmmClearFrameScripts } = require("devtools/client/performance/test/helpers/profiler-mm-utils");
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   pmmLoadFrameScripts(gBrowser);
+ 
+-  ok(!(yield pmmIsProfilerActive()),
++  ok(!(await pmmIsProfilerActive()),
+     "The built-in profiler module should not have been automatically started.");
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+ 
+-  ok((yield pmmIsProfilerActive()),
++  ok((await pmmIsProfilerActive()),
+     "The built-in profiler module should now be active.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+-  ok((yield pmmIsProfilerActive()),
++  ok((await pmmIsProfilerActive()),
+     "The built-in profiler module should still be active.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ 
+   pmmClearFrameScripts();
+ });
+diff --git a/devtools/client/performance/test/browser_timeline-filters-01.js b/devtools/client/performance/test/browser_timeline-filters-01.js
+--- a/devtools/client/performance/test/browser_timeline-filters-01.js
++++ b/devtools/client/performance/test/browser_timeline-filters-01.js
+@@ -3,34 +3,34 @@
+ /* eslint-disable */
+ 
+ /**
+  * Tests markers filtering mechanism.
+  */
+ 
+ const EPSILON = 0.00000001;
+ 
+-function* spawnTest() {
+-  let { panel } = yield initPerformance(SIMPLE_URL);
++async function spawnTest() {
++  let { panel } = await initPerformance(SIMPLE_URL);
+   let { $, $$, EVENTS, PerformanceController, OverviewView, WaterfallView } = panel.panelWin;
+   let { TimelineGraph } = require("devtools/client/performance/modules/widgets/graphs");
+   let { rowHeight: MARKERS_GRAPH_ROW_HEIGHT } = TimelineGraph.prototype;
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   ok(true, "Recording has started.");
+ 
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     // Wait until we get 3 different markers.
+     let markers = PerformanceController.getCurrentRecording().getMarkers();
+     return markers.some(m => m.name == "Styles") &&
+            markers.some(m => m.name == "Reflow") &&
+            markers.some(m => m.name == "Paint");
+   });
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+   ok(true, "Recording has ended.");
+ 
+   // Push some fake markers of a type we do not have a blueprint for
+   let markers = PerformanceController.getCurrentRecording().getMarkers();
+   let endTime = markers[markers.length - 1].end;
+   markers.push({ name: "CustomMarker", start: endTime + EPSILON, end: endTime + (EPSILON * 2) });
+   markers.push({ name: "CustomMarker", start: endTime + (EPSILON * 3), end: endTime + (EPSILON * 4) });
+ 
+@@ -45,74 +45,74 @@ function* spawnTest() {
+   let menuItem1 = $("menuitem[marker-type=Styles]");
+   let menuItem2 = $("menuitem[marker-type=Reflow]");
+   let menuItem3 = $("menuitem[marker-type=Paint]");
+   let menuItem4 = $("menuitem[marker-type=UNKNOWN]");
+ 
+   let overview = OverviewView.graphs.get("timeline");
+   let originalHeight = overview.fixedHeight;
+ 
+-  yield waterfallRendered;
++  await waterfallRendered;
+ 
+   ok($(".waterfall-marker-bar[type=Styles]"), "Found at least one 'Styles' marker (1)");
+   ok($(".waterfall-marker-bar[type=Reflow]"), "Found at least one 'Reflow' marker (1)");
+   ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker (1)");
+   ok($(".waterfall-marker-bar[type=CustomMarker]"), "Found at least one 'Unknown' marker (1)");
+ 
+   let heightBefore = overview.fixedHeight;
+   EventUtils.synthesizeMouseAtCenter(menuItem1, {type: "mouseup"}, panel.panelWin);
+-  yield waitForOverviewAndCommand(overview, menuItem1);
++  await waitForOverviewAndCommand(overview, menuItem1);
+ 
+   is(overview.fixedHeight, heightBefore, "Overview height hasn't changed");
+   ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker (2)");
+   ok($(".waterfall-marker-bar[type=Reflow]"), "Found at least one 'Reflow' marker (2)");
+   ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker (2)");
+   ok($(".waterfall-marker-bar[type=CustomMarker]"), "Found at least one 'Unknown' marker (2)");
+ 
+   heightBefore = overview.fixedHeight;
+   EventUtils.synthesizeMouseAtCenter(menuItem2, {type: "mouseup"}, panel.panelWin);
+-  yield waitForOverviewAndCommand(overview, menuItem2);
++  await waitForOverviewAndCommand(overview, menuItem2);
+ 
+   is(overview.fixedHeight, heightBefore, "Overview height hasn't changed");
+   ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker (3)");
+   ok(!$(".waterfall-marker-bar[type=Reflow]"), "No 'Reflow' marker (3)");
+   ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker (3)");
+   ok($(".waterfall-marker-bar[type=CustomMarker]"), "Found at least one 'Unknown' marker (3)");
+ 
+   heightBefore = overview.fixedHeight;
+   EventUtils.synthesizeMouseAtCenter(menuItem3, {type: "mouseup"}, panel.panelWin);
+-  yield waitForOverviewAndCommand(overview, menuItem3);
++  await waitForOverviewAndCommand(overview, menuItem3);
+ 
+   is(overview.fixedHeight, heightBefore - MARKERS_GRAPH_ROW_HEIGHT, "Overview is smaller");
+   ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker (4)");
+   ok(!$(".waterfall-marker-bar[type=Reflow]"), "No 'Reflow' marker (4)");
+   ok(!$(".waterfall-marker-bar[type=Paint]"), "No 'Paint' marker (4)");
+   ok($(".waterfall-marker-bar[type=CustomMarker]"), "Found at least one 'Unknown' marker (4)");
+ 
+   EventUtils.synthesizeMouseAtCenter(menuItem4, {type: "mouseup"}, panel.panelWin);
+-  yield waitForOverviewAndCommand(overview, menuItem4);
++  await waitForOverviewAndCommand(overview, menuItem4);
+ 
+   ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker (5)");
+   ok(!$(".waterfall-marker-bar[type=Reflow]"), "No 'Reflow' marker (5)");
+   ok(!$(".waterfall-marker-bar[type=Paint]"), "No 'Paint' marker (5)");
+   ok(!$(".waterfall-marker-bar[type=CustomMarker]"), "No 'Unknown' marker (5)");
+ 
+   for (let item of [menuItem1, menuItem2, menuItem3]) {
+     EventUtils.synthesizeMouseAtCenter(item, {type: "mouseup"}, panel.panelWin);
+-    yield waitForOverviewAndCommand(overview, item);
++    await waitForOverviewAndCommand(overview, item);
+   }
+ 
+   ok($(".waterfall-marker-bar[type=Styles]"), "Found at least one 'Styles' marker (6)");
+   ok($(".waterfall-marker-bar[type=Reflow]"), "Found at least one 'Reflow' marker (6)");
+   ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker (6)");
+   ok(!$(".waterfall-marker-bar[type=CustomMarker]"), "No 'Unknown' marker (6)");
+ 
+   is(overview.fixedHeight, originalHeight, "Overview restored");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ 
+ function waitForOverviewAndCommand(overview, item) {
+   let overviewRendered = overview.once("refresh");
+   let menuitemCommandDispatched = once(item, "command");
+   return Promise.all([overviewRendered, menuitemCommandDispatched]);
+ }
+diff --git a/devtools/client/performance/test/browser_timeline-filters-02.js b/devtools/client/performance/test/browser_timeline-filters-02.js
+--- a/devtools/client/performance/test/browser_timeline-filters-02.js
++++ b/devtools/client/performance/test/browser_timeline-filters-02.js
+@@ -3,46 +3,46 @@
+ "use strict";
+ /* eslint-disable */
+ /**
+  * Tests markers filtering mechanism.
+  */
+ 
+ const URL = EXAMPLE_URL + "doc_innerHTML.html";
+ 
+-function* spawnTest() {
+-  let { panel } = yield initPerformance(URL);
++async function spawnTest() {
++  let { panel } = await initPerformance(URL);
+   let { $, $$, EVENTS, PerformanceController, OverviewView, WaterfallView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   ok(true, "Recording has started.");
+ 
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     let markers = PerformanceController.getCurrentRecording().getMarkers();
+     return markers.some(m => m.name == "Parse HTML") &&
+            markers.some(m => m.name == "Javascript");
+   });
+ 
+   let waterfallRendered = WaterfallView.once(EVENTS.UI_WATERFALL_RENDERED);
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+ 
+   $("#filter-button").click();
+   let filterJS = $("menuitem[marker-type=Javascript]");
+ 
+-  yield waterfallRendered;
++  await waterfallRendered;
+ 
+   ok($(".waterfall-marker-bar[type=Javascript]"), "Found at least one 'Javascript' marker");
+   ok(!$(".waterfall-marker-bar[type='Parse HTML']"), "Found no Parse HTML markers as they are nested still");
+ 
+   EventUtils.synthesizeMouseAtCenter(filterJS, {type: "mouseup"}, panel.panelWin);
+-  yield Promise.all([
++  await Promise.all([
+     WaterfallView.once(EVENTS.UI_WATERFALL_RENDERED),
+     once(filterJS, "command")
+   ]);
+ 
+   ok(!$(".waterfall-marker-bar[type=Javascript]"), "Javascript markers are all hidden.");
+   ok($(".waterfall-marker-bar[type='Parse HTML']"),
+     "Found at least one 'Parse HTML' marker still visible after hiding JS markers");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ /* eslint-enable */
+diff --git a/devtools/client/performance/test/browser_timeline-waterfall-background.js b/devtools/client/performance/test/browser_timeline-waterfall-background.js
+--- a/devtools/client/performance/test/browser_timeline-waterfall-background.js
++++ b/devtools/client/performance/test/browser_timeline-waterfall-background.js
+@@ -6,36 +6,36 @@
+  * Tests if the waterfall background is a 1px high canvas stretching across
+  * the container bounds.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording, waitForOverviewRenderedWithMarkers } = require("devtools/client/performance/test/helpers/actions");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { WaterfallView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   ok(true, "Recording has started.");
+ 
+   // Ensure overview is rendering and some markers were received.
+-  yield waitForOverviewRenderedWithMarkers(panel);
++  await waitForOverviewRenderedWithMarkers(panel);
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+   ok(true, "Recording has ended.");
+ 
+   // Test the waterfall background.
+ 
+   ok(WaterfallView.canvas, "A canvas should be created after the recording ended.");
+ 
+   is(WaterfallView.canvas.width, WaterfallView.waterfallWidth,
+     "The canvas width is correct.");
+   is(WaterfallView.canvas.height, 1,
+     "The canvas height is correct.");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_timeline-waterfall-generic.js b/devtools/client/performance/test/browser_timeline-waterfall-generic.js
+--- a/devtools/client/performance/test/browser_timeline-waterfall-generic.js
++++ b/devtools/client/performance/test/browser_timeline-waterfall-generic.js
+@@ -6,31 +6,31 @@
+  * Tests if the waterfall is properly built after finishing a recording.
+  */
+ 
+ const { SIMPLE_URL } = require("devtools/client/performance/test/helpers/urls");
+ const { initPerformanceInNewTab, teardownToolboxAndRemoveTab } = require("devtools/client/performance/test/helpers/panel-utils");
+ const { startRecording, stopRecording, waitForOverviewRenderedWithMarkers } = require("devtools/client/performance/test/helpers/actions");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+-add_task(function* () {
+-  let { panel } = yield initPerformanceInNewTab({
++add_task(async function() {
++  let { panel } = await initPerformanceInNewTab({
+     url: SIMPLE_URL,
+     win: window
+   });
+ 
+   let { $, $$, EVENTS, WaterfallView } = panel.panelWin;
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   ok(true, "Recording has started.");
+ 
+   // Ensure overview is rendering and some markers were received.
+-  yield waitForOverviewRenderedWithMarkers(panel);
++  await waitForOverviewRenderedWithMarkers(panel);
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+   ok(true, "Recording has ended.");
+ 
+   // Test the header container.
+ 
+   ok($(".waterfall-header"),
+     "A header container should have been created.");
+ 
+   // Test the header sidebar (left).
+@@ -77,17 +77,17 @@ add_task(function* () {
+     "The details view width should be 0 when hidden.");
+   is(WaterfallView.waterfallWidth,
+      parentWidthBefore - sidebarWidthBefore
+                        - WaterfallView.WATERFALL_MARKER_SIDEBAR_SAFE_BOUNDS,
+      "The waterfall width is correct (1).");
+ 
+   let waterfallRerendered = once(WaterfallView, EVENTS.UI_WATERFALL_RENDERED);
+   $$(".waterfall-tree-item")[0].click();
+-  yield waterfallRerendered;
++  await waterfallRerendered;
+ 
+   let parentWidthAfter = $("#waterfall-view").getBoundingClientRect().width;
+   let sidebarWidthAfter = $(".waterfall-sidebar").getBoundingClientRect().width;
+   let detailsWidthAfter = $("#waterfall-details").getBoundingClientRect().width;
+ 
+   ok(!detailsView.hidden,
+     "The details view in the waterfall view is now visible.");
+   is(parentWidthBefore, parentWidthAfter,
+@@ -96,10 +96,10 @@ add_task(function* () {
+     "The sidebar view's width should not have changed.");
+   isnot(detailsWidthAfter, 0,
+     "The details view width should not be 0 when visible.");
+   is(WaterfallView.waterfallWidth,
+      parentWidthAfter - sidebarWidthAfter - detailsWidthAfter
+                       - WaterfallView.WATERFALL_MARKER_SIDEBAR_SAFE_BOUNDS,
+      "The waterfall width is correct (2).");
+ 
+-  yield teardownToolboxAndRemoveTab(panel);
++  await teardownToolboxAndRemoveTab(panel);
+ });
+diff --git a/devtools/client/performance/test/browser_timeline-waterfall-rerender.js b/devtools/client/performance/test/browser_timeline-waterfall-rerender.js
+--- a/devtools/client/performance/test/browser_timeline-waterfall-rerender.js
++++ b/devtools/client/performance/test/browser_timeline-waterfall-rerender.js
+@@ -1,46 +1,46 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ /* eslint-disable */
+ /**
+  * Tests if the waterfall remembers the selection when rerendering.
+  */
+ 
+-function* spawnTest() {
+-  let { target, panel } = yield initPerformance(SIMPLE_URL);
++async function spawnTest() {
++  let { target, panel } = await initPerformance(SIMPLE_URL);
+   let { $, $$, EVENTS, PerformanceController, OverviewView, WaterfallView } = panel.panelWin;
+ 
+   const MIN_MARKERS_COUNT = 50;
+   const MAX_MARKERS_SELECT = 20;
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   ok(true, "Recording has started.");
+ 
+   let updated = 0;
+   OverviewView.on(EVENTS.UI_OVERVIEW_RENDERED, () => updated++);
+ 
+-  ok((yield waitUntil(() => updated > 0)),
++  ok((await waitUntil(() => updated > 0)),
+     "The overview graphs were updated a bunch of times.");
+-  ok((yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length > MIN_MARKERS_COUNT)),
++  ok((await waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length > MIN_MARKERS_COUNT)),
+     "There are some markers available.");
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+   ok(true, "Recording has ended.");
+ 
+   let currentMarkers = PerformanceController.getCurrentRecording().getMarkers();
+   info("Gathered markers: " + JSON.stringify(currentMarkers, null, 2));
+ 
+   let initialBarsCount = $$(".waterfall-marker-bar").length;
+   info("Initial bars count: " + initialBarsCount);
+ 
+   // Select a portion of the overview.
+   let rerendered = WaterfallView.once(EVENTS.UI_WATERFALL_RENDERED);
+   OverviewView.setTimeInterval({ startTime: 0, endTime: currentMarkers[MAX_MARKERS_SELECT].end });
+-  yield rerendered;
++  await rerendered;
+ 
+   ok(!$(".waterfall-tree-item:focus"),
+     "There is no item focused in the waterfall yet.");
+   ok($("#waterfall-details").hidden,
+     "The waterfall sidebar is initially hidden.");
+ 
+   // Focus the second item in the tree.
+   WaterfallView._markersRoot.getChild(1).focus();
+@@ -53,24 +53,24 @@ function* spawnTest() {
+   is(Array.indexOf($$(".waterfall-tree-item"), $(".waterfall-tree-item:focus")), 2,
+     "The correct item was focused in the tree.");
+   ok(!$("#waterfall-details").hidden,
+     "The waterfall sidebar is now visible.");
+ 
+   // Simulate a resize on the marker details.
+   rerendered = WaterfallView.once(EVENTS.UI_WATERFALL_RENDERED);
+   EventUtils.sendMouseEvent({ type: "mouseup" }, WaterfallView.detailsSplitter);
+-  yield rerendered;
++  await rerendered;
+ 
+   let afterResizeBarsCount = $$(".waterfall-marker-bar").length;
+   info("After resize bars count: " + afterResizeBarsCount);
+   is(afterResizeBarsCount, beforeResizeBarsCount,
+     "The same subset of the total markers remained visible.");
+ 
+   is(Array.indexOf($$(".waterfall-tree-item"), $(".waterfall-tree-item:focus")), 2,
+     "The correct item is still focused in the tree.");
+   ok(!$("#waterfall-details").hidden,
+     "The waterfall sidebar is still visible.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ /* eslint-enable */
+diff --git a/devtools/client/performance/test/browser_timeline-waterfall-sidebar.js b/devtools/client/performance/test/browser_timeline-waterfall-sidebar.js
+--- a/devtools/client/performance/test/browser_timeline-waterfall-sidebar.js
++++ b/devtools/client/performance/test/browser_timeline-waterfall-sidebar.js
+@@ -1,41 +1,41 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ /* eslint-disable */
+ /**
+  * Tests if the sidebar is properly updated when a marker is selected.
+  */
+ 
+-function* spawnTest() {
+-  let { target, panel } = yield initPerformance(SIMPLE_URL);
++async function spawnTest() {
++  let { target, panel } = await initPerformance(SIMPLE_URL);
+   let { $, $$, PerformanceController, WaterfallView } = panel.panelWin;
+   let { L10N } = require("devtools/client/performance/modules/global");
+   let { MarkerBlueprintUtils } = require("devtools/client/performance/modules/marker-blueprint-utils");
+ 
+   // Hijack the markers massaging part of creating the waterfall view,
+   // to prevent collapsing markers and allowing this test to verify
+   // everything individually. A better solution would be to just expand
+   // all markers first and then skip the meta nodes, but I'm lazy.
+   WaterfallView._prepareWaterfallTree = markers => {
+     return { submarkers: markers };
+   };
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   ok(true, "Recording has started.");
+ 
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     // Wait until we get 3 different markers.
+     let markers = PerformanceController.getCurrentRecording().getMarkers();
+     return markers.some(m => m.name == "Styles") &&
+            markers.some(m => m.name == "Reflow") &&
+            markers.some(m => m.name == "Paint");
+   });
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+   ok(true, "Recording has ended.");
+ 
+   info("No need to select everything in the timeline.");
+   info("All the markers should be displayed by default.");
+ 
+   let bars = $$(".waterfall-marker-bar");
+   let markers = PerformanceController.getCurrentRecording().getMarkers();
+ 
+@@ -66,12 +66,12 @@ function* spawnTest() {
+     is(toMs(mkr.end - mkr.start), duration, "Sidebar duration is valid.");
+ 
+     // For some reason, anything that creates "→" here turns it into a "â" for some reason.
+     // So just check that start and end time are in there somewhere.
+     ok(tooltip.includes(toMs(mkr.start)), "Tooltip has start time.");
+     ok(tooltip.includes(toMs(mkr.end)), "Tooltip has end time.");
+   }
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ /* eslint-enable */
+diff --git a/devtools/client/performance/test/browser_timeline-waterfall-workers.js b/devtools/client/performance/test/browser_timeline-waterfall-workers.js
+--- a/devtools/client/performance/test/browser_timeline-waterfall-workers.js
++++ b/devtools/client/performance/test/browser_timeline-waterfall-workers.js
+@@ -1,50 +1,50 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ /* eslint-disable */
+ /**
+  * Tests if the sidebar is properly updated with worker markers.
+  */
+ 
+-function* spawnTest() {
+-  let { panel } = yield initPerformance(WORKER_URL);
++async function spawnTest() {
++  let { panel } = await initPerformance(WORKER_URL);
+   let { $$, $, PerformanceController } = panel.panelWin;
+ 
+   loadFrameScripts();
+ 
+-  yield startRecording(panel);
++  await startRecording(panel);
+   ok(true, "Recording has started.");
+ 
+   evalInDebuggee("performWork()");
+ 
+-  yield waitUntil(() => {
++  await waitUntil(() => {
+     // Wait until we get the worker markers.
+     let markers = PerformanceController.getCurrentRecording().getMarkers();
+     if (!markers.some(m => m.name == "Worker") ||
+         !markers.some(m => m.workerOperation == "serializeDataOffMainThread") ||
+         !markers.some(m => m.workerOperation == "serializeDataOnMainThread") ||
+         !markers.some(m => m.workerOperation == "deserializeDataOffMainThread") ||
+         !markers.some(m => m.workerOperation == "deserializeDataOnMainThread")) {
+       return false;
+     }
+ 
+     testWorkerMarkerData(markers.find(m => m.name == "Worker"));
+     return true;
+   });
+ 
+-  yield stopRecording(panel);
++  await stopRecording(panel);
+   ok(true, "Recording has ended.");
+ 
+   for (let node of $$(".waterfall-marker-name[value=Worker")) {
+     testWorkerMarkerUI(node.parentNode.parentNode);
+   }
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ 
+ function testWorkerMarkerData(marker) {
+   ok(true, "Found a worker marker.");
+ 
+   ok("start" in marker,
+     "The start time is specified in the worker marker.");
+diff --git a/devtools/client/performance/test/helpers/panel-utils.js b/devtools/client/performance/test/helpers/panel-utils.js
+--- a/devtools/client/performance/test/helpers/panel-utils.js
++++ b/devtools/client/performance/test/helpers/panel-utils.js
+@@ -7,100 +7,100 @@
+ const { gDevTools } = require("devtools/client/framework/devtools");
+ const { TargetFactory } = require("devtools/client/framework/target");
+ const { addTab, removeTab } = require("devtools/client/performance/test/helpers/tab-utils");
+ const { once } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+ /**
+  * Initializes a toolbox panel in a new tab.
+  */
+-exports.initPanelInNewTab = function* ({ tool, url, win }, options = {}) {
+-  let tab = yield addTab({ url, win }, options);
+-  return (yield exports.initPanelInTab({ tool, tab }));
++exports.initPanelInNewTab = async function({ tool, url, win }, options = {}) {
++  let tab = await addTab({ url, win }, options);
++  return exports.initPanelInTab({ tool, tab });
+ };
+ 
+ /**
+  * Initializes a toolbox panel in the specified tab.
+  */
+-exports.initPanelInTab = function* ({ tool, tab }) {
++exports.initPanelInTab = async function({ tool, tab }) {
+   dump(`Initializing a ${tool} panel.\n`);
+ 
+   let target = TargetFactory.forTab(tab);
+-  yield target.makeRemote();
++  await target.makeRemote();
+ 
+   // Open a toolbox and wait for the connection to the performance actors
+   // to be opened. This is necessary because of the WebConsole's
+   // `profile` and `profileEnd` methods.
+-  let toolbox = yield gDevTools.showToolbox(target, tool);
+-  yield toolbox.initPerformance();
++  let toolbox = await gDevTools.showToolbox(target, tool);
++  await toolbox.initPerformance();
+ 
+   let panel = toolbox.getCurrentPanel();
+   return { target, toolbox, panel };
+ };
+ 
+ /**
+  * Initializes a performance panel in a new tab.
+  */
+-exports.initPerformanceInNewTab = function* ({ url, win }, options = {}) {
+-  let tab = yield addTab({ url, win }, options);
+-  return (yield exports.initPerformanceInTab({ tab }));
++exports.initPerformanceInNewTab = async function({ url, win }, options = {}) {
++  let tab = await addTab({ url, win }, options);
++  return exports.initPerformanceInTab({ tab });
+ };
+ 
+ /**
+  * Initializes a performance panel in the specified tab.
+  */
+-exports.initPerformanceInTab = function* ({ tab }) {
+-  return (yield exports.initPanelInTab({
++exports.initPerformanceInTab = async function({ tab }) {
++  return exports.initPanelInTab({
+     tool: "performance",
+     tab: tab
+-  }));
++  });
+ };
+ 
+ /**
+  * Initializes a webconsole panel in a new tab.
+  * Returns a console property that allows calls to `profile` and `profileEnd`.
+  */
+-exports.initConsoleInNewTab = function* ({ url, win }, options = {}) {
+-  let tab = yield addTab({ url, win }, options);
+-  return (yield exports.initConsoleInTab({ tab }));
++exports.initConsoleInNewTab = async function({ url, win }, options = {}) {
++  let tab = await addTab({ url, win }, options);
++  return exports.initConsoleInTab({ tab });
+ };
+ 
+ /**
+  * Initializes a webconsole panel in the specified tab.
+  * Returns a console property that allows calls to `profile` and `profileEnd`.
+  */
+-exports.initConsoleInTab = function* ({ tab }) {
+-  let { target, toolbox, panel } = yield exports.initPanelInTab({
++exports.initConsoleInTab = async function({ tab }) {
++  let { target, toolbox, panel } = await exports.initPanelInTab({
+     tool: "webconsole",
+     tab: tab
+   });
+ 
+-  let consoleMethod = function* (method, label, event) {
++  let consoleMethod = async function(method, label, event) {
+     let recordingEventReceived = once(toolbox.performance, event);
+     if (label === undefined) {
+-      yield panel.hud.jsterm.execute(`console.${method}()`);
++      await panel.hud.jsterm.execute(`console.${method}()`);
+     } else {
+-      yield panel.hud.jsterm.execute(`console.${method}("${label}")`);
++      await panel.hud.jsterm.execute(`console.${method}("${label}")`);
+     }
+-    yield recordingEventReceived;
++    await recordingEventReceived;
+   };
+ 
+-  let profile = function* (label) {
+-    return yield consoleMethod("profile", label, "recording-started");
++  let profile = async function(label) {
++    return consoleMethod("profile", label, "recording-started");
+   };
+ 
+-  let profileEnd = function* (label) {
+-    return yield consoleMethod("profileEnd", label, "recording-stopped");
++  let profileEnd = async function(label) {
++    return consoleMethod("profileEnd", label, "recording-stopped");
+   };
+ 
+   return { target, toolbox, panel, console: { profile, profileEnd } };
+ };
+ 
+ /**
+  * Tears down a toolbox panel and removes an associated tab.
+  */
+-exports.teardownToolboxAndRemoveTab = function* (panel) {
++exports.teardownToolboxAndRemoveTab = async function(panel) {
+   dump("Destroying panel.\n");
+ 
+   let tab = panel.target.tab;
+-  yield panel.toolbox.destroy();
+-  yield removeTab(tab);
++  await panel.toolbox.destroy();
++  await removeTab(tab);
+ };
+diff --git a/devtools/client/performance/test/helpers/profiler-mm-utils.js b/devtools/client/performance/test/helpers/profiler-mm-utils.js
+--- a/devtools/client/performance/test/helpers/profiler-mm-utils.js
++++ b/devtools/client/performance/test/helpers/profiler-mm-utils.js
+@@ -5,17 +5,16 @@
+ 
+ /**
+  * The following functions are used in testing to control and inspect
+  * the nsIProfiler in child process content. These should be called from
+  * the parent process.
+  */
+ 
+ const { Cc, Ci } = require("chrome");
+-const { Task } = require("devtools/shared/task");
+ 
+ const FRAME_SCRIPT_UTILS_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/frame-script-utils.js";
+ 
+ let gMM = null;
+ 
+ /**
+  * Loads the relevant frame scripts into the provided browser's message manager.
+  */
+@@ -61,34 +60,34 @@ exports.pmmUniqueMessage = function(mess
+  */
+ exports.pmmIsProfilerActive = () => {
+   return exports.pmmSendProfilerCommand("IsActive");
+ };
+ 
+ /**
+  * Starts the nsProfiler module.
+  */
+-exports.pmmStartProfiler = Task.async(function* ({ entries, interval, features }) {
+-  let isActive = (yield exports.pmmSendProfilerCommand("IsActive")).isActive;
++exports.pmmStartProfiler = async function({ entries, interval, features }) {
++  let isActive = (await exports.pmmSendProfilerCommand("IsActive")).isActive;
+   if (!isActive) {
+     return exports.pmmSendProfilerCommand("StartProfiler", [entries, interval, features,
+                                                             features.length]);
+   }
+   return null;
+-});
++};
+ /**
+  * Stops the nsProfiler module.
+  */
+-exports.pmmStopProfiler = Task.async(function* () {
+-  let isActive = (yield exports.pmmSendProfilerCommand("IsActive")).isActive;
++exports.pmmStopProfiler = async function() {
++  let isActive = (await exports.pmmSendProfilerCommand("IsActive")).isActive;
+   if (isActive) {
+     return exports.pmmSendProfilerCommand("StopProfiler");
+   }
+   return null;
+-});
++};
+ 
+ /**
+  * Calls a method on the nsProfiler module.
+  */
+ exports.pmmSendProfilerCommand = (method, args = []) => {
+   return exports.pmmUniqueMessage("devtools:test:profiler", { method, args });
+ };
+ 
+diff --git a/devtools/client/performance/test/helpers/tab-utils.js b/devtools/client/performance/test/helpers/tab-utils.js
+--- a/devtools/client/performance/test/helpers/tab-utils.js
++++ b/devtools/client/performance/test/helpers/tab-utils.js
+@@ -39,14 +39,14 @@ exports.removeTab = function(tab) {
+   dump(`Removing tab: ${tab.linkedBrowser.currentURI.spec}.\n`);
+ 
+   BrowserTestUtils.removeTab(tab);
+ };
+ 
+ /**
+  * Adds a browser window with the provided options.
+  */
+-exports.addWindow = function* (options) {
++exports.addWindow = async function(options) {
+   let { OpenBrowserWindow } = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
+   let win = OpenBrowserWindow(options);
+-  yield waitForDelayedStartupFinished(win);
++  await waitForDelayedStartupFinished(win);
+   return win;
+ };
+diff --git a/devtools/client/performance/test/helpers/wait-utils.js b/devtools/client/performance/test/helpers/wait-utils.js
+--- a/devtools/client/performance/test/helpers/wait-utils.js
++++ b/devtools/client/performance/test/helpers/wait-utils.js
+@@ -1,16 +1,15 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ /* globals dump */
+ 
+ const { CC } = require("chrome");
+-const { Task } = require("devtools/shared/task");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ const { once, observeOnce } = require("devtools/client/performance/test/helpers/event-utils");
+ 
+ /**
+  * Blocks the main thread for the specified amount of time.
+  */
+ exports.busyWait = function(time) {
+   dump(`Busy waiting for: ${time} milliseconds.\n`);
+@@ -29,23 +28,23 @@ exports.busyWait = function(time) {
+ exports.idleWait = function(time) {
+   dump(`Idly waiting for: ${time} milliseconds.\n`);
+   return DevToolsUtils.waitForTime(time);
+ };
+ 
+ /**
+  * Waits until a predicate returns true.
+  */
+-exports.waitUntil = function* (predicate, interval = 100, tries = 100) {
++exports.waitUntil = async function(predicate, interval = 100, tries = 100) {
+   for (let i = 1; i <= tries; i++) {
+-    if (yield Task.spawn(predicate)) {
++    if (await predicate()) {
+       dump(`Predicate returned true after ${i} tries.\n`);
+       return;
+     }
+-    yield exports.idleWait(interval);
++    await exports.idleWait(interval);
+   }
+   throw new Error(`Predicate returned false after ${tries} tries, aborting.\n`);
+ };
+ 
+ /**
+  * Waits for a `MozAfterPaint` event to be fired on the specified window.
+  */
+ exports.waitForMozAfterPaint = function(window) {
+diff --git a/devtools/client/performance/views/details-abstract-subview.js b/devtools/client/performance/views/details-abstract-subview.js
+--- a/devtools/client/performance/views/details-abstract-subview.js
++++ b/devtools/client/performance/views/details-abstract-subview.js
+@@ -28,21 +28,21 @@ var DetailsSubview = {
+     DetailsView.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
+ 
+     let self = this;
+     let originalRenderFn = this.render;
+     let afterRenderFn = () => {
+       this._wasRendered = true;
+     };
+ 
+-    this.render = Task.async(function* (...args) {
+-      let maybeRetval = yield originalRenderFn.apply(self, args);
++    this.render = async function(...args) {
++      let maybeRetval = await originalRenderFn.apply(self, args);
+       afterRenderFn();
+       return maybeRetval;
+-    });
++    };
+   },
+ 
+   /**
+    * Unbinds events.
+    */
+   destroy: function() {
+     clearNamedTimeout("range-change-debounce");
+ 
+diff --git a/devtools/client/performance/views/details-js-flamegraph.js b/devtools/client/performance/views/details-js-flamegraph.js
+--- a/devtools/client/performance/views/details-js-flamegraph.js
++++ b/devtools/client/performance/views/details-js-flamegraph.js
+@@ -19,42 +19,42 @@ var JsFlameGraphView = extend(DetailsSub
+     "flatten-tree-recursion",
+     "show-platform-data",
+     "show-idle-blocks"
+   ],
+ 
+   /**
+    * Sets up the view with event binding.
+    */
+-  initialize: Task.async(function* () {
++  async initialize() {
+     DetailsSubview.initialize.call(this);
+ 
+     this.graph = new FlameGraph($("#js-flamegraph-view"));
+     this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
+     this.graph.setTheme(PerformanceController.getTheme());
+-    yield this.graph.ready();
++    await this.graph.ready();
+ 
+     this._onRangeChangeInGraph = this._onRangeChangeInGraph.bind(this);
+     this._onThemeChanged = this._onThemeChanged.bind(this);
+ 
+     PerformanceController.on(EVENTS.THEME_CHANGED, this._onThemeChanged);
+     this.graph.on("selecting", this._onRangeChangeInGraph);
+-  }),
++  },
+ 
+   /**
+    * Unbinds events.
+    */
+-  destroy: Task.async(function* () {
++  async destroy() {
+     DetailsSubview.destroy.call(this);
+ 
+     PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
+     this.graph.off("selecting", this._onRangeChangeInGraph);
+ 
+-    yield this.graph.destroy();
+-  }),
++    await this.graph.destroy();
++  },
+ 
+   /**
+    * Method for handling all the set up for rendering a new flamegraph.
+    *
+    * @param object interval [optional]
+    *        The { startTime, endTime }, in milliseconds.
+    */
+   render: function(interval = {}) {
+diff --git a/devtools/client/performance/views/details-memory-flamegraph.js b/devtools/client/performance/views/details-memory-flamegraph.js
+--- a/devtools/client/performance/views/details-memory-flamegraph.js
++++ b/devtools/client/performance/views/details-memory-flamegraph.js
+@@ -18,42 +18,42 @@ var MemoryFlameGraphView = extend(Detail
+     "invert-flame-graph",
+     "flatten-tree-recursion",
+     "show-idle-blocks"
+   ],
+ 
+   /**
+    * Sets up the view with event binding.
+    */
+-  initialize: Task.async(function* () {
++  async initialize() {
+     DetailsSubview.initialize.call(this);
+ 
+     this.graph = new FlameGraph($("#memory-flamegraph-view"));
+     this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
+     this.graph.setTheme(PerformanceController.getTheme());
+-    yield this.graph.ready();
++    await this.graph.ready();
+ 
+     this._onRangeChangeInGraph = this._onRangeChangeInGraph.bind(this);
+     this._onThemeChanged = this._onThemeChanged.bind(this);
+ 
+     PerformanceController.on(EVENTS.THEME_CHANGED, this._onThemeChanged);
+     this.graph.on("selecting", this._onRangeChangeInGraph);
+-  }),
++  },
+ 
+   /**
+    * Unbinds events.
+    */
+-  destroy: Task.async(function* () {
++  async destroy() {
+     DetailsSubview.destroy.call(this);
+ 
+     PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
+     this.graph.off("selecting", this._onRangeChangeInGraph);
+ 
+-    yield this.graph.destroy();
+-  }),
++    await this.graph.destroy();
++  },
+ 
+   /**
+    * Method for handling all the set up for rendering a new flamegraph.
+    *
+    * @param object interval [optional]
+    *        The { startTime, endTime }, in milliseconds.
+    */
+   render: function(interval = {}) {
+diff --git a/devtools/client/performance/views/details.js b/devtools/client/performance/views/details.js
+--- a/devtools/client/performance/views/details.js
++++ b/devtools/client/performance/views/details.js
+@@ -41,63 +41,63 @@ var DetailsView = {
+       features: ["withAllocations"],
+       prefs: ["enable-memory-flame"],
+     },
+   },
+ 
+   /**
+    * Sets up the view with event binding, initializes subviews.
+    */
+-  initialize: Task.async(function* () {
++  async initialize() {
+     this.el = $("#details-pane");
+     this.toolbar = $("#performance-toolbar-controls-detail-views");
+ 
+     this._onViewToggle = this._onViewToggle.bind(this);
+     this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
+     this.setAvailableViews = this.setAvailableViews.bind(this);
+ 
+     for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
+       button.addEventListener("command", this._onViewToggle);
+     }
+ 
+-    yield this.setAvailableViews();
++    await this.setAvailableViews();
+ 
+     PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE,
+                              this._onRecordingStoppedOrSelected);
+     PerformanceController.on(EVENTS.RECORDING_SELECTED,
+                              this._onRecordingStoppedOrSelected);
+     PerformanceController.on(EVENTS.PREF_CHANGED, this.setAvailableViews);
+-  }),
++  },
+ 
+   /**
+    * Unbinds events, destroys subviews.
+    */
+-  destroy: Task.async(function* () {
++  async destroy() {
+     for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
+       button.removeEventListener("command", this._onViewToggle);
+     }
+ 
+     for (let component of Object.values(this.components)) {
+-      component.initialized && (yield component.view.destroy());
++      component.initialized && (await component.view.destroy());
+     }
+ 
+     PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
+                               this._onRecordingStoppedOrSelected);
+     PerformanceController.off(EVENTS.RECORDING_SELECTED,
+                               this._onRecordingStoppedOrSelected);
+     PerformanceController.off(EVENTS.PREF_CHANGED, this.setAvailableViews);
+-  }),
++  },
+ 
+   /**
+    * Sets the possible views based off of recording features and server actor support
+    * by hiding/showing the buttons that select them and going to default view
+    * if currently selected. Called when a preference changes in
+    * `devtools.performance.ui.`.
+    */
+-  setAvailableViews: Task.async(function* () {
++  async setAvailableViews() {
+     let recording = PerformanceController.getCurrentRecording();
+     let isCompleted = recording && recording.isCompleted();
+     let invalidCurrentView = false;
+ 
+     for (let [name, { view }] of Object.entries(this.components)) {
+       let isSupported = this._isViewSupported(name);
+ 
+       $(`toolbarbutton[data-view=${name}]`).hidden = !isSupported;
+@@ -114,19 +114,19 @@ var DetailsView = {
+     // 1: If we currently have selected a view that is no longer valid due
+     // to feature support, and this isn't the first view, and the current recording
+     // is completed.
+     //
+     // 2. If we have a finished recording and no panel was selected yet,
+     // use a default now that we have the recording configurations
+     if ((this._initialized && isCompleted && invalidCurrentView) ||
+         (!this._initialized && isCompleted && recording)) {
+-      yield this.selectDefaultView();
++      await this.selectDefaultView();
+     }
+-  }),
++  },
+ 
+   /**
+    * Takes a view name and determines if the current recording
+    * can support the view.
+    *
+    * @param {string} viewName
+    * @return {boolean}
+    */
+@@ -146,36 +146,36 @@ var DetailsView = {
+ 
+   /**
+    * Select one of the DetailView's subviews to be rendered,
+    * hiding the others.
+    *
+    * @param String viewName
+    *        Name of the view to be shown.
+    */
+-  selectView: Task.async(function* (viewName) {
++  async selectView(viewName) {
+     let component = this.components[viewName];
+     this.el.selectedPanel = $("#" + component.id);
+ 
+-    yield this._whenViewInitialized(component);
++    await this._whenViewInitialized(component);
+ 
+     for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
+       if (button.getAttribute("data-view") === viewName) {
+         button.setAttribute("checked", true);
+       } else {
+         button.removeAttribute("checked");
+       }
+     }
+ 
+     // Set a flag indicating that a view was explicitly set based on a
+     // recording's features.
+     this._initialized = true;
+ 
+     this.emit(EVENTS.UI_DETAILS_VIEW_SELECTED, viewName);
+-  }),
++  },
+ 
+   /**
+    * Selects a default view based off of protocol support
+    * and preferences enabled.
+    */
+   selectDefaultView: function() {
+     // We want the waterfall to be default view in almost all cases, except when
+     // timeline actor isn't supported, or we have markers disabled (which should only
+@@ -215,32 +215,32 @@ var DetailsView = {
+ 
+   /**
+    * Initializes a subview if it wasn't already set up, and makes sure
+    * it's populated with recording data if there is some available.
+    *
+    * @param object component
+    *        A component descriptor from DetailsView.components
+    */
+-  _whenViewInitialized: Task.async(function* (component) {
++  async _whenViewInitialized(component) {
+     if (component.initialized) {
+       return;
+     }
+     component.initialized = true;
+-    yield component.view.initialize();
++    await component.view.initialize();
+ 
+     // If this view is initialized *after* a recording is shown, it won't display
+     // any data. Make sure it's populated by setting `shouldUpdateWhenShown`.
+     // All detail views require a recording to be complete, so do not
+     // attempt to render if recording is in progress or does not exist.
+     let recording = PerformanceController.getCurrentRecording();
+     if (recording && recording.isCompleted()) {
+       component.view.shouldUpdateWhenShown = true;
+     }
+-  }),
++  },
+ 
+   /**
+    * Called when recording stops or is selected.
+    */
+   _onRecordingStoppedOrSelected: function(_, state, recording) {
+     if (typeof state === "string" && state !== "recording-stopped") {
+       return;
+     }
+diff --git a/devtools/client/performance/views/overview.js b/devtools/client/performance/views/overview.js
+--- a/devtools/client/performance/views/overview.js
++++ b/devtools/client/performance/views/overview.js
+@@ -72,26 +72,26 @@ var OverviewView = {
+     PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
+     this.graphs.on("selecting", this._onGraphSelecting);
+     this.graphs.on("rendered", this._onGraphRendered);
+   },
+ 
+   /**
+    * Unbinds events.
+    */
+-  destroy: Task.async(function* () {
++  async destroy() {
+     PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
+     PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
+     PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
+                               this._onRecordingStateChange);
+     PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
+     this.graphs.off("selecting", this._onGraphSelecting);
+     this.graphs.off("rendered", this._onGraphRendered);
+-    yield this.graphs.destroy();
+-  }),
++    await this.graphs.destroy();
++  },
+ 
+   /**
+    * Returns true if any of the overview graphs have mouse dragging active,
+    * false otherwise.
+    */
+   get isMouseActive() {
+     // Fetch all graphs currently stored in the GraphsController.
+     // These graphs are not necessarily active, but will not have
+@@ -168,82 +168,82 @@ var OverviewView = {
+   },
+ 
+   /**
+    * Method for handling all the set up for rendering the overview graphs.
+    *
+    * @param number resolution
+    *        The fps graph resolution. @see Graphs.js
+    */
+-  render: Task.async(function* (resolution) {
++  async render(resolution) {
+     if (this.isDisabled()) {
+       return;
+     }
+ 
+     let recording = PerformanceController.getCurrentRecording();
+-    yield this.graphs.render(recording.getAllData(), resolution);
++    await this.graphs.render(recording.getAllData(), resolution);
+ 
+     // Finished rendering all graphs in this overview.
+     this.emit(EVENTS.UI_OVERVIEW_RENDERED, resolution);
+-  }),
++  },
+ 
+   /**
+    * Called at most every OVERVIEW_UPDATE_INTERVAL milliseconds
+    * and uses data fetched from the controller to render
+    * data into all the corresponding overview graphs.
+    */
+-  _onRecordingTick: Task.async(function* () {
+-    yield this.render(FRAMERATE_GRAPH_LOW_RES_INTERVAL);
++  async _onRecordingTick() {
++    await this.render(FRAMERATE_GRAPH_LOW_RES_INTERVAL);
+     this._prepareNextTick();
+-  }),
++  },
+ 
+   /**
+    * Called to refresh the timer to keep firing _onRecordingTick.
+    */
+   _prepareNextTick: function() {
+     // Check here to see if there's still a _timeoutId, incase
+     // `stop` was called before the _prepareNextTick call was executed.
+     if (this.isRendering()) {
+       this._timeoutId = setTimeout(this._onRecordingTick, this.OVERVIEW_UPDATE_INTERVAL);
+     }
+   },
+ 
+   /**
+    * Called when recording state changes.
+    */
+-  _onRecordingStateChange: OverviewViewOnStateChange(Task.async(
+-    function* (_, state, recording) {
++  _onRecordingStateChange:
++    OverviewViewOnStateChange(async function(_, state, recording) {
+       if (state !== "recording-stopped") {
+         return;
+       }
+       // Check to see if the recording that just stopped is the current recording.
+       // If it is, render the high-res graphs. For manual recordings, it will also
+       // be the current recording, but profiles generated by `console.profile` can stop
+       // while having another profile selected -- in this case, OverviewView should keep
+       // rendering the current recording.
+       if (recording !== PerformanceController.getCurrentRecording()) {
+         return;
+       }
+       this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
+-      yield this._checkSelection(recording);
+-    })),
++      await this._checkSelection(recording);
++    }),
+ 
+   /**
+    * Called when a new recording is selected.
+    */
+-  _onRecordingSelected: OverviewViewOnStateChange(Task.async(function* (_, recording) {
++  _onRecordingSelected: OverviewViewOnStateChange(async function(_, recording) {
+     this._setGraphVisibilityFromRecordingFeatures(recording);
+ 
+     // If this recording is complete, render the high res graph
+     if (recording.isCompleted()) {
+-      yield this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
++      await this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
+     }
+-    yield this._checkSelection(recording);
++    await this._checkSelection(recording);
+     this.graphs.dropSelection();
+-  })),
++  }),
+ 
+   /**
+    * Start the polling for rendering the overview graph.
+    */
+   _startPolling: function() {
+     this._timeoutId = setTimeout(this._onRecordingTick, this.OVERVIEW_UPDATE_INTERVAL);
+   },
+ 
+@@ -261,20 +261,20 @@ var OverviewView = {
+   isRendering: function() {
+     return !!this._timeoutId;
+   },
+ 
+   /**
+    * Makes sure the selection is enabled or disabled in all the graphs,
+    * based on whether a recording currently exists and is not in progress.
+    */
+-  _checkSelection: Task.async(function* (recording) {
++  async _checkSelection(recording) {
+     let isEnabled = recording ? recording.isCompleted() : false;
+-    yield this.graphs.selectionEnabled(isEnabled);
+-  }),
++    await this.graphs.selectionEnabled(isEnabled);
++  },
+ 
+   /**
+    * Fired when the graph selection has changed. Called by
+    * mouseup and scroll events.
+    */
+   _onGraphSelecting: function() {
+     if (this._stopSelectionChangeEventPropagation) {
+       return;
+@@ -298,29 +298,29 @@ var OverviewView = {
+   },
+ 
+   /**
+    * Called whenever a preference in `devtools.performance.ui.` changes.
+    * Does not care about the enabling of memory/framerate graphs,
+    * because those will set values on a recording model, and
+    * the graphs will render based on the existence.
+    */
+-  _onPrefChanged: Task.async(function* (_, prefName, prefValue) {
++  async _onPrefChanged(_, prefName, prefValue) {
+     switch (prefName) {
+       case "hidden-markers": {
+-        let graph = yield this.graphs.isAvailable("timeline");
++        let graph = await this.graphs.isAvailable("timeline");
+         if (graph) {
+           let filter = PerformanceController.getPref("hidden-markers");
+           graph.setFilter(filter);
+           graph.refresh({ force: true });
+         }
+         break;
+       }
+     }
+-  }),
++  },
+ 
+   _setGraphVisibilityFromRecordingFeatures: function(recording) {
+     for (let [graphName, requirements] of Object.entries(GRAPH_REQUIREMENTS)) {
+       this.graphs.enable(graphName,
+                          PerformanceController.isFeatureSupported(requirements.features));
+     }
+   },
+ 
+diff --git a/devtools/client/performance/views/recordings.js b/devtools/client/performance/views/recordings.js
+--- a/devtools/client/performance/views/recordings.js
++++ b/devtools/client/performance/views/recordings.js
+@@ -156,21 +156,21 @@ var RecordingsView = {
+     }
+     recordings.splice(index, 1);
+     this._renderList();
+   },
+ 
+   /**
+    * The select listener for this container.
+    */
+-  _onSelect: Task.async(function* (recording) {
++  async _onSelect(recording) {
+     this._listState.selected = recording;
+     this.emit(EVENTS.UI_RECORDING_SELECTED, recording);
+     this._renderList();
+-  }),
++  },
+ 
+   /**
+    * The click listener for the "save" button of each item in this container.
+    */
+   _onSaveButtonClick: function(recording) {
+     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+     fp.init(window, L10N.getStr("recordingsList.saveDialogTitle"),
+             Ci.nsIFilePicker.modeSave);
+diff --git a/devtools/client/performance/views/toolbar.js b/devtools/client/performance/views/toolbar.js
+--- a/devtools/client/performance/views/toolbar.js
++++ b/devtools/client/performance/views/toolbar.js
+@@ -8,43 +8,43 @@
+ 
+ /**
+  * View handler for toolbar events (mostly option toggling and triggering)
+  */
+ var ToolbarView = {
+   /**
+    * Sets up the view with event binding.
+    */
+-  initialize: Task.async(function* () {
++  async initialize() {
+     this._onFilterPopupShowing = this._onFilterPopupShowing.bind(this);
+     this._onFilterPopupHiding = this._onFilterPopupHiding.bind(this);
+     this._onHiddenMarkersChanged = this._onHiddenMarkersChanged.bind(this);
+     this._onPrefChanged = this._onPrefChanged.bind(this);
+     this._popup = $("#performance-options-menupopup");
+ 
+     this.optionsView = new OptionsView({
+       branchName: BRANCH_NAME,
+       menupopup: this._popup
+     });
+ 
+     // Set the visibility of experimental UI options on load
+     // based off of `devtools.performance.ui.experimental` preference
+     let experimentalEnabled = PerformanceController.getOption("experimental");
+     this._toggleExperimentalUI(experimentalEnabled);
+ 
+-    yield this.optionsView.initialize();
++    await this.optionsView.initialize();
+     this.optionsView.on("pref-changed", this._onPrefChanged);
+ 
+     this._buildMarkersFilterPopup();
+     this._updateHiddenMarkersPopup();
+     $("#performance-filter-menupopup").addEventListener("popupshowing",
+                                                         this._onFilterPopupShowing);
+     $("#performance-filter-menupopup").addEventListener("popuphiding",
+                                                         this._onFilterPopupHiding);
+-  }),
++  },
+ 
+   /**
+    * Unbinds events and cleans up view.
+    */
+   destroy: function() {
+     $("#performance-filter-menupopup").removeEventListener("popupshowing",
+                                                            this._onFilterPopupShowing);
+     $("#performance-filter-menupopup").removeEventListener("popuphiding",

+ 535 - 0
frg/work-js/mozilla-release/patches/1440321-1k-scratchpad-61a1.patch

@@ -0,0 +1,535 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  5a00250c426bb59ff4e16ee6eb281cc1eb6fd29d
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1k. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/scratchpad/test/browser_scratchpad_autocomplete.js b/devtools/client/scratchpad/test/browser_scratchpad_autocomplete.js
+--- a/devtools/client/scratchpad/test/browser_scratchpad_autocomplete.js
++++ b/devtools/client/scratchpad/test/browser_scratchpad_autocomplete.js
+@@ -1,60 +1,59 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ /* Bug 968896 */
+ 
+ // Test the completions using numbers.
+ const source = "0x1.";
+ const completions = ["toExponential", "toFixed", "toString"];
+-const { Task } = require("devtools/shared/task");
+ 
+ function test() {
+   const options = { tabContent: "test scratchpad autocomplete" };
+   openTabAndScratchpad(options)
+     .then(Task.async(runTests))
+     .then(finish, console.error);
+ }
+ 
+ 
+-function* runTests([win, sp]) {
++async function runTests([win, sp]) {
+   const {editor} = sp;
+   const editorWin = editor.container.contentWindow;
+ 
+   // Show the completions popup.
+   sp.setText(source);
+   sp.editor.setCursor({ line: 0, ch: source.length });
+-  yield keyOnce("suggestion-entered", " ", { ctrlKey: true });
++  await keyOnce("suggestion-entered", " ", { ctrlKey: true });
+ 
+   // Get the hints popup container.
+   const hints = editorWin.document.querySelector(".CodeMirror-hints");
+ 
+   ok(hints,
+      "The hint container should exist.");
+   is(hints.childNodes.length, 3,
+      "The hint container should have the completions.");
+ 
+   let i = 0;
+   for (let completion of completions) {
+     let active = hints.querySelector(".CodeMirror-hint-active");
+     is(active.textContent, completion,
+        "Check that completion " + i++ + " is what is expected.");
+-    yield keyOnce("suggestion-entered", "VK_DOWN");
++    await keyOnce("suggestion-entered", "VK_DOWN");
+   }
+ 
+   // We should have looped around to the first suggestion again. Accept it.
+-  yield keyOnce("after-suggest", "VK_RETURN");
++  await keyOnce("after-suggest", "VK_RETURN");
+ 
+   is(sp.getText(), source + completions[0],
+      "Autocompletion should work and select the right element.");
+ 
+   // Check that the information tooltips work.
+   sp.setText("5");
+-  yield keyOnce("show-information", " ", { ctrlKey: true, shiftKey: true });
++  await keyOnce("show-information", " ", { ctrlKey: true, shiftKey: true });
+ 
+   // Get the information container.
+   const info = editorWin.document.querySelector(".CodeMirror-Tern-information");
+   ok(info,
+      "Info tooltip should appear.");
+   is(info.textContent.slice(0, 6), "number",
+      "Info tooltip should have expected contents.");
+ 
+diff --git a/devtools/client/scratchpad/test/browser_scratchpad_close_toolbox.js b/devtools/client/scratchpad/test/browser_scratchpad_close_toolbox.js
+--- a/devtools/client/scratchpad/test/browser_scratchpad_close_toolbox.js
++++ b/devtools/client/scratchpad/test/browser_scratchpad_close_toolbox.js
+@@ -1,38 +1,37 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ // Test that closing the toolbox after having opened a scratchpad leaves the
+ // latter in a functioning state.
+ 
+-var {Task} = require("devtools/shared/task");
+ var {TargetFactory} = require("devtools/client/framework/target");
+ 
+ function test() {
+   const options = {
+     tabContent: "test closing toolbox and then reusing scratchpad"
+   };
+   openTabAndScratchpad(options)
+     .then(Task.async(runTests))
+     .then(finish, console.error);
+ }
+ 
+-function* runTests([win, sp]) {
++async function runTests([win, sp]) {
+   // Use the scratchpad before opening the toolbox.
+   const source = "window.foobar = 7;";
+   sp.setText(source);
+-  let [,, result] = yield sp.display();
++  let [,, result] = await sp.display();
+   is(result, 7, "Display produced the expected output.");
+ 
+   // Now open the toolbox and close it again.
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  let toolbox = yield gDevTools.showToolbox(target, "webconsole");
++  let toolbox = await gDevTools.showToolbox(target, "webconsole");
+   ok(toolbox, "Toolbox was opened.");
+-  let closed = yield gDevTools.closeToolbox(target);
++  let closed = await gDevTools.closeToolbox(target);
+   is(closed, true, "Toolbox was closed.");
+ 
+   // Now see if using the scratcphad works as expected.
+   sp.setText(source);
+-  let [,, result2] = yield sp.display();
++  let [,, result2] = await sp.display();
+   is(result2, 7,
+      "Display produced the expected output after the toolbox was gone.");
+ }
+diff --git a/devtools/client/scratchpad/test/browser_scratchpad_contexts.js b/devtools/client/scratchpad/test/browser_scratchpad_contexts.js
+--- a/devtools/client/scratchpad/test/browser_scratchpad_contexts.js
++++ b/devtools/client/scratchpad/test/browser_scratchpad_contexts.js
+@@ -21,45 +21,45 @@ function runTests() {
+   let notificationBox = sp.notificationBox;
+ 
+   ok(contentMenu, "found #sp-menu-content");
+   ok(chromeMenu, "found #sp-menu-browser");
+   ok(notificationBox, "found Scratchpad.notificationBox");
+ 
+   let tests = [{
+     method: "run",
+-    prepare: function* () {
++    prepare: async function () {
+       sp.setContentContext();
+ 
+       is(sp.executionContext, gScratchpadWindow.SCRATCHPAD_CONTEXT_CONTENT,
+          "executionContext is content");
+ 
+       is(contentMenu.getAttribute("checked"), "true",
+          "content menuitem is checked");
+ 
+       isnot(chromeMenu.getAttribute("checked"), "true",
+          "chrome menuitem is not checked");
+ 
+       ok(!notificationBox.currentNotification,
+          "there is no notification in content context");
+ 
+       sp.editor.setText("window.foobarBug636725 = 'aloha';");
+ 
+-      let pageResult = yield inContent(function* () {
++      let pageResult = await inContent(function* () {
+         return content.wrappedJSObject.foobarBug636725;
+       });
+       ok(!pageResult, "no content.foobarBug636725");
+     },
+-    then: function* () {
++    then: function() {
+       is(content.wrappedJSObject.foobarBug636725, "aloha",
+          "content.foobarBug636725 has been set");
+     }
+   }, {
+     method: "run",
+-    prepare: function* () {
++    prepare: function() {
+       sp.setBrowserContext();
+ 
+       is(sp.executionContext, gScratchpadWindow.SCRATCHPAD_CONTEXT_BROWSER,
+          "executionContext is chrome");
+ 
+       is(chromeMenu.getAttribute("checked"), "true",
+          "chrome menuitem is checked");
+ 
+@@ -70,75 +70,75 @@ function runTests() {
+          "there is a notification in browser context");
+ 
+       let [ from, to ] = sp.editor.getPosition(31, 32);
+       sp.editor.replaceText("2'", from, to);
+ 
+       is(sp.getText(), "window.foobarBug636725 = 'aloha2';",
+          "setText() worked");
+     },
+-    then: function* () {
++    then: function() {
+       is(window.foobarBug636725, "aloha2",
+          "window.foobarBug636725 has been set");
+ 
+       delete window.foobarBug636725;
+       ok(!window.foobarBug636725, "no window.foobarBug636725");
+     }
+   }, {
+     method: "run",
+-    prepare: function* () {
++    prepare: function() {
+       sp.editor.replaceText("gBrowser", sp.editor.getPosition(7));
+ 
+       is(sp.getText(), "window.gBrowser",
+          "setText() worked with no end for the replace range");
+     },
+-    then: function* ([, , result]) {
++    then: function([, , result]) {
+       is(result.class, "XULElement",
+          "chrome context has access to chrome objects");
+     }
+   }, {
+     method: "run",
+-    prepare: function* () {
++    prepare: function() {
+       // Check that the sandbox is cached.
+       sp.editor.setText("typeof foobarBug636725cache;");
+     },
+-    then: function* ([, , result]) {
++    then: function([, , result]) {
+       is(result, "undefined", "global variable does not exist");
+     }
+   }, {
+     method: "run",
+-    prepare: function* () {
++    prepare: function() {
+       sp.editor.setText("window.foobarBug636725cache = 'foo';" +
+                  "typeof foobarBug636725cache;");
+     },
+-    then: function* ([, , result]) {
++    then: function([, , result]) {
+       is(result, "string",
+          "global variable exists across two different executions");
+     }
+   }, {
+     method: "run",
+     prepare: function* () {
+       sp.editor.setText("window.foobarBug636725cache2 = 'foo';" +
+                  "typeof foobarBug636725cache2;");
+     },
+-    then: function* ([, , result]) {
++    then: function([, , result]) {
+       is(result, "string",
+          "global variable exists across two different executions");
+     }
+   }, {
+     method: "run",
+-    prepare: function* () {
++    prepare: function() {
+       sp.setContentContext();
+ 
+       is(sp.executionContext, gScratchpadWindow.SCRATCHPAD_CONTEXT_CONTENT,
+          "executionContext is content");
+ 
+       sp.editor.setText("typeof foobarBug636725cache2;");
+     },
+-    then: function* ([, , result]) {
++    then: function([, , result]) {
+       is(result, "undefined",
+          "global variable no longer exists after changing the context");
+     }
+   }];
+ 
+   runAsyncCallbackTests(sp, tests).then(() => {
+     sp.setBrowserContext();
+     sp.editor.setText("delete foobarBug636725cache;" +
+diff --git a/devtools/client/scratchpad/test/browser_scratchpad_disable_view_menu_items.js b/devtools/client/scratchpad/test/browser_scratchpad_disable_view_menu_items.js
+--- a/devtools/client/scratchpad/test/browser_scratchpad_disable_view_menu_items.js
++++ b/devtools/client/scratchpad/test/browser_scratchpad_disable_view_menu_items.js
+@@ -1,36 +1,35 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ // Test if the view menu items "Larger Font" and "Smaller Font" are disabled
+ // when the font size reaches the maximum/minimum values.
+ 
+-var {Task} = require("devtools/shared/task");
+ 
+ function test() {
+   const options = {
+     tabContent: 'test if view menu items "Larger Font" and "Smaller Font" are enabled/disabled.'
+   };
+   openTabAndScratchpad(options)
+     .then(Task.async(runTests))
+     .then(finish, console.error);
+ }
+ 
+-function* runTests([win, sp]) {
+-  yield testMaximumFontSize(win, sp);
++async function runTests([win, sp]) {
++  await testMaximumFontSize(win, sp);
+ 
+-  yield testMinimumFontSize(win, sp);
++  await testMinimumFontSize(win, sp);
+ }
+ 
+ const MAXIMUM_FONT_SIZE = 96;
+ const MINIMUM_FONT_SIZE = 6;
+ const NORMAL_FONT_SIZE = 12;
+ 
+-var testMaximumFontSize = Task.async(function* (win, sp) {
++var testMaximumFontSize = async function (win, sp) {
+   let doc = win.document;
+ 
+   Services.prefs.clearUserPref("devtools.scratchpad.editorFontSize");
+ 
+   let menu = doc.getElementById("sp-menu-larger-font");
+ 
+   for (let i = NORMAL_FONT_SIZE; i <= MAXIMUM_FONT_SIZE; i++) {
+     menu.doCommand();
+@@ -38,19 +37,19 @@ var testMaximumFontSize = Task.async(fun
+ 
+   let cmd = doc.getElementById("sp-cmd-larger-font");
+   ok(cmd.getAttribute("disabled") === "true", 'Command "sp-cmd-larger-font" is disabled.');
+ 
+   menu = doc.getElementById("sp-menu-smaller-font");
+   menu.doCommand();
+ 
+   ok(cmd.hasAttribute("disabled") === false, 'Command "sp-cmd-larger-font" is enabled.');
+-});
++};
+ 
+-var testMinimumFontSize = Task.async(function* (win, sp) {
++var testMinimumFontSize = async function (win, sp) {
+   let doc = win.document;
+ 
+   let menu = doc.getElementById("sp-menu-smaller-font");
+ 
+   for (let i = MAXIMUM_FONT_SIZE; i >= MINIMUM_FONT_SIZE; i--) {
+     menu.doCommand();
+   }
+ 
+@@ -58,9 +57,9 @@ var testMinimumFontSize = Task.async(fun
+   ok(cmd.getAttribute("disabled") === "true", 'Command "sp-cmd-smaller-font" is disabled.');
+ 
+   menu = doc.getElementById("sp-menu-larger-font");
+   menu.doCommand();
+ 
+   ok(cmd.hasAttribute("disabled") === false, 'Command "sp-cmd-smaller-font" is enabled.');
+ 
+   Services.prefs.clearUserPref("devtools.scratchpad.editorFontSize");
+-});
++};
+diff --git a/devtools/client/scratchpad/test/browser_scratchpad_execute_print.js b/devtools/client/scratchpad/test/browser_scratchpad_execute_print.js
+--- a/devtools/client/scratchpad/test/browser_scratchpad_execute_print.js
++++ b/devtools/client/scratchpad/test/browser_scratchpad_execute_print.js
+@@ -13,79 +13,79 @@ function test() {
+ 
+   content.location = "data:text/html,<p>test run() and display() in Scratchpad";
+ }
+ 
+ function runTests() {
+   let sp = gScratchpadWindow.Scratchpad;
+   let tests = [{
+     method: "run",
+-    prepare: function* () {
+-      yield inContent(function* () {
++    prepare: async function () {
++      await inContent(function() {
+         content.wrappedJSObject.foobarBug636725 = 1;
+       });
+       sp.editor.setText("++window.foobarBug636725");
+     },
+-    then: function* ([code, , result]) {
++    then: async function ([code, , result]) {
+       is(code, sp.getText(), "code is correct");
+ 
+-      let pageResult = yield inContent(function* () {
++      let pageResult = await inContent(function() {
+         return content.wrappedJSObject.foobarBug636725;
+       });
+       is(result, pageResult,
+          "result is correct");
+ 
+       is(sp.getText(), "++window.foobarBug636725",
+          "run() does not change the editor content");
+ 
+       is(pageResult, 2, "run() updated window.foobarBug636725");
+     }
+   }, {
+     method: "display",
+-    prepare: function* () {},
+-    then: function* () {
+-      let pageResult = yield inContent(function* () {
++    prepare: function() {},
++    then: async function () {
++      let pageResult = await inContent(function() {
+         return content.wrappedJSObject.foobarBug636725;
+       });
+       is(pageResult, 3, "display() updated window.foobarBug636725");
+ 
+       is(sp.getText(), "++window.foobarBug636725\n/*\n3\n*/",
+          "display() shows evaluation result in the textbox");
+ 
+       is(sp.editor.getSelection(), "\n/*\n3\n*/", "getSelection is correct");
+     }
+   }, {
+     method: "run",
+-    prepare: function* () {
++    prepare: function() {
+       sp.editor.setText("window.foobarBug636725 = 'a';\n" +
+         "window.foobarBug636725 = 'b';");
+       sp.editor.setSelection({ line: 0, ch: 0 }, { line: 0, ch: 29 });
+     },
+-    then: function* ([code, , result]) {
++    then: async function ([code, , result]) {
+       is(code, "window.foobarBug636725 = 'a';", "code is correct");
+       is(result, "a", "result is correct");
+ 
+       is(sp.getText(), "window.foobarBug636725 = 'a';\n" +
+                        "window.foobarBug636725 = 'b';",
+          "run() does not change the textbox value");
+ 
+-      let pageResult = yield inContent(function* () {
++      let pageResult = await inContent(function() {
+         return content.wrappedJSObject.foobarBug636725;
+       });
+       is(pageResult, "a", "run() worked for the selected range");
+     }
+   }, {
+     method: "display",
+-    prepare: function* () {
++    prepare: function() {
+       sp.editor.setText("window.foobarBug636725 = 'c';\n" +
+                  "window.foobarBug636725 = 'b';");
+       sp.editor.setSelection({ line: 0, ch: 0 }, { line: 0, ch: 22 });
+     },
+-    then: function* () {
+-      let pageResult = yield inContent(function* () {
++    then: async function () {
++      let pageResult = await inContent(function() {
+         return content.wrappedJSObject.foobarBug636725;
+       });
+       is(pageResult, "a", "display() worked for the selected range");
+ 
+       is(sp.getText(), "window.foobarBug636725" +
+                        "\n/*\na\n*/" +
+                        " = 'c';\n" +
+                        "window.foobarBug636725 = 'b';",
+diff --git a/devtools/client/scratchpad/test/browser_scratchpad_inspect_primitives.js b/devtools/client/scratchpad/test/browser_scratchpad_inspect_primitives.js
+--- a/devtools/client/scratchpad/test/browser_scratchpad_inspect_primitives.js
++++ b/devtools/client/scratchpad/test/browser_scratchpad_inspect_primitives.js
+@@ -1,45 +1,44 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ // Test that inspecting primitive values uses the object inspector, not an
+ // inline comment.
+ 
+-var {Task} = require("devtools/shared/task");
+ 
+ function test() {
+   const options = {
+     tabContent: "test inspecting primitive values"
+   };
+   openTabAndScratchpad(options)
+     .then(Task.async(runTests))
+     .then(finish, console.error);
+ }
+ 
+-function* runTests([win, sp]) {
++async function runTests([win, sp]) {
+   // Inspect a number.
+-  yield checkResults(sp, 7);
++  await checkResults(sp, 7);
+ 
+   // Inspect a string.
+-  yield checkResults(sp, "foobar", true);
++  await checkResults(sp, "foobar", true);
+ 
+   // Inspect a boolean.
+-  yield checkResults(sp, true);
++  await checkResults(sp, true);
+ }
+ 
+ // Helper function that does the actual testing.
+-var checkResults = Task.async(function* (sp, value, isString = false) {
++var checkResults = async function (sp, value, isString = false) {
+   let sourceValue = value;
+   if (isString) {
+     sourceValue = '"' + value + '"';
+   }
+   let source = "var foobar = " + sourceValue + "; foobar";
+   sp.setText(source);
+-  yield sp.inspect();
++  await sp.inspect();
+ 
+   let sidebar = sp.sidebar;
+   ok(sidebar.visible, "sidebar is open");
+ 
+   let found = false;
+ 
+   outer: for (let scope of sidebar.variablesView) {
+     for (let [, obj] of scope) {
+@@ -53,9 +52,9 @@ var checkResults = Task.async(function* 
+   }
+ 
+   ok(found, "found the value of " + value);
+ 
+   let tabbox = sidebar._sidebar._tabbox;
+   ok(!tabbox.hasAttribute("hidden"), "Scratchpad sidebar visible");
+   sidebar.hide();
+   ok(tabbox.hasAttribute("hidden"), "Scratchpad sidebar hidden");
+-});
++};
+diff --git a/devtools/client/scratchpad/test/head.js b/devtools/client/scratchpad/test/head.js
+--- a/devtools/client/scratchpad/test/head.js
++++ b/devtools/client/scratchpad/test/head.js
+@@ -188,23 +188,23 @@ function runAsyncTests(aScratchpad, aTes
+  *          Scratchpad method to use, one of "run", "display", or "inspect".
+  *        - prepare
+  *          The callback to run just prior to executing the scratchpad method.
+  *        - then
+  *          The callback to run when the scratchpad execution promise resolves.
+  * @return Promise
+  *         The promise that will be resolved when all tests are finished.
+  */
+-var runAsyncCallbackTests = Task.async(function* (aScratchpad, aTests) {
++var runAsyncCallbackTests = async function (aScratchpad, aTests) {
+   for (let {prepare, method, then} of aTests) {
+-    yield prepare();
+-    let res = yield aScratchpad[method]();
+-    yield then(res);
++    await prepare();
++    let res = await aScratchpad[method]();
++    await then(res);
+   }
+-});
++};
+ 
+ /**
+  * A simple wrapper for ContentTask.spawn for more compact code.
+  */
+ function inContent(generator) {
+   return ContentTask.spawn(gBrowser.selectedBrowser, {}, generator);
+ }
+ 

+ 2950 - 0
frg/work-js/mozilla-release/patches/1440321-1l-shadereditor-61a1.patch

@@ -0,0 +1,2950 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  a8d7914a6e51300575b29a5bfb57ae841d7e70fa
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1l. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/shadereditor/shadereditor.js b/devtools/client/shadereditor/shadereditor.js
+--- a/devtools/client/shadereditor/shadereditor.js
++++ b/devtools/client/shadereditor/shadereditor.js
+@@ -3,25 +3,25 @@
+  * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
+ const {SideMenuWidget} = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
+ const promise = require("promise");
+ const defer = require("devtools/shared/defer");
++const {Task} = require("devtools/shared/task");
+ const Services = require("Services");
+ const EventEmitter = require("devtools/shared/old-event-emitter");
+ const Tooltip = require("devtools/client/shared/widgets/tooltip/Tooltip");
+ const Editor = require("devtools/client/sourceeditor/editor");
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const {extend} = require("devtools/shared/extend");
+ const {WidgetMethods, setNamedTimeout} =
+   require("devtools/client/shared/widgets/view-helpers");
+-const {Task} = require("devtools/shared/task");
+ 
+ // Use privileged promise in panel documents to prevent having them to freeze
+ // during toolbox destruction. See bug 1402779.
+ const Promise = require("Promise");
+ 
+ // The panel's window global is an EventEmitter firing the following events:
+ const EVENTS = {
+   // When new programs are received from the server.
+@@ -400,24 +400,24 @@ var ShadersEditorsView = {
+    */
+   setText: function (sources) {
+     let view = this;
+     function setTextAndClearHistory(editor, text) {
+       editor.setText(text);
+       editor.clearHistory();
+     }
+ 
+-    return Task.spawn(function* () {
+-      yield view._toggleListeners("off");
+-      yield promise.all([
++    return (async function () {
++      await view._toggleListeners("off");
++      await promise.all([
+         view._getEditor("vs").then(e => setTextAndClearHistory(e, sources.vs)),
+         view._getEditor("fs").then(e => setTextAndClearHistory(e, sources.fs))
+       ]);
+-      yield view._toggleListeners("on");
+-    }).then(() => window.emit(EVENTS.SOURCES_SHOWN, sources));
++      await view._toggleListeners("on");
++    })().then(() => window.emit(EVENTS.SOURCES_SHOWN, sources));
+   },
+ 
+   /**
+    * Lazily initializes and returns a promise for an Editor instance.
+    *
+    * @param string type
+    *        Specifies for which shader type should an editor be retrieved,
+    *        either are "vs" for a vertex, or "fs" for a fragment shader.
+@@ -493,27 +493,27 @@ var ShadersEditorsView = {
+   /**
+    * Recompiles the source code for the shader being edited.
+    * This function is fired at a certain delay after the user stops typing.
+    *
+    * @param string type
+    *        The corresponding shader type for the focused editor (e.g. "vs").
+    */
+   _doCompile: function (type) {
+-    Task.spawn(function* () {
+-      let editor = yield this._getEditor(type);
+-      let shaderActor = yield ShadersListView.selectedAttachment[type];
++    (async function () {
++      let editor = await this._getEditor(type);
++      let shaderActor = await ShadersListView.selectedAttachment[type];
+ 
+       try {
+-        yield shaderActor.compile(editor.getText());
++        await shaderActor.compile(editor.getText());
+         this._onSuccessfulCompilation();
+       } catch (e) {
+         this._onFailedCompilation(type, editor, e);
+       }
+-    }.bind(this));
++    }.bind(this))();
+   },
+ 
+   /**
+    * Called uppon a successful shader compilation.
+    */
+   _onSuccessfulCompilation: function () {
+     // Signal that the shader was compiled successfully.
+     window.emit(EVENTS.SHADER_COMPILED, null);
+diff --git a/devtools/client/shadereditor/test/browser_se_aaa_run_first_leaktest.js b/devtools/client/shadereditor/test/browser_se_aaa_run_first_leaktest.js
+--- a/devtools/client/shadereditor/test/browser_se_aaa_run_first_leaktest.js
++++ b/devtools/client/shadereditor/test/browser_se_aaa_run_first_leaktest.js
+@@ -1,17 +1,17 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the shader editor leaks on initialization and sudden destruction.
+  * You can also use this initialization format as a template for other tests.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(SIMPLE_CANVAS_URL);
+ 
+   ok(target, "Should have a target available.");
+   ok(panel, "Should have a panel available.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_bfcache.js b/devtools/client/shadereditor/test/browser_se_bfcache.js
+--- a/devtools/client/shadereditor/test/browser_se_bfcache.js
++++ b/devtools/client/shadereditor/test/browser_se_bfcache.js
+@@ -1,60 +1,60 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the shader editor works with bfcache.
+  */
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(SIMPLE_CANVAS_URL);
+   let { gFront, $, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
+ 
+   // Attach frame scripts if in e10s to perform
+   // history navigation via the content
+   loadFrameScripts();
+ 
+   let reloaded = reload(target);
+-  let firstProgram = yield once(gFront, "program-linked");
+-  yield reloaded;
++  let firstProgram = await once(gFront, "program-linked");
++  await reloaded;
+ 
+   let navigated = navigate(target, MULTIPLE_CONTEXTS_URL);
+-  let [secondProgram, thirdProgram] = yield getPrograms(gFront, 2);
+-  yield navigated;
++  let [secondProgram, thirdProgram] = await getPrograms(gFront, 2);
++  await navigated;
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+-  yield navigateInHistory(target, "back", "will-navigate");
+-  yield once(panel.panelWin, EVENTS.PROGRAMS_ADDED);
+-  yield once(panel.panelWin, EVENTS.SOURCES_SHOWN);
++  await navigateInHistory(target, "back", "will-navigate");
++  await once(panel.panelWin, EVENTS.PROGRAMS_ADDED);
++  await once(panel.panelWin, EVENTS.SOURCES_SHOWN);
+ 
+   is($("#content").hidden, false,
+     "The tool's content should not be hidden.");
+   is(ShadersListView.itemCount, 1,
+     "The shaders list contains one entry after navigating back.");
+   is(ShadersListView.selectedIndex, 0,
+     "The shaders list has a correct selection after navigating back.");
+ 
+   is(vsEditor.getText().indexOf("gl_Position"), 170,
+     "The vertex shader editor contains the correct text.");
+   is(fsEditor.getText().indexOf("gl_FragColor"), 97,
+     "The fragment shader editor contains the correct text.");
+ 
+-  yield navigateInHistory(target, "forward", "will-navigate");
+-  yield once(panel.panelWin, EVENTS.PROGRAMS_ADDED);
+-  yield once(panel.panelWin, EVENTS.SOURCES_SHOWN);
++  await navigateInHistory(target, "forward", "will-navigate");
++  await once(panel.panelWin, EVENTS.PROGRAMS_ADDED);
++  await once(panel.panelWin, EVENTS.SOURCES_SHOWN);
+ 
+   is($("#content").hidden, false,
+     "The tool's content should not be hidden.");
+   is(ShadersListView.itemCount, 2,
+     "The shaders list contains two entries after navigating forward.");
+   is(ShadersListView.selectedIndex, 0,
+     "The shaders list has a correct selection after navigating forward.");
+ 
+   is(vsEditor.getText().indexOf("gl_Position"), 100,
+     "The vertex shader editor contains the correct text.");
+   is(fsEditor.getText().indexOf("gl_FragColor"), 89,
+     "The fragment shader editor contains the correct text.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_editors-contents.js b/devtools/client/shadereditor/test/browser_se_editors-contents.js
+--- a/devtools/client/shadereditor/test/browser_se_editors-contents.js
++++ b/devtools/client/shadereditor/test/browser_se_editors-contents.js
+@@ -1,30 +1,30 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the editors contain the correct text when a program
+  * becomes available.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(SIMPLE_CANVAS_URL);
+   let { gFront, ShadersEditorsView, EVENTS } = panel.panelWin;
+ 
+   reload(target);
+-  yield promise.all([
++  await promise.all([
+     once(gFront, "program-linked"),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]);
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+ 
+   is(vsEditor.getText().indexOf("gl_Position"), 170,
+     "The vertex shader editor contains the correct text.");
+   is(fsEditor.getText().indexOf("gl_FragColor"), 97,
+     "The fragment shader editor contains the correct text.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_editors-error-gutter.js b/devtools/client/shadereditor/test/browser_se_editors-error-gutter.js
+--- a/devtools/client/shadereditor/test/browser_se_editors-error-gutter.js
++++ b/devtools/client/shadereditor/test/browser_se_editors-error-gutter.js
+@@ -1,82 +1,82 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if error indicators are shown in the editor's gutter and text area
+  * when there's a shader compilation error.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(SIMPLE_CANVAS_URL);
+   let { gFront, EVENTS, ShadersEditorsView } = panel.panelWin;
+ 
+   reload(target);
+-  yield promise.all([
++  await promise.all([
+     once(gFront, "program-linked"),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]);
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+   vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
+-  let [, vertError] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
++  let [, vertError] = await onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
+   checkHasVertFirstError(true, vertError);
+   checkHasVertSecondError(false, vertError);
+   info("Error marks added in the vertex shader editor.");
+ 
+   vsEditor.insertText(" ", { line: 1, ch: 0 });
+-  yield once(panel.panelWin, EVENTS.EDITOR_ERROR_MARKERS_REMOVED);
++  await once(panel.panelWin, EVENTS.EDITOR_ERROR_MARKERS_REMOVED);
+   is(vsEditor.getText(1), "       precision lowp float;", "Typed space.");
+   checkHasVertFirstError(false, vertError);
+   checkHasVertSecondError(false, vertError);
+   info("Error marks removed while typing in the vertex shader editor.");
+ 
+-  [, vertError] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
++  [, vertError] = await onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
+   checkHasVertFirstError(true, vertError);
+   checkHasVertSecondError(false, vertError);
+   info("Error marks were re-added after recompiling the vertex shader.");
+ 
+   fsEditor.replaceText("vec4", { line: 2, ch: 14 }, { line: 2, ch: 18 });
+-  let [, fragError] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
++  let [, fragError] = await onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
+   checkHasVertFirstError(true, vertError);
+   checkHasVertSecondError(false, vertError);
+   checkHasFragError(true, fragError);
+   info("Error marks added in the fragment shader editor.");
+ 
+   fsEditor.insertText(" ", { line: 1, ch: 0 });
+-  yield once(panel.panelWin, EVENTS.EDITOR_ERROR_MARKERS_REMOVED);
++  await once(panel.panelWin, EVENTS.EDITOR_ERROR_MARKERS_REMOVED);
+   is(fsEditor.getText(1), "       precision lowp float;", "Typed space.");
+   checkHasVertFirstError(true, vertError);
+   checkHasVertSecondError(false, vertError);
+   checkHasFragError(false, fragError);
+   info("Error marks removed while typing in the fragment shader editor.");
+ 
+-  [, fragError] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
++  [, fragError] = await onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
+   checkHasVertFirstError(true, vertError);
+   checkHasVertSecondError(false, vertError);
+   checkHasFragError(true, fragError);
+   info("Error marks were re-added after recompiling the fragment shader.");
+ 
+   vsEditor.replaceText("2", { line: 3, ch: 19 }, { line: 3, ch: 20 });
+-  yield once(panel.panelWin, EVENTS.EDITOR_ERROR_MARKERS_REMOVED);
++  await once(panel.panelWin, EVENTS.EDITOR_ERROR_MARKERS_REMOVED);
+   checkHasVertFirstError(false, vertError);
+   checkHasVertSecondError(false, vertError);
+   checkHasFragError(true, fragError);
+   info("Error marks removed while typing in the vertex shader editor again.");
+ 
+-  [, vertError] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
++  [, vertError] = await onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
+   checkHasVertFirstError(true, vertError);
+   checkHasVertSecondError(true, vertError);
+   checkHasFragError(true, fragError);
+   info("Error marks were re-added after recompiling the fragment shader again.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ 
+   function checkHasVertFirstError(bool, error) {
+     ok(error, "Vertex shader compiled with errors.");
+     isnot(error.link, "", "The linkage status should not be empty.");
+ 
+     let line = 7;
+     info("Checking first vertex shader error on line " + line + "...");
+diff --git a/devtools/client/shadereditor/test/browser_se_editors-error-tooltip.js b/devtools/client/shadereditor/test/browser_se_editors-error-tooltip.js
+--- a/devtools/client/shadereditor/test/browser_se_editors-error-tooltip.js
++++ b/devtools/client/shadereditor/test/browser_se_editors-error-tooltip.js
+@@ -1,31 +1,31 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if error tooltips can be opened from the editor's gutter when there's
+  * a shader compilation error.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(SIMPLE_CANVAS_URL);
+   let { gFront, EVENTS, ShadersEditorsView } = panel.panelWin;
+ 
+   reload(target);
+-  yield promise.all([
++  await promise.all([
+     once(gFront, "program-linked"),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]);
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+   vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
+-  yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
++  await once(panel.panelWin, EVENTS.SHADER_COMPILED);
+ 
+   // Synthesizing 'mouseover' events doesn't work, hack around this by
+   // manually calling the event listener with the expected arguments.
+   let editorDocument = vsEditor.container.contentDocument;
+   let marker = editorDocument.querySelector(".error");
+   let parsed = ShadersEditorsView._errors.vs[0].messages;
+   ShadersEditorsView._onMarkerMouseOver(7, marker, parsed);
+ 
+@@ -50,11 +50,11 @@ function* ifWebGLSupported() {
+ 
+   ok(messages[0].textContent.includes("'constructor' : too many arguments"),
+     "The first message contains the correct text.");
+   ok(messages[1].textContent.includes("'=' : dimension mismatch"),
+     "The second message contains the correct text.");
+   ok(messages[2].textContent.includes("'assign' : cannot convert"),
+     "The third message contains the correct text.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_editors-lazy-init.js b/devtools/client/shadereditor/test/browser_se_editors-lazy-init.js
+--- a/devtools/client/shadereditor/test/browser_se_editors-lazy-init.js
++++ b/devtools/client/shadereditor/test/browser_se_editors-lazy-init.js
+@@ -1,34 +1,34 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if source editors are lazily initialized.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(SIMPLE_CANVAS_URL);
+   let { gFront, ShadersEditorsView } = panel.panelWin;
+ 
+   reload(target);
+-  yield once(gFront, "program-linked");
++  await once(gFront, "program-linked");
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+   ok(vsEditor, "A vertex shader editor was initialized.");
+   ok(fsEditor, "A fragment shader editor was initialized.");
+ 
+   isnot(vsEditor, fsEditor,
+     "The vertex shader editor is distinct from the fragment shader editor.");
+ 
+-  let vsEditor2 = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor2 = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor2 = await ShadersEditorsView._getEditor("vs");
++  let fsEditor2 = await ShadersEditorsView._getEditor("fs");
+ 
+   is(vsEditor, vsEditor2,
+     "The vertex shader editor instances are cached.");
+   is(fsEditor, fsEditor2,
+     "The fragment shader editor instances are cached.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_first-run.js b/devtools/client/shadereditor/test/browser_se_first-run.js
+--- a/devtools/client/shadereditor/test/browser_se_first-run.js
++++ b/devtools/client/shadereditor/test/browser_se_first-run.js
+@@ -1,43 +1,43 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the shader editor shows the appropriate UI when opened.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(SIMPLE_CANVAS_URL);
+   let { gFront, $ } = panel.panelWin;
+ 
+   is($("#reload-notice").hidden, false,
+     "The 'reload this page' notice should initially be visible.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for a WebGL context' notice should initially be hidden.");
+   is($("#content").hidden, true,
+     "The tool's content should initially be hidden.");
+ 
+   let navigating = once(target, "will-navigate");
+   let linked = once(gFront, "program-linked");
+   reload(target);
+ 
+-  yield navigating;
++  await navigating;
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should be hidden when navigating.");
+   is($("#waiting-notice").hidden, false,
+     "The 'waiting for a WebGL context' notice should be visible when navigating.");
+   is($("#content").hidden, true,
+     "The tool's content should still be hidden.");
+ 
+-  yield linked;
++  await linked;
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should be hidden after linking.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for a WebGL context' notice should be hidden after linking.");
+   is($("#content").hidden, false,
+     "The tool's content should not be hidden anymore.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_navigation.js b/devtools/client/shadereditor/test/browser_se_navigation.js
+--- a/devtools/client/shadereditor/test/browser_se_navigation.js
++++ b/devtools/client/shadereditor/test/browser_se_navigation.js
+@@ -1,21 +1,21 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests target navigations are handled correctly in the UI.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(SIMPLE_CANVAS_URL);
+   let { gFront, $, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
+ 
+   reload(target);
+-  yield promise.all([
++  await promise.all([
+     once(gFront, "program-linked"),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]);
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should be hidden after linking.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for a WebGL context' notice should be visible after linking.");
+@@ -24,48 +24,48 @@ function* ifWebGLSupported() {
+ 
+   is(ShadersListView.itemCount, 1,
+     "The shaders list contains one entry.");
+   is(ShadersListView.selectedItem, ShadersListView.items[0],
+     "The shaders list has a correct item selected.");
+   is(ShadersListView.selectedIndex, 0,
+     "The shaders list has a correct index selected.");
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+   is(vsEditor.getText().indexOf("gl_Position"), 170,
+     "The vertex shader editor contains the correct text.");
+   is(fsEditor.getText().indexOf("gl_FragColor"), 97,
+     "The fragment shader editor contains the correct text.");
+ 
+   let navigating = once(target, "will-navigate");
+   let navigated = once(target, "will-navigate");
+   navigate(target, "about:blank");
+ 
+-  yield promise.all([navigating, once(panel.panelWin, EVENTS.UI_RESET) ]);
++  await promise.all([navigating, once(panel.panelWin, EVENTS.UI_RESET) ]);
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should be hidden while navigating.");
+   is($("#waiting-notice").hidden, false,
+     "The 'waiting for a WebGL context' notice should be visible while navigating.");
+   is($("#content").hidden, true,
+     "The tool's content should be hidden now that there's no WebGL content.");
+ 
+   is(ShadersListView.itemCount, 0,
+     "The shaders list should be empty.");
+   is(ShadersListView.selectedItem, null,
+     "The shaders list has no correct item.");
+   is(ShadersListView.selectedIndex, -1,
+     "The shaders list has a negative index.");
+ 
+-  yield navigated;
++  await navigated;
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should still be hidden after navigating.");
+   is($("#waiting-notice").hidden, false,
+     "The 'waiting for a WebGL context' notice should still be visible after navigating.");
+   is($("#content").hidden, true,
+     "The tool's content should be still hidden since there's no WebGL content.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_programs-blackbox-01.js b/devtools/client/shadereditor/test/browser_se_programs-blackbox-01.js
+--- a/devtools/client/shadereditor/test/browser_se_programs-blackbox-01.js
++++ b/devtools/client/shadereditor/test/browser_se_programs-blackbox-01.js
+@@ -1,46 +1,46 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if blackboxing a program works properly.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, debuggee, panel } = yield initShaderEditor(MULTIPLE_CONTEXTS_URL);
++async function ifWebGLSupported() {
++  let { target, debuggee, panel } = await initShaderEditor(MULTIPLE_CONTEXTS_URL);
+   let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
+ 
+   once(panel.panelWin, EVENTS.SHADER_COMPILED).then(() => {
+     ok(false, "No shaders should be publicly compiled during this test.");
+   });
+ 
+   reload(target);
+-  let [[firstProgramActor, secondProgramActor]] = yield promise.all([
++  let [[firstProgramActor, secondProgramActor]] = await promise.all([
+     getPrograms(gFront, 2),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]);
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+   vsEditor.once("change", () => {
+     ok(false, "The vertex shader source was unexpectedly changed.");
+   });
+   fsEditor.once("change", () => {
+     ok(false, "The fragment shader source was unexpectedly changed.");
+   });
+   once(panel.panelWin, EVENTS.SOURCES_SHOWN).then(() => {
+     ok(false, "No sources should be changed form this point onward.");
+   });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+ 
+   ok(!ShadersListView.selectedAttachment.isBlackBoxed,
+     "The first program should not be blackboxed yet.");
+   is(getBlackBoxCheckbox(panel, 0).checked, true,
+     "The first blackbox checkbox should be initially checked.");
+   ok(!ShadersListView.attachments[1].isBlackBoxed,
+     "The second program should not be blackboxed yet.");
+   is(getBlackBoxCheckbox(panel, 1).checked, true,
+@@ -52,108 +52,108 @@ function* ifWebGLSupported() {
+     "The first program should now be blackboxed.");
+   is(getBlackBoxCheckbox(panel, 0).checked, false,
+     "The first blackbox checkbox should now be unchecked.");
+   ok(!ShadersListView.attachments[1].isBlackBoxed,
+     "The second program should still not be blackboxed.");
+   is(getBlackBoxCheckbox(panel, 1).checked, true,
+     "The second blackbox checkbox should still be checked.");
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The first program was correctly blackboxed.");
+ 
+   getBlackBoxCheckbox(panel, 1).click();
+ 
+   ok(ShadersListView.selectedAttachment.isBlackBoxed,
+     "The first program should still be blackboxed.");
+   is(getBlackBoxCheckbox(panel, 0).checked, false,
+     "The first blackbox checkbox should still be unchecked.");
+   ok(ShadersListView.attachments[1].isBlackBoxed,
+     "The second program should now be blackboxed.");
+   is(getBlackBoxCheckbox(panel, 1).checked, false,
+     "The second blackbox checkbox should now be unchecked.");
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+   ok(true, "The second program was correctly blackboxed.");
+ 
+   ShadersListView._onProgramMouseOver({ target: getItemLabel(panel, 0) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+   ok(true, "Highlighting shouldn't work while blackboxed (1).");
+ 
+   ShadersListView._onProgramMouseOut({ target: getItemLabel(panel, 0) });
+   ShadersListView._onProgramMouseOver({ target: getItemLabel(panel, 1) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+   ok(true, "Highlighting shouldn't work while blackboxed (2).");
+ 
+   ShadersListView._onProgramMouseOut({ target: getItemLabel(panel, 1) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
+   ok(true, "Highlighting shouldn't work while blackboxed (3).");
+ 
+   getBlackBoxCheckbox(panel, 0).click();
+   getBlackBoxCheckbox(panel, 1).click();
+ 
+   ok(!ShadersListView.selectedAttachment.isBlackBoxed,
+     "The first program should now be unblackboxed.");
+   is(getBlackBoxCheckbox(panel, 0).checked, true,
+     "The first blackbox checkbox should now be rechecked.");
+   ok(!ShadersListView.attachments[1].isBlackBoxed,
+     "The second program should now be unblackboxed.");
+   is(getBlackBoxCheckbox(panel, 1).checked, true,
+     "The second blackbox checkbox should now be rechecked.");
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The two programs were correctly unblackboxed.");
+ 
+   ShadersListView._onProgramMouseOver({ target: getItemLabel(panel, 0) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The first program was correctly highlighted.");
+ 
+   ShadersListView._onProgramMouseOut({ target: getItemLabel(panel, 0) });
+   ShadersListView._onProgramMouseOver({ target: getItemLabel(panel, 1) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2");
+   ok(true, "The second program was correctly highlighted.");
+ 
+   ShadersListView._onProgramMouseOut({ target: getItemLabel(panel, 1) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The two programs were correctly unhighlighted.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ 
+ function getItemLabel(aPanel, aIndex) {
+   return aPanel.panelWin.document.querySelectorAll(
+     ".side-menu-widget-item-contents")[aIndex];
+ }
+ 
+diff --git a/devtools/client/shadereditor/test/browser_se_programs-blackbox-02.js b/devtools/client/shadereditor/test/browser_se_programs-blackbox-02.js
+--- a/devtools/client/shadereditor/test/browser_se_programs-blackbox-02.js
++++ b/devtools/client/shadereditor/test/browser_se_programs-blackbox-02.js
+@@ -1,63 +1,63 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if blackboxing a program works properly in tandem with blended
+  * overlapping geometry.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, debuggee, panel } = yield initShaderEditor(BLENDED_GEOMETRY_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, debuggee, panel } = await initShaderEditor(BLENDED_GEOMETRY_CANVAS_URL);
+   let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
+ 
+   reload(target);
+-  let [[firstProgramActor, secondProgramActor]] = yield promise.all([
++  let [[firstProgramActor, secondProgramActor]] = await promise.all([
+     getPrograms(gFront, 2),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]);
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
+   ok(true, "The canvas was correctly drawn.");
+ 
+   getBlackBoxCheckbox(panel, 0).click();
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 0, b: 0, a: 127 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 0, b: 0, a: 127 }, true);
+   ok(true, "The first program was correctly blackboxed.");
+ 
+   getBlackBoxCheckbox(panel, 0).click();
+   getBlackBoxCheckbox(panel, 1).click();
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 127, g: 127, b: 127, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 127, g: 127, b: 127, a: 255 }, true);
+   ok(true, "The second program was correctly blackboxed.");
+ 
+   getBlackBoxCheckbox(panel, 1).click();
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
+   ok(true, "The two programs were correctly unblackboxed.");
+ 
+   getBlackBoxCheckbox(panel, 0).click();
+   getBlackBoxCheckbox(panel, 1).click();
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+   ok(true, "The two programs were correctly blackboxed again.");
+ 
+   getBlackBoxCheckbox(panel, 0).click();
+   getBlackBoxCheckbox(panel, 1).click();
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
+   ok(true, "The two programs were correctly unblackboxed again.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ 
+ function getBlackBoxCheckbox(aPanel, aIndex) {
+   return aPanel.panelWin.document.querySelectorAll(
+     ".side-menu-widget-item-checkbox")[aIndex];
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_programs-cache.js b/devtools/client/shadereditor/test/browser_se_programs-cache.js
+--- a/devtools/client/shadereditor/test/browser_se_programs-cache.js
++++ b/devtools/client/shadereditor/test/browser_se_programs-cache.js
+@@ -1,41 +1,41 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that program and shader actors are cached in the frontend.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, debuggee, panel } = yield initShaderEditor(MULTIPLE_CONTEXTS_URL);
++async function ifWebGLSupported() {
++  let { target, debuggee, panel } = await initShaderEditor(MULTIPLE_CONTEXTS_URL);
+   let { EVENTS, gFront, ShadersListView, ShadersEditorsView } = panel.panelWin;
+ 
+   reload(target);
+-  let [[programActor]] = yield promise.all([
++  let [[programActor]] = await promise.all([
+     getPrograms(gFront, 1),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]);
+ 
+   let programItem = ShadersListView.selectedItem;
+ 
+   is(programItem.attachment.programActor, programActor,
+     "The correct program actor is cached for the selected item.");
+ 
+-  is((yield programActor.getVertexShader()),
+-     (yield programItem.attachment.vs),
++  is((await programActor.getVertexShader()),
++     (await programItem.attachment.vs),
+     "The cached vertex shader promise returns the correct actor.");
+ 
+-  is((yield programActor.getFragmentShader()),
+-     (yield programItem.attachment.fs),
++  is((await programActor.getFragmentShader()),
++     (await programItem.attachment.fs),
+     "The cached fragment shader promise returns the correct actor.");
+ 
+-  is((yield (yield programActor.getVertexShader()).getText()),
+-     (yield (yield ShadersEditorsView._getEditor("vs")).getText()),
++  is((await (await programActor.getVertexShader()).getText()),
++     (await (await ShadersEditorsView._getEditor("vs")).getText()),
+     "The cached vertex shader promise returns the correct text.");
+ 
+-  is((yield (yield programActor.getFragmentShader()).getText()),
+-     (yield (yield ShadersEditorsView._getEditor("fs")).getText()),
++  is((await (await programActor.getFragmentShader()).getText()),
++     (await (await ShadersEditorsView._getEditor("fs")).getText()),
+     "The cached fragment shader promise returns the correct text.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_programs-highlight-01.js b/devtools/client/shadereditor/test/browser_se_programs-highlight-01.js
+--- a/devtools/client/shadereditor/test/browser_se_programs-highlight-01.js
++++ b/devtools/client/shadereditor/test/browser_se_programs-highlight-01.js
+@@ -1,89 +1,89 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if highlighting a program works properly.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(MULTIPLE_CONTEXTS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(MULTIPLE_CONTEXTS_URL);
+   let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
+ 
+   once(panel.panelWin, EVENTS.SHADER_COMPILED).then(() => {
+     ok(false, "No shaders should be publicly compiled during this test.");
+   });
+ 
+   reload(target);
+-  let [[firstProgramActor, secondProgramActor]] = yield promise.all([
++  let [[firstProgramActor, secondProgramActor]] = await promise.all([
+     getPrograms(gFront, 2),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]);
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+   vsEditor.once("change", () => {
+     ok(false, "The vertex shader source was unexpectedly changed.");
+   });
+   fsEditor.once("change", () => {
+     ok(false, "The fragment shader source was unexpectedly changed.");
+   });
+   once(panel.panelWin, EVENTS.SOURCES_SHOWN).then(() => {
+     ok(false, "No sources should be changed form this point onward.");
+   });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+ 
+   ShadersListView._onProgramMouseOver({ target: getItemLabel(panel, 0) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The first program was correctly highlighted.");
+ 
+   ShadersListView._onProgramMouseOut({ target: getItemLabel(panel, 0) });
+   ShadersListView._onProgramMouseOver({ target: getItemLabel(panel, 1) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2");
+   ok(true, "The second program was correctly highlighted.");
+ 
+   ShadersListView._onProgramMouseOut({ target: getItemLabel(panel, 1) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The two programs were correctly unhighlighted.");
+ 
+   ShadersListView._onProgramMouseOver({ target: getBlackBoxCheckbox(panel, 0) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The two programs were left unchanged after hovering a blackbox checkbox.");
+ 
+   ShadersListView._onProgramMouseOut({ target: getBlackBoxCheckbox(panel, 0) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The two programs were left unchanged after unhovering a blackbox checkbox.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ 
+ function getItemLabel(aPanel, aIndex) {
+   return aPanel.panelWin.document.querySelectorAll(
+     ".side-menu-widget-item-contents")[aIndex];
+ }
+ 
+diff --git a/devtools/client/shadereditor/test/browser_se_programs-highlight-02.js b/devtools/client/shadereditor/test/browser_se_programs-highlight-02.js
+--- a/devtools/client/shadereditor/test/browser_se_programs-highlight-02.js
++++ b/devtools/client/shadereditor/test/browser_se_programs-highlight-02.js
+@@ -1,49 +1,49 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if highlighting a program works properly in tandem with blended
+  * overlapping geometry.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, debuggee, panel } = yield initShaderEditor(BLENDED_GEOMETRY_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, debuggee, panel } = await initShaderEditor(BLENDED_GEOMETRY_CANVAS_URL);
+   let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
+ 
+   reload(target);
+-  let [[firstProgramActor, secondProgramActor]] = yield promise.all([
++  let [[firstProgramActor, secondProgramActor]] = await promise.all([
+     getPrograms(gFront, 2),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]);
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
+   ok(true, "The canvas was correctly drawn.");
+ 
+   ShadersListView._onProgramMouseOver({ target: getItemLabel(panel, 0) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 0, b: 32, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 0, b: 32, a: 127 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 0, b: 32, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 0, b: 32, a: 127 }, true);
+   ok(true, "The first program was correctly highlighted.");
+ 
+   ShadersListView._onProgramMouseOut({ target: getItemLabel(panel, 0) });
+   ShadersListView._onProgramMouseOver({ target: getItemLabel(panel, 1) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 255, g: 0, b: 64, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 255, g: 0, b: 64, a: 255 }, true);
+   ok(true, "The second program was correctly highlighted.");
+ 
+   ShadersListView._onProgramMouseOut({ target: getItemLabel(panel, 1) });
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true);
+   ok(true, "The two programs were correctly unhighlighted.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+ 
+ function getItemLabel(aPanel, aIndex) {
+   return aPanel.panelWin.document.querySelectorAll(
+     ".side-menu-widget-item-contents")[aIndex];
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_programs-list.js b/devtools/client/shadereditor/test/browser_se_programs-list.js
+--- a/devtools/client/shadereditor/test/browser_se_programs-list.js
++++ b/devtools/client/shadereditor/test/browser_se_programs-list.js
+@@ -1,76 +1,76 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the programs list contains an entry after vertex and fragment
+  * shaders are linked.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(MULTIPLE_CONTEXTS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(MULTIPLE_CONTEXTS_URL);
+   let { gFront, EVENTS, L10N, ShadersListView, ShadersEditorsView } = panel.panelWin;
+ 
+   is(ShadersListView.itemCount, 0,
+     "The shaders list should initially be empty.");
+   is(ShadersListView.selectedItem, null,
+     "The shaders list has no selected item.");
+   is(ShadersListView.selectedIndex, -1,
+     "The shaders list has a negative index.");
+ 
+   reload(target);
+ 
+-  let [firstProgramActor, secondProgramActor] = yield promise.all([
++  let [firstProgramActor, secondProgramActor] = await promise.all([
+     getPrograms(gFront, 2, (actors) => {
+       // Fired upon each actor addition, we want to check only
+       // after the first actor has been added so we can test state
+       if (actors.length === 1)
+         checkFirstProgram();
+       if (actors.length === 2)
+         checkSecondProgram();
+     }),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]).then(([programs, ]) => programs);
+ 
+   is(ShadersListView.attachments[0].label, L10N.getFormatStr("shadersList.programLabel", 0),
+     "The correct first label is shown in the shaders list.");
+   is(ShadersListView.attachments[1].label, L10N.getFormatStr("shadersList.programLabel", 1),
+     "The correct second label is shown in the shaders list.");
+ 
+-  let vertexShader = yield firstProgramActor.getVertexShader();
+-  let fragmentShader = yield firstProgramActor.getFragmentShader();
+-  let vertSource = yield vertexShader.getText();
+-  let fragSource = yield fragmentShader.getText();
++  let vertexShader = await firstProgramActor.getVertexShader();
++  let fragmentShader = await firstProgramActor.getFragmentShader();
++  let vertSource = await vertexShader.getText();
++  let fragSource = await fragmentShader.getText();
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+   is(vertSource, vsEditor.getText(),
+     "The vertex shader editor contains the correct text.");
+   is(fragSource, fsEditor.getText(),
+     "The vertex shader editor contains the correct text.");
+ 
+   let compiled = once(panel.panelWin, EVENTS.SHADER_COMPILED).then(() => {
+     ok(false, "Selecting a different program shouldn't recompile its shaders.");
+   });
+ 
+   let shown = once(panel.panelWin, EVENTS.SOURCES_SHOWN).then(() => {
+     ok(true, "The vertex and fragment sources have changed in the editors.");
+   });
+ 
+   EventUtils.sendMouseEvent({ type: "mousedown" }, ShadersListView.items[1].target);
+-  yield shown;
++  await shown;
+ 
+   is(ShadersListView.selectedItem, ShadersListView.items[1],
+     "The shaders list has a correct item selected.");
+   is(ShadersListView.selectedIndex, 1,
+     "The shaders list has a correct index selected.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ 
+   function checkFirstProgram() {
+     is(ShadersListView.itemCount, 1,
+       "The shaders list contains one entry.");
+     is(ShadersListView.selectedItem, ShadersListView.items[0],
+       "The shaders list has a correct item selected.");
+     is(ShadersListView.selectedIndex, 0,
+diff --git a/devtools/client/shadereditor/test/browser_se_shaders-edit-01.js b/devtools/client/shadereditor/test/browser_se_shaders-edit-01.js
+--- a/devtools/client/shadereditor/test/browser_se_shaders-edit-01.js
++++ b/devtools/client/shadereditor/test/browser_se_shaders-edit-01.js
+@@ -1,73 +1,73 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if editing a vertex and a fragment shader works properly.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(SIMPLE_CANVAS_URL);
+   let { gFront, $, EVENTS, ShadersEditorsView } = panel.panelWin;
+ 
+   reload(target);
+-  yield promise.all([
++  await promise.all([
+     once(gFront, "program-linked"),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]);
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+   is(vsEditor.getText().indexOf("gl_Position"), 170,
+     "The vertex shader editor contains the correct text.");
+   is(fsEditor.getText().indexOf("gl_FragColor"), 97,
+     "The fragment shader editor contains the correct text.");
+ 
+   is($("#vs-editor-label").hasAttribute("selected"), false,
+     "The vertex shader editor shouldn't be initially selected.");
+   is($("#fs-editor-label").hasAttribute("selected"), false,
+     "The vertex shader editor shouldn't be initially selected.");
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 128, y: 128 }, { r: 191, g: 64, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 128, y: 128 }, { r: 191, g: 64, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+ 
+   vsEditor.focus();
+ 
+   is($("#vs-editor-label").hasAttribute("selected"), true,
+     "The vertex shader editor should now be selected.");
+   is($("#fs-editor-label").hasAttribute("selected"), false,
+     "The vertex shader editor shouldn't still not be selected.");
+ 
+   vsEditor.replaceText("2.0", { line: 7, ch: 44 }, { line: 7, ch: 47 });
+-  yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
++  await once(panel.panelWin, EVENTS.SHADER_COMPILED);
+ 
+   ok(true, "Vertex shader was changed.");
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 128, y: 128 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 128, y: 128 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+ 
+   ok(true, "The vertex shader was recompiled successfully.");
+ 
+   fsEditor.focus();
+ 
+   is($("#vs-editor-label").hasAttribute("selected"), false,
+     "The vertex shader editor should now be deselected.");
+   is($("#fs-editor-label").hasAttribute("selected"), true,
+     "The vertex shader editor should now be selected.");
+ 
+   fsEditor.replaceText("0.5", { line: 5, ch: 44 }, { line: 5, ch: 47 });
+-  yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
++  await once(panel.panelWin, EVENTS.SHADER_COMPILED);
+ 
+   ok(true, "Fragment shader was changed.");
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 128, y: 128 }, { r: 255, g: 0, b: 0, a: 127 }, true);
+-  yield ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 128, y: 128 }, { r: 255, g: 0, b: 0, a: 127 }, true);
++  await ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+ 
+   ok(true, "The fragment shader was recompiled successfully.");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_shaders-edit-02.js b/devtools/client/shadereditor/test/browser_se_shaders-edit-02.js
+--- a/devtools/client/shadereditor/test/browser_se_shaders-edit-02.js
++++ b/devtools/client/shadereditor/test/browser_se_shaders-edit-02.js
+@@ -1,32 +1,32 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if compile or linkage errors are emitted when a shader source
+  * gets malformed after being edited.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(SIMPLE_CANVAS_URL);
+   let { gFront, EVENTS, ShadersEditorsView } = panel.panelWin;
+ 
+   reload(target);
+-  yield promise.all([
++  await promise.all([
+     once(gFront, "program-linked"),
+     once(panel.panelWin, EVENTS.SOURCES_SHOWN)
+   ]);
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+ 
+   vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
+-  let [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
++  let [, error] = await onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
+ 
+   ok(error,
+     "The new vertex shader source was compiled with errors.");
+ 
+   // The implementation has the choice to defer all compile-time errors to link time.
+   let infoLog = (error.compile != "") ? error.compile : error.link;
+ 
+   isnot(infoLog, "",
+@@ -37,40 +37,40 @@ function* ifWebGLSupported() {
+     "A constructor error is contained in the info log.");
+   ok(infoLog.includes("ERROR: 0:8: '='"),
+     "A dimension error is contained in the info log.");
+   ok(infoLog.includes("ERROR: 0:8: 'assign'"),
+     "An assignment error is contained in the info log.");
+ 
+ 
+   fsEditor.replaceText("vec4", { line: 2, ch: 14 }, { line: 2, ch: 18 });
+-  [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
++  [, error] = await onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
+ 
+   ok(error,
+     "The new fragment shader source was compiled with errors.");
+ 
+   infoLog = (error.compile != "") ? error.compile : error.link;
+ 
+   isnot(infoLog, "",
+     "The one of the compile or link info logs should not be empty.");
+   is(infoLog.split("ERROR").length - 1, 1,
+     "The info log contains one error.");
+   ok(infoLog.includes("ERROR: 0:6: 'constructor'"),
+     "A constructor error is contained in the info log.");
+ 
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+ 
+   vsEditor.replaceText("vec4", { line: 7, ch: 22 }, { line: 7, ch: 26 });
+-  [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
++  [, error] = await onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
+   ok(!error, "The new vertex shader source was compiled successfully.");
+ 
+   fsEditor.replaceText("vec3", { line: 2, ch: 14 }, { line: 2, ch: 18 });
+-  [, error] = yield onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
++  [, error] = await onceSpread(panel.panelWin, EVENTS.SHADER_COMPILED);
+   ok(!error, "The new fragment shader source was compiled successfully.");
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(gFront, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_se_shaders-edit-03.js b/devtools/client/shadereditor/test/browser_se_shaders-edit-03.js
+--- a/devtools/client/shadereditor/test/browser_se_shaders-edit-03.js
++++ b/devtools/client/shadereditor/test/browser_se_shaders-edit-03.js
+@@ -1,85 +1,85 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if editing a vertex and a fragment shader would permanently store
+  * their new source on the backend and reshow it in the frontend when required.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, panel } = yield initShaderEditor(MULTIPLE_CONTEXTS_URL);
++async function ifWebGLSupported() {
++  let { target, panel } = await initShaderEditor(MULTIPLE_CONTEXTS_URL);
+   let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
+ 
+   reload(target);
+ 
+-  yield promise.all([
++  await promise.all([
+     once(gFront, "program-linked"),
+     once(gFront, "program-linked")
+   ]);
+ 
+-  yield once(panel.panelWin, EVENTS.SOURCES_SHOWN);
++  await once(panel.panelWin, EVENTS.SOURCES_SHOWN);
+ 
+-  let vsEditor = yield ShadersEditorsView._getEditor("vs");
+-  let fsEditor = yield ShadersEditorsView._getEditor("fs");
++  let vsEditor = await ShadersEditorsView._getEditor("vs");
++  let fsEditor = await ShadersEditorsView._getEditor("fs");
+ 
+   is(ShadersListView.selectedIndex, 0,
+     "The first program is currently selected.");
+   is(vsEditor.getText().indexOf("1);"), 136,
+     "The vertex shader editor contains the correct initial text (1).");
+   is(fsEditor.getText().indexOf("1);"), 117,
+     "The fragment shader editor contains the correct initial text (1).");
+   is(vsEditor.getText().indexOf("2.);"), -1,
+     "The vertex shader editor contains the correct initial text (2).");
+   is(fsEditor.getText().indexOf(".0);"), -1,
+     "The fragment shader editor contains the correct initial text (2).");
+ 
+   vsEditor.replaceText("2.", { line: 5, ch: 44 }, { line: 5, ch: 45 });
+-  yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
++  await once(panel.panelWin, EVENTS.SHADER_COMPILED);
+ 
+   fsEditor.replaceText(".0", { line: 5, ch: 35 }, { line: 5, ch: 37 });
+-  yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
++  await once(panel.panelWin, EVENTS.SHADER_COMPILED);
+ 
+   ok(true, "Vertex and fragment shaders were changed.");
+ 
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 32, y: 32 }, { r: 255, g: 255, b: 0, a: 0 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 255, g: 255, b: 0, a: 0 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 32, y: 32 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 32, y: 32 }, { r: 255, g: 255, b: 0, a: 0 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 255, g: 255, b: 0, a: 0 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(gFront, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 32, y: 32 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(gFront, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+ 
+   ok(true, "The vertex and fragment shaders were recompiled successfully.");
+ 
+   EventUtils.sendMouseEvent({ type: "mousedown" }, ShadersListView.items[1].target);
+-  yield once(panel.panelWin, EVENTS.SOURCES_SHOWN);
++  await once(panel.panelWin, EVENTS.SOURCES_SHOWN);
+ 
+   is(ShadersListView.selectedIndex, 1,
+     "The second program is currently selected.");
+   is(vsEditor.getText().indexOf("1);"), 136,
+     "The vertex shader editor contains the correct text (1).");
+   is(fsEditor.getText().indexOf("1);"), 117,
+     "The fragment shader editor contains the correct text (1).");
+   is(vsEditor.getText().indexOf("2.);"), -1,
+     "The vertex shader editor contains the correct text (2).");
+   is(fsEditor.getText().indexOf(".0);"), -1,
+     "The fragment shader editor contains the correct text (2).");
+ 
+   EventUtils.sendMouseEvent({ type: "mousedown" }, ShadersListView.items[0].target);
+-  yield once(panel.panelWin, EVENTS.SOURCES_SHOWN);
++  await once(panel.panelWin, EVENTS.SOURCES_SHOWN);
+ 
+   is(ShadersListView.selectedIndex, 0,
+     "The first program is currently selected again.");
+   is(vsEditor.getText().indexOf("1);"), -1,
+     "The vertex shader editor contains the correct text (3).");
+   is(fsEditor.getText().indexOf("1);"), -1,
+     "The fragment shader editor contains the correct text (3).");
+   is(vsEditor.getText().indexOf("2.);"), 136,
+     "The vertex shader editor contains the correct text (4).");
+   is(fsEditor.getText().indexOf(".0);"), 116,
+     "The fragment shader editor contains the correct text (4).");
+ 
+-  yield teardown(panel);
++  await teardown(panel);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-01.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-01.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-01.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-01.js
+@@ -1,16 +1,16 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if a WebGL front can be created for a remote tab target.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+ 
+   ok(target, "Should have a target available.");
+   ok(front, "Should have a protocol front available.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-02.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-02.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-02.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-02.js
+@@ -1,21 +1,21 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if notifications about WebGL programs being linked are not sent
+  * if the front wasn't set up first.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+ 
+   once(front, "program-linked").then(() => {
+     ok(false, "A 'program-linked' notification shouldn't have been sent!");
+   });
+ 
+   ok(true, "Each test requires at least one pass, fail or todo so here is a pass.");
+ 
+-  yield reload(target);
+-  yield removeTab(target.tab);
++  await reload(target);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-03.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-03.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-03.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-03.js
+@@ -1,26 +1,26 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if notifications about WebGL programs being linked are sent
+  * after a target navigation.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+ 
+   let navigated = once(target, "navigate");
+   let linked = once(front, "program-linked");
+ 
+-  yield front.setup({ reload: true });
++  await front.setup({ reload: true });
+   ok(true, "The front was setup up successfully.");
+ 
+-  yield navigated;
++  await navigated;
+   ok(true, "Target automatically navigated when the front was set up.");
+ 
+-  yield linked;
++  await linked;
+   ok(true, "A 'program-linked' notification was sent after reloading.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-04.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-04.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-04.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-04.js
+@@ -1,27 +1,27 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if a program actor is sent when WebGL programs are linked,
+  * and that the corresponding vertex and fragment actors can be retrieved.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+   front.setup({ reload: true });
+ 
+-  let programActor = yield once(front, "program-linked");
++  let programActor = await once(front, "program-linked");
+   ok(programActor,
+     "A program actor was sent along with the 'program-linked' notification.");
+ 
+-  let vertexShader = yield programActor.getVertexShader();
++  let vertexShader = await programActor.getVertexShader();
+   ok(programActor,
+     "A vertex shader actor was retrieved from the program actor.");
+ 
+-  let fragmentShader = yield programActor.getFragmentShader();
++  let fragmentShader = await programActor.getFragmentShader();
+   ok(programActor,
+     "A fragment shader actor was retrieved from the program actor.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-05.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-05.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-05.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-05.js
+@@ -1,27 +1,27 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the source contents can be retrieved from the vertex and fragment
+  * shader actors.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+   front.setup({ reload: true });
+ 
+-  let programActor = yield once(front, "program-linked");
+-  let vertexShader = yield programActor.getVertexShader();
+-  let fragmentShader = yield programActor.getFragmentShader();
++  let programActor = await once(front, "program-linked");
++  let vertexShader = await programActor.getVertexShader();
++  let fragmentShader = await programActor.getFragmentShader();
+ 
+-  let vertSource = yield vertexShader.getText();
++  let vertSource = await vertexShader.getText();
+   ok(vertSource.includes("gl_Position"),
+     "The correct vertex shader source was retrieved.");
+ 
+-  let fragSource = yield fragmentShader.getText();
++  let fragSource = await fragmentShader.getText();
+   ok(fragSource.includes("gl_FragColor"),
+     "The correct fragment shader source was retrieved.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-06.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-06.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-06.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-06.js
+@@ -1,64 +1,64 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the highlight/unhighlight and blackbox/unblackbox operations on
+  * program actors work as expected.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+   front.setup({ reload: true });
+ 
+-  let programActor = yield once(front, "program-linked");
+-  let vertexShader = yield programActor.getVertexShader();
+-  let fragmentShader = yield programActor.getFragmentShader();
++  let programActor = await once(front, "program-linked");
++  let vertexShader = await programActor.getVertexShader();
++  let fragmentShader = await programActor.getFragmentShader();
+ 
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+-  yield checkShaderSource("The shader sources are correct before highlighting.");
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await checkShaderSource("The shader sources are correct before highlighting.");
+   ok(true, "The corner pixel colors are correct before highlighting.");
+ 
+-  yield programActor.highlight([0, 1, 0, 1]);
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+-  yield checkShaderSource("The shader sources are preserved after highlighting.");
++  await programActor.highlight([0, 1, 0, 1]);
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await checkShaderSource("The shader sources are preserved after highlighting.");
+   ok(true, "The corner pixel colors are correct after highlighting.");
+ 
+-  yield programActor.unhighlight();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+-  yield checkShaderSource("The shader sources are correct after unhighlighting.");
++  await programActor.unhighlight();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await checkShaderSource("The shader sources are correct after unhighlighting.");
+   ok(true, "The corner pixel colors are correct after unhighlighting.");
+ 
+-  yield programActor.blackbox();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-  yield checkShaderSource("The shader sources are preserved after blackboxing.");
++  await programActor.blackbox();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await checkShaderSource("The shader sources are preserved after blackboxing.");
+   ok(true, "The corner pixel colors are correct after blackboxing.");
+ 
+-  yield programActor.unblackbox();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+-  yield checkShaderSource("The shader sources are correct after unblackboxing.");
++  await programActor.unblackbox();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await checkShaderSource("The shader sources are correct after unblackboxing.");
+   ok(true, "The corner pixel colors are correct after unblackboxing.");
+ 
+   function checkShaderSource(aMessage) {
+-    return Task.spawn(function* () {
+-      let newVertexShader = yield programActor.getVertexShader();
+-      let newFragmentShader = yield programActor.getFragmentShader();
++    return (async function () {
++      let newVertexShader = await programActor.getVertexShader();
++      let newFragmentShader = await programActor.getFragmentShader();
+       is(vertexShader, newVertexShader,
+         "The same vertex shader actor was retrieved.");
+       is(fragmentShader, newFragmentShader,
+         "The same fragment shader actor was retrieved.");
+ 
+-      let vertSource = yield newVertexShader.getText();
+-      let fragSource = yield newFragmentShader.getText();
++      let vertSource = await newVertexShader.getText();
++      let fragSource = await newFragmentShader.getText();
+       ok(vertSource.includes("I'm special!") &&
+          fragSource.includes("I'm also special!"), aMessage);
+-    });
++    })();
+   }
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-07.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-07.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-07.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-07.js
+@@ -1,61 +1,61 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that vertex and fragment shader sources can be changed.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+   front.setup({ reload: true });
+ 
+-  let programActor = yield once(front, "program-linked");
+-  let vertexShader = yield programActor.getVertexShader();
+-  let fragmentShader = yield programActor.getFragmentShader();
++  let programActor = await once(front, "program-linked");
++  let vertexShader = await programActor.getVertexShader();
++  let fragmentShader = await programActor.getFragmentShader();
+ 
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 128, y: 128 }, { r: 191, g: 64, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 128, y: 128 }, { r: 191, g: 64, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+ 
+-  let vertSource = yield vertexShader.getText();
+-  let fragSource = yield fragmentShader.getText();
++  let vertSource = await vertexShader.getText();
++  let fragSource = await fragmentShader.getText();
+   ok(!vertSource.includes("2.0"),
+     "The vertex shader source is correct before changing it.");
+   ok(!fragSource.includes("0.5"),
+     "The fragment shader source is correct before changing it.");
+ 
+   let newVertSource = vertSource.replace("1.0", "2.0");
+-  let status = yield vertexShader.compile(newVertSource);
++  let status = await vertexShader.compile(newVertSource);
+   ok(!status,
+     "The new vertex shader source was compiled without errors.");
+ 
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 128, y: 128 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 128, y: 128 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+ 
+-  vertSource = yield vertexShader.getText();
+-  fragSource = yield fragmentShader.getText();
++  vertSource = await vertexShader.getText();
++  fragSource = await fragmentShader.getText();
+   ok(vertSource.includes("2.0"),
+     "The vertex shader source is correct after changing it.");
+   ok(!fragSource.includes("0.5"),
+     "The fragment shader source is correct after changing the vertex shader.");
+ 
+   let newFragSource = fragSource.replace("1.0", "0.5");
+-  status = yield fragmentShader.compile(newFragSource);
++  status = await fragmentShader.compile(newFragSource);
+   ok(!status,
+     "The new fragment shader source was compiled without errors.");
+ 
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 128, y: 128 }, { r: 255, g: 0, b: 0, a: 127 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 128, y: 128 }, { r: 255, g: 0, b: 0, a: 127 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+ 
+-  vertSource = yield vertexShader.getText();
+-  fragSource = yield fragmentShader.getText();
++  vertSource = await vertexShader.getText();
++  fragSource = await fragmentShader.getText();
+   ok(vertSource.includes("2.0"),
+     "The vertex shader source is correct after changing the fragment shader.");
+   ok(fragSource.includes("0.5"),
+     "The fragment shader source is correct after changing it.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-08.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-08.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-08.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-08.js
+@@ -1,37 +1,37 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the rendering is updated when a varying variable is
+  * changed in one shader.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+   front.setup({ reload: true });
+ 
+-  let programActor = yield once(front, "program-linked");
+-  let vertexShader = yield programActor.getVertexShader();
+-  let fragmentShader = yield programActor.getFragmentShader();
++  let programActor = await once(front, "program-linked");
++  let vertexShader = await programActor.getVertexShader();
++  let fragmentShader = await programActor.getFragmentShader();
+ 
+-  let oldVertSource = yield vertexShader.getText();
++  let oldVertSource = await vertexShader.getText();
+   let newVertSource = oldVertSource.replace("= aVertexColor", "= vec3(0, 0, 1)");
+-  let status = yield vertexShader.compile(newVertSource);
++  let status = await vertexShader.compile(newVertSource);
+   ok(!status,
+     "The new vertex shader source was compiled without errors.");
+ 
+-  yield front.waitForFrame();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 128, y: 128 }, { r: 0, g: 0, b: 255, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 0, b: 255, a: 255 }, true);
++  await front.waitForFrame();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true);
++  await ensurePixelIs(front, { x: 128, y: 128 }, { r: 0, g: 0, b: 255, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 0, b: 255, a: 255 }, true);
+ 
+-  let vertSource = yield vertexShader.getText();
+-  let fragSource = yield fragmentShader.getText();
++  let vertSource = await vertexShader.getText();
++  let fragSource = await fragmentShader.getText();
+   ok(vertSource.includes("vFragmentColor = vec3(0, 0, 1);"),
+     "The vertex shader source is correct after changing it.");
+   ok(fragSource.includes("gl_FragColor = vec4(vFragmentColor, 1.0);"),
+     "The fragment shader source is correct after changing the vertex shader.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-09.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-09.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-09.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-09.js
+@@ -1,29 +1,29 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that errors are properly handled when trying to compile a
+  * defective shader source.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+   front.setup({ reload: true });
+ 
+-  let programActor = yield once(front, "program-linked");
+-  let vertexShader = yield programActor.getVertexShader();
+-  let fragmentShader = yield programActor.getFragmentShader();
++  let programActor = await once(front, "program-linked");
++  let vertexShader = await programActor.getVertexShader();
++  let fragmentShader = await programActor.getFragmentShader();
+ 
+-  let oldVertSource = yield vertexShader.getText();
++  let oldVertSource = await vertexShader.getText();
+   let newVertSource = oldVertSource.replace("vec4", "vec3");
+ 
+   try {
+-    yield vertexShader.compile(newVertSource);
++    await vertexShader.compile(newVertSource);
+     ok(false, "Vertex shader was compiled with a defective source!");
+   } catch (error) {
+     ok(error,
+       "The new vertex shader source was compiled with errors.");
+ 
+     // The implementation has the choice to defer all compile-time errors to link time.
+     let infoLog = (error.compile != "") ? error.compile : error.link;
+ 
+@@ -34,58 +34,58 @@ function* ifWebGLSupported() {
+     ok(infoLog.includes("ERROR: 0:8: 'constructor'"),
+       "A constructor error is contained in the info log.");
+     ok(infoLog.includes("ERROR: 0:8: '='"),
+       "A dimension error is contained in the info log.");
+     ok(infoLog.includes("ERROR: 0:8: 'assign'"),
+       "An assignment error is contained in the info log.");
+   }
+ 
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+   ok(true, "The shader was reverted to the old source.");
+ 
+-  let vertSource = yield vertexShader.getText();
++  let vertSource = await vertexShader.getText();
+   ok(vertSource.includes("vec4(aVertexPosition, 1.0);"),
+     "The previous correct vertex shader source was preserved.");
+ 
+-  let oldFragSource = yield fragmentShader.getText();
++  let oldFragSource = await fragmentShader.getText();
+   let newFragSource = oldFragSource.replace("vec3", "vec4");
+ 
+   try {
+-    yield fragmentShader.compile(newFragSource);
++    await fragmentShader.compile(newFragSource);
+     ok(false, "Fragment shader was compiled with a defective source!");
+   } catch (error) {
+     ok(error,
+       "The new fragment shader source was compiled with errors.");
+ 
+     // The implementation has the choice to defer all compile-time errors to link time.
+     let infoLog = (error.compile != "") ? error.compile : error.link;
+ 
+     isnot(infoLog, "",
+       "The one of the compile or link info logs should not be empty.");
+     is(infoLog.split("ERROR").length - 1, 1,
+       "The info log contains one error.");
+     ok(infoLog.includes("ERROR: 0:6: 'constructor'"),
+       "A constructor error is contained in the info log.");
+   }
+ 
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+   ok(true, "The shader was reverted to the old source.");
+ 
+-  let fragSource = yield fragmentShader.getText();
++  let fragSource = await fragmentShader.getText();
+   ok(fragSource.includes("vec3 vFragmentColor;"),
+     "The previous correct fragment shader source was preserved.");
+ 
+-  yield programActor.highlight([0, 1, 0, 1]);
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await programActor.highlight([0, 1, 0, 1]);
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+   ok(true, "Highlighting worked after setting a defective fragment source.");
+ 
+-  yield programActor.unhighlight();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++  await programActor.unhighlight();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+   ok(true, "Unhighlighting worked after setting a defective vertex source.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-10.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-10.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-10.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-10.js
+@@ -1,44 +1,44 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the WebGL context is correctly instrumented every time the
+  * target navigates.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+ 
+   front.setup({ reload: true });
+-  yield testHighlighting((yield once(front, "program-linked")));
++  await testHighlighting((await once(front, "program-linked")));
+   ok(true, "Canvas was correctly instrumented on the first navigation.");
+ 
+   reload(target);
+-  yield testHighlighting((yield once(front, "program-linked")));
++  await testHighlighting((await once(front, "program-linked")));
+   ok(true, "Canvas was correctly instrumented on the second navigation.");
+ 
+   reload(target);
+-  yield testHighlighting((yield once(front, "program-linked")));
++  await testHighlighting((await once(front, "program-linked")));
+   ok(true, "Canvas was correctly instrumented on the third navigation.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ 
+   function testHighlighting(programActor) {
+-    return Task.spawn(function* () {
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-      yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++    return (async function () {
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++      await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+       ok(true, "The corner pixel colors are correct before highlighting.");
+ 
+-      yield programActor.highlight([0, 1, 0, 1]);
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-      yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++      await programActor.highlight([0, 1, 0, 1]);
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++      await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+       ok(true, "The corner pixel colors are correct after highlighting.");
+ 
+-      yield programActor.unhighlight();
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-      yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++      await programActor.unhighlight();
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++      await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+       ok(true, "The corner pixel colors are correct after unhighlighting.");
+-    });
++    })();
+   }
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-11.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-11.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-11.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-11.js
+@@ -1,25 +1,25 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if the WebGL context is never instrumented anymore after the
+  * finalize method is called.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+ 
+   let linked = once(front, "program-linked");
+   front.setup({ reload: true });
+-  yield linked;
++  await linked;
+   ok(true, "Canvas was correctly instrumented on the first navigation.");
+ 
+   once(front, "program-linked").then(() => {
+     ok(false, "A 'program-linked' notification shouldn't have been sent!");
+   });
+ 
+-  yield front.finalize();
+-  yield reload(target);
+-  yield removeTab(target.tab);
++  await front.finalize();
++  await reload(target);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-12.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-12.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-12.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-12.js
+@@ -1,27 +1,27 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the correct vertex and fragment shader sources are retrieved
+  * regardless of the order in which they were compiled and attached.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SHADER_ORDER_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SHADER_ORDER_URL);
+   front.setup({ reload: true });
+ 
+-  let programActor = yield once(front, "program-linked");
+-  let vertexShader = yield programActor.getVertexShader();
+-  let fragmentShader = yield programActor.getFragmentShader();
++  let programActor = await once(front, "program-linked");
++  let vertexShader = await programActor.getVertexShader();
++  let fragmentShader = await programActor.getFragmentShader();
+ 
+-  let vertSource = yield vertexShader.getText();
+-  let fragSource = yield fragmentShader.getText();
++  let vertSource = await vertexShader.getText();
++  let fragSource = await fragmentShader.getText();
+ 
+   ok(vertSource.includes("I'm a vertex shader!"),
+     "The correct vertex shader text was retrieved.");
+   ok(fragSource.includes("I'm a fragment shader!"),
+     "The correct fragment shader text was retrieved.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-13.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-13.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-13.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-13.js
+@@ -1,67 +1,67 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if multiple WebGL contexts are correctly handled.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(MULTIPLE_CONTEXTS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(MULTIPLE_CONTEXTS_URL);
+   front.setup({ reload: true });
+ 
+-  let [firstProgramActor, secondProgramActor] = yield getPrograms(front, 2);
++  let [firstProgramActor, secondProgramActor] = await getPrograms(front, 2);
+ 
+   isnot(firstProgramActor, secondProgramActor,
+     "Two distinct program actors were recevide from two separate contexts.");
+ 
+-  let firstVertexShader = yield firstProgramActor.getVertexShader();
+-  let firstFragmentShader = yield firstProgramActor.getFragmentShader();
+-  let secondVertexShader = yield secondProgramActor.getVertexShader();
+-  let secondFragmentShader = yield secondProgramActor.getFragmentShader();
++  let firstVertexShader = await firstProgramActor.getVertexShader();
++  let firstFragmentShader = await firstProgramActor.getFragmentShader();
++  let secondVertexShader = await secondProgramActor.getVertexShader();
++  let secondFragmentShader = await secondProgramActor.getFragmentShader();
+ 
+   isnot(firstVertexShader, secondVertexShader,
+     "The two programs should have distinct vertex shaders.");
+   isnot(firstFragmentShader, secondFragmentShader,
+     "The two programs should have distinct fragment shaders.");
+ 
+-  let firstVertSource = yield firstVertexShader.getText();
+-  let firstFragSource = yield firstFragmentShader.getText();
+-  let secondVertSource = yield secondVertexShader.getText();
+-  let secondFragSource = yield secondFragmentShader.getText();
++  let firstVertSource = await firstVertexShader.getText();
++  let firstFragSource = await firstFragmentShader.getText();
++  let secondVertSource = await secondVertexShader.getText();
++  let secondFragSource = await secondFragmentShader.getText();
+ 
+   is(firstVertSource, secondVertSource,
+     "The vertex shaders should have identical sources.");
+   is(firstFragSource, secondFragSource,
+     "The vertex shaders should have identical sources.");
+ 
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The two canvases are correctly drawn.");
+ 
+-  yield firstProgramActor.highlight([1, 0, 0, 1]);
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await firstProgramActor.highlight([1, 0, 0, 1]);
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The first canvas was correctly filled after highlighting.");
+ 
+-  yield secondProgramActor.highlight([0, 1, 0, 1]);
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
++  await secondProgramActor.highlight([0, 1, 0, 1]);
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
+   ok(true, "The second canvas was correctly filled after highlighting.");
+ 
+-  yield firstProgramActor.unhighlight();
+-  yield secondProgramActor.unhighlight();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await firstProgramActor.unhighlight();
++  await secondProgramActor.unhighlight();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The two canvases were correctly filled after unhighlighting.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-14.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-14.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-14.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-14.js
+@@ -1,46 +1,46 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the rendering is updated when a uniform variable is
+  * changed in one shader of a page with multiple WebGL contexts.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(MULTIPLE_CONTEXTS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(MULTIPLE_CONTEXTS_URL);
+   front.setup({ reload: true });
+ 
+-  let [firstProgramActor, secondProgramActor] = yield getPrograms(front, 2);
++  let [firstProgramActor, secondProgramActor] = await getPrograms(front, 2);
+ 
+-  let firstFragmentShader = yield firstProgramActor.getFragmentShader();
+-  let secondFragmentShader = yield secondProgramActor.getFragmentShader();
++  let firstFragmentShader = await firstProgramActor.getFragmentShader();
++  let secondFragmentShader = await secondProgramActor.getFragmentShader();
+ 
+-  let oldFragSource = yield firstFragmentShader.getText();
++  let oldFragSource = await firstFragmentShader.getText();
+   let newFragSource = oldFragSource.replace("vec4(uColor", "vec4(0.25, 0.25, 0.25");
+-  let status = yield firstFragmentShader.compile(newFragSource);
++  let status = await firstFragmentShader.compile(newFragSource);
+   ok(!status,
+     "The first new fragment shader source was compiled without errors.");
+ 
+-  yield front.waitForFrame();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 64, g: 64, b: 64, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 64, g: 64, b: 64, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await front.waitForFrame();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 64, g: 64, b: 64, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 64, g: 64, b: 64, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+   ok(true, "The first fragment shader was changed.");
+ 
+-  oldFragSource = yield secondFragmentShader.getText();
++  oldFragSource = await secondFragmentShader.getText();
+   newFragSource = oldFragSource.replace("vec4(uColor", "vec4(0.75, 0.75, 0.75");
+-  status = yield secondFragmentShader.compile(newFragSource);
++  status = await secondFragmentShader.compile(newFragSource);
+   ok(!status,
+     "The second new fragment shader source was compiled without errors.");
+ 
+-  yield front.waitForFrame();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 64, g: 64, b: 64, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 64, g: 64, b: 64, a: 255 }, true, "#canvas1");
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 191, g: 191, b: 191, a: 255 }, true, "#canvas2");
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 191, g: 191, b: 191, a: 255 }, true, "#canvas2");
++  await front.waitForFrame();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 64, g: 64, b: 64, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 64, g: 64, b: 64, a: 255 }, true, "#canvas1");
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 191, g: 191, b: 191, a: 255 }, true, "#canvas2");
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 191, g: 191, b: 191, a: 255 }, true, "#canvas2");
+   ok(true, "The second fragment shader was changed.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-15.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-15.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-15.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-15.js
+@@ -1,133 +1,133 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if program actors are cached when navigating in the bfcache.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+   front.setup({ reload: false });
+ 
+   // Attach frame scripts if in e10s to perform
+   // history navigation via the content
+   loadFrameScripts();
+ 
+   reload(target);
+-  let firstProgram = yield once(front, "program-linked");
+-  yield checkFirstCachedPrograms(firstProgram);
+-  yield checkHighlightingInTheFirstPage(firstProgram);
++  let firstProgram = await once(front, "program-linked");
++  await checkFirstCachedPrograms(firstProgram);
++  await checkHighlightingInTheFirstPage(firstProgram);
+   ok(true, "The cached programs behave correctly before the navigation.");
+ 
+   navigate(target, MULTIPLE_CONTEXTS_URL);
+-  let [secondProgram, thirdProgram] = yield getPrograms(front, 2);
+-  yield checkSecondCachedPrograms(firstProgram, [secondProgram, thirdProgram]);
+-  yield checkHighlightingInTheSecondPage(secondProgram, thirdProgram);
++  let [secondProgram, thirdProgram] = await getPrograms(front, 2);
++  await checkSecondCachedPrograms(firstProgram, [secondProgram, thirdProgram]);
++  await checkHighlightingInTheSecondPage(secondProgram, thirdProgram);
+   ok(true, "The cached programs behave correctly after the navigation.");
+ 
+   once(front, "program-linked").then(() => {
+     ok(false, "Shouldn't have received any more program-linked notifications.");
+   });
+ 
+-  yield navigateInHistory(target, "back");
+-  yield checkFirstCachedPrograms(firstProgram);
+-  yield checkHighlightingInTheFirstPage(firstProgram);
++  await navigateInHistory(target, "back");
++  await checkFirstCachedPrograms(firstProgram);
++  await checkHighlightingInTheFirstPage(firstProgram);
+   ok(true, "The cached programs behave correctly after navigating back.");
+ 
+-  yield navigateInHistory(target, "forward");
+-  yield checkSecondCachedPrograms(firstProgram, [secondProgram, thirdProgram]);
+-  yield checkHighlightingInTheSecondPage(secondProgram, thirdProgram);
++  await navigateInHistory(target, "forward");
++  await checkSecondCachedPrograms(firstProgram, [secondProgram, thirdProgram]);
++  await checkHighlightingInTheSecondPage(secondProgram, thirdProgram);
+   ok(true, "The cached programs behave correctly after navigating forward.");
+ 
+-  yield navigateInHistory(target, "back");
+-  yield checkFirstCachedPrograms(firstProgram);
+-  yield checkHighlightingInTheFirstPage(firstProgram);
++  await navigateInHistory(target, "back");
++  await checkFirstCachedPrograms(firstProgram);
++  await checkHighlightingInTheFirstPage(firstProgram);
+   ok(true, "The cached programs behave correctly after navigating back again.");
+ 
+-  yield navigateInHistory(target, "forward");
+-  yield checkSecondCachedPrograms(firstProgram, [secondProgram, thirdProgram]);
+-  yield checkHighlightingInTheSecondPage(secondProgram, thirdProgram);
++  await navigateInHistory(target, "forward");
++  await checkSecondCachedPrograms(firstProgram, [secondProgram, thirdProgram]);
++  await checkHighlightingInTheSecondPage(secondProgram, thirdProgram);
+   ok(true, "The cached programs behave correctly after navigating forward again.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ 
+   function checkFirstCachedPrograms(programActor) {
+-    return Task.spawn(function* () {
+-      let programs = yield front.getPrograms();
++    return (async function () {
++      let programs = await front.getPrograms();
+ 
+       is(programs.length, 1,
+         "There should be 1 cached program actor.");
+       is(programs[0], programActor,
+         "The cached program actor was the expected one.");
+-    });
++    })();
+   }
+ 
+   function checkSecondCachedPrograms(oldProgramActor, newProgramActors) {
+-    return Task.spawn(function* () {
+-      let programs = yield front.getPrograms();
++    return (async function () {
++      let programs = await front.getPrograms();
+ 
+       is(programs.length, 2,
+         "There should be 2 cached program actors after the navigation.");
+       is(programs[0], newProgramActors[0],
+         "The first cached program actor was the expected one after the navigation.");
+       is(programs[1], newProgramActors[1],
+         "The second cached program actor was the expected one after the navigation.");
+ 
+       isnot(newProgramActors[0], oldProgramActor,
+         "The old program actor is not equal to the new first program actor.");
+       isnot(newProgramActors[1], oldProgramActor,
+         "The old program actor is not equal to the new second program actor.");
+-    });
++    })();
+   }
+ 
+   function checkHighlightingInTheFirstPage(programActor) {
+-    return Task.spawn(function* () {
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-      yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++    return (async function () {
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++      await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+       ok(true, "The corner pixel colors are correct before highlighting.");
+ 
+-      yield programActor.highlight([0, 1, 0, 1]);
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-      yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++      await programActor.highlight([0, 1, 0, 1]);
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++      await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+       ok(true, "The corner pixel colors are correct after highlighting.");
+ 
+-      yield programActor.unhighlight();
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-      yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++      await programActor.unhighlight();
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++      await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+       ok(true, "The corner pixel colors are correct after unhighlighting.");
+-    });
++    })();
+   }
+ 
+   function checkHighlightingInTheSecondPage(firstProgramActor, secondProgramActor) {
+-    return Task.spawn(function* () {
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++    return (async function () {
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+       ok(true, "The two canvases are correctly drawn before highlighting.");
+ 
+-      yield firstProgramActor.highlight([1, 0, 0, 1]);
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++      await firstProgramActor.highlight([1, 0, 0, 1]);
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+       ok(true, "The first canvas was correctly filled after highlighting.");
+ 
+-      yield secondProgramActor.highlight([0, 1, 0, 1]);
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
++      await secondProgramActor.highlight([0, 1, 0, 1]);
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
+       ok(true, "The second canvas was correctly filled after highlighting.");
+ 
+-      yield firstProgramActor.unhighlight();
+-      yield secondProgramActor.unhighlight();
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++      await firstProgramActor.unhighlight();
++      await secondProgramActor.unhighlight();
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+       ok(true, "The two canvases were correctly filled after unhighlighting.");
+-    });
++    })();
+   }
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-16.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-16.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-16.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-16.js
+@@ -1,141 +1,141 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests if program actors are invalidated from the cache when a window is
+  * removed from the bfcache.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(SIMPLE_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(SIMPLE_CANVAS_URL);
+   front.setup({ reload: false });
+ 
+   // Attach frame scripts if in e10s to perform
+   // history navigation via the content
+   loadFrameScripts();
+ 
+   // 0. Perform the initial reload.
+ 
+   reload(target);
+-  let firstProgram = yield once(front, "program-linked");
+-  let programs = yield front.getPrograms();
++  let firstProgram = await once(front, "program-linked");
++  let programs = await front.getPrograms();
+   is(programs.length, 1,
+     "The first program should be returned by a call to getPrograms().");
+   is(programs[0], firstProgram,
+     "The first programs was correctly retrieved from the cache.");
+ 
+-  let allPrograms = yield front._getAllPrograms();
++  let allPrograms = await front._getAllPrograms();
+   is(allPrograms.length, 1,
+     "Should be only one program in cache.");
+ 
+   // 1. Perform a simple navigation.
+ 
+   navigate(target, MULTIPLE_CONTEXTS_URL);
+-  let [secondProgram, thirdProgram] = yield getPrograms(front, 2);
+-  programs = yield front.getPrograms();
++  let [secondProgram, thirdProgram] = await getPrograms(front, 2);
++  programs = await front.getPrograms();
+   is(programs.length, 2,
+     "The second and third programs should be returned by a call to getPrograms().");
+   is(programs[0], secondProgram,
+     "The second programs was correctly retrieved from the cache.");
+   is(programs[1], thirdProgram,
+     "The third programs was correctly retrieved from the cache.");
+ 
+-  allPrograms = yield front._getAllPrograms();
++  allPrograms = await front._getAllPrograms();
+   is(allPrograms.length, 3,
+     "Should be three programs in cache.");
+ 
+   // 2. Perform a bfcache navigation.
+ 
+-  yield navigateInHistory(target, "back");
++  await navigateInHistory(target, "back");
+   let globalDestroyed = once(front, "global-created");
+   let globalCreated = once(front, "global-destroyed");
+   let programsLinked = once(front, "program-linked");
+   reload(target);
+ 
+-  yield promise.all([programsLinked, globalDestroyed, globalCreated]);
+-  allPrograms = yield front._getAllPrograms();
++  await promise.all([programsLinked, globalDestroyed, globalCreated]);
++  allPrograms = await front._getAllPrograms();
+   is(allPrograms.length, 3,
+     "Should be 3 programs total in cache.");
+ 
+-  programs = yield front.getPrograms();
++  programs = await front.getPrograms();
+   is(programs.length, 1,
+     "There should be 1 cached program actor now.");
+ 
+-  yield checkHighlightingInTheFirstPage(programs[0]);
++  await checkHighlightingInTheFirstPage(programs[0]);
+   ok(true, "The cached programs behave correctly after navigating back and reloading.");
+ 
+   // 3. Perform a bfcache navigation and a page reload.
+ 
+-  yield navigateInHistory(target, "forward");
++  await navigateInHistory(target, "forward");
+ 
+   globalDestroyed = once(front, "global-created");
+   globalCreated = once(front, "global-destroyed");
+   programsLinked = getPrograms(front, 2);
+ 
+   reload(target);
+ 
+-  yield promise.all([programsLinked, globalDestroyed, globalCreated]);
+-  allPrograms = yield front._getAllPrograms();
++  await promise.all([programsLinked, globalDestroyed, globalCreated]);
++  allPrograms = await front._getAllPrograms();
+   is(allPrograms.length, 3,
+     "Should be 3 programs total in cache.");
+ 
+-  programs = yield front.getPrograms();
++  programs = await front.getPrograms();
+   is(programs.length, 2,
+     "There should be 2 cached program actors now.");
+ 
+-  yield checkHighlightingInTheSecondPage(programs[0], programs[1]);
++  await checkHighlightingInTheSecondPage(programs[0], programs[1]);
+   ok(true, "The cached programs behave correctly after navigating forward and reloading.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ 
+   function checkHighlightingInTheFirstPage(programActor) {
+-    return Task.spawn(function* () {
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-      yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++    return (async function () {
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++      await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+       ok(true, "The corner pixel colors are correct before highlighting.");
+ 
+-      yield programActor.highlight([0, 1, 0, 1]);
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-      yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++      await programActor.highlight([0, 1, 0, 1]);
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++      await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+       ok(true, "The corner pixel colors are correct after highlighting.");
+ 
+-      yield programActor.unhighlight();
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
+-      yield ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
++      await programActor.unhighlight();
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
++      await ensurePixelIs(front, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
+       ok(true, "The corner pixel colors are correct after unhighlighting.");
+-    });
++    })();
+   }
+ 
+   function checkHighlightingInTheSecondPage(firstProgramActor, secondProgramActor) {
+-    return Task.spawn(function* () {
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++    return (async function () {
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+       ok(true, "The two canvases are correctly drawn before highlighting.");
+ 
+-      yield firstProgramActor.highlight([1, 0, 0, 1]);
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++      await firstProgramActor.highlight([1, 0, 0, 1]);
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+       ok(true, "The first canvas was correctly filled after highlighting.");
+ 
+-      yield secondProgramActor.highlight([0, 1, 0, 1]);
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
++      await secondProgramActor.highlight([0, 1, 0, 1]);
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 0, a: 255 }, true, "#canvas2");
+       ok(true, "The second canvas was correctly filled after highlighting.");
+ 
+-      yield firstProgramActor.unhighlight();
+-      yield secondProgramActor.unhighlight();
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
+-      yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++      await firstProgramActor.unhighlight();
++      await secondProgramActor.unhighlight();
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
++      await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
+       ok(true, "The two canvases were correctly filled after unhighlighting.");
+-    });
++    })();
+   }
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-17.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-17.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-17.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-17.js
+@@ -1,46 +1,46 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the blackbox/unblackbox operations work as expected with
+  * overlapping geometry.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(OVERLAPPING_GEOMETRY_CANVAS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(OVERLAPPING_GEOMETRY_CANVAS_URL);
+   front.setup({ reload: true });
+ 
+-  let [firstProgramActor, secondProgramActor] = yield getPrograms(front, 2);
++  let [firstProgramActor, secondProgramActor] = await getPrograms(front, 2);
+ 
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+   ok(true, "The corner vs. center pixel colors are correct before blackboxing.");
+ 
+-  yield firstProgramActor.blackbox();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await firstProgramActor.blackbox();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true);
+   ok(true, "The corner vs. center pixel colors are correct after blackboxing (1).");
+ 
+-  yield firstProgramActor.unblackbox();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
++  await firstProgramActor.unblackbox();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+   ok(true, "The corner vs. center pixel colors are correct after unblackboxing (1).");
+ 
+-  yield secondProgramActor.blackbox();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 64, y: 64 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
++  await secondProgramActor.blackbox();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 64, y: 64 }, { r: 255, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+   ok(true, "The corner vs. center pixel colors are correct after blackboxing (2).");
+ 
+-  yield secondProgramActor.unblackbox();
+-  yield ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
+-  yield ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
++  await secondProgramActor.unblackbox();
++  await ensurePixelIs(front, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
++  await ensurePixelIs(front, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
++  await ensurePixelIs(front, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
+   ok(true, "The corner vs. center pixel colors are correct after unblackboxing (2).");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/browser_webgl-actor-test-18.js b/devtools/client/shadereditor/test/browser_webgl-actor-test-18.js
+--- a/devtools/client/shadereditor/test/browser_webgl-actor-test-18.js
++++ b/devtools/client/shadereditor/test/browser_webgl-actor-test-18.js
+@@ -1,31 +1,31 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests the `getPixel` actor method works across threads.
+  */
+ 
+-function* ifWebGLSupported() {
+-  let { target, front } = yield initBackend(MULTIPLE_CONTEXTS_URL);
++async function ifWebGLSupported() {
++  let { target, front } = await initBackend(MULTIPLE_CONTEXTS_URL);
+   front.setup({ reload: true });
+ 
+-  yield getPrograms(front, 2);
++  await getPrograms(front, 2);
+ 
+   // Wait a frame to ensure rendering
+-  yield front.waitForFrame();
++  await front.waitForFrame();
+ 
+-  let pixel = yield front.getPixel({ selector: "#canvas1", position: { x: 0, y: 0 }});
++  let pixel = await front.getPixel({ selector: "#canvas1", position: { x: 0, y: 0 }});
+   is(pixel.r, 255, "correct `r` value for first canvas.");
+   is(pixel.g, 255, "correct `g` value for first canvas.");
+   is(pixel.b, 0, "correct `b` value for first canvas.");
+   is(pixel.a, 255, "correct `a` value for first canvas.");
+ 
+-  pixel = yield front.getPixel({ selector: "#canvas2", position: { x: 0, y: 0 }});
++  pixel = await front.getPixel({ selector: "#canvas2", position: { x: 0, y: 0 }});
+   is(pixel.r, 0, "correct `r` value for second canvas.");
+   is(pixel.g, 255, "correct `g` value for second canvas.");
+   is(pixel.b, 255, "correct `b` value for second canvas.");
+   is(pixel.a, 255, "correct `a` value for second canvas.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+   finish();
+ }
+diff --git a/devtools/client/shadereditor/test/head.js b/devtools/client/shadereditor/test/head.js
+--- a/devtools/client/shadereditor/test/head.js
++++ b/devtools/client/shadereditor/test/head.js
+@@ -63,19 +63,23 @@ function ifWebGLSupported() {
+   finish();
+ }
+ 
+ function ifWebGLUnsupported() {
+   todo(false, "Skipping test because WebGL isn't supported.");
+   finish();
+ }
+ 
+-function test() {
++async function test() {
+   let generator = isWebGLSupported(document) ? ifWebGLSupported : ifWebGLUnsupported;
+-  Task.spawn(generator).catch(handleError);
++  try {
++    await generator();
++  } catch(e) {
++    handlError(e);
++  }
+ }
+ 
+ function createCanvas() {
+   return document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ }
+ 
+ // Hack around `once`, as that only resolves to a single (first) argument
+ // and discards the rest. `onceSpread` is similar, except resolves to an
+@@ -107,31 +111,31 @@ function isApprox(aFirst, aSecond, aMarg
+ function isApproxColor(aFirst, aSecond, aMargin) {
+   return isApprox(aFirst.r, aSecond.r, aMargin) &&
+     isApprox(aFirst.g, aSecond.g, aMargin) &&
+     isApprox(aFirst.b, aSecond.b, aMargin) &&
+     isApprox(aFirst.a, aSecond.a, aMargin);
+ }
+ 
+ function ensurePixelIs(aFront, aPosition, aColor, aWaitFlag = false, aSelector = "canvas") {
+-  return Task.spawn(function* () {
+-    let pixel = yield aFront.getPixel({ selector: aSelector, position: aPosition });
++  return (async function () {
++    let pixel = await aFront.getPixel({ selector: aSelector, position: aPosition });
+     if (isApproxColor(pixel, aColor)) {
+       ok(true, "Expected pixel is shown at: " + aPosition.toSource());
+       return;
+     }
+ 
+     if (aWaitFlag) {
+-      yield aFront.waitForFrame();
++      await aFront.waitForFrame();
+       return ensurePixelIs(aFront, aPosition, aColor, aWaitFlag, aSelector);
+     }
+ 
+     ok(false, "Expected pixel was not already shown at: " + aPosition.toSource());
+     throw new Error("Expected pixel was not already shown at: " + aPosition.toSource());
+-  });
++  })();
+ }
+ 
+ function navigateInHistory(aTarget, aDirection, aWaitForTargetEvent = "navigate") {
+   if (Cu.isCrossProcessWrapper(content)) {
+     let mm = gBrowser.selectedBrowser.messageManager;
+     mm.sendAsyncMessage("devtools:test:history", { direction: aDirection });
+   } else {
+     executeSoon(() => content.history[aDirection]());
+@@ -150,41 +154,41 @@ function reload(aTarget, aWaitForTargetE
+ }
+ 
+ function initBackend(aUrl) {
+   info("Initializing a shader editor front.");
+ 
+   DebuggerServer.init();
+   DebuggerServer.registerAllActors();
+ 
+-  return Task.spawn(function* () {
+-    let tab = yield addTab(aUrl);
++  return (async function () {
++    let tab = await addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+-    yield target.makeRemote();
++    await target.makeRemote();
+ 
+     let front = new WebGLFront(target.client, target.form);
+     return { target, front };
+-  });
++  })();
+ }
+ 
+ function initShaderEditor(aUrl) {
+   info("Initializing a shader editor pane.");
+ 
+-  return Task.spawn(function* () {
+-    let tab = yield addTab(aUrl);
++  return (async function () {
++    let tab = await addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+-    yield target.makeRemote();
++    await target.makeRemote();
+ 
+     Services.prefs.setBoolPref("devtools.shadereditor.enabled", true);
+-    let toolbox = yield gDevTools.showToolbox(target, "shadereditor");
++    let toolbox = await gDevTools.showToolbox(target, "shadereditor");
+     let panel = toolbox.getCurrentPanel();
+     return { target, panel };
+-  });
++  })();
+ }
+ 
+ function teardown(aPanel) {
+   info("Destroying the specified shader editor.");
+ 
+   return promise.all([
+     once(aPanel, "destroyed"),
+     removeTab(aPanel.target.tab)

+ 10544 - 0
frg/work-js/mozilla-release/patches/1440321-1m-shared-61a1.patch

@@ -0,0 +1,10544 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  a721416b729557b183fe2deb90a41e69b16d650c
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1m. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/shared/components/test/browser/browser_notification_box_basic.js b/devtools/client/shared/components/test/browser/browser_notification_box_basic.js
+--- a/devtools/client/shared/components/test/browser/browser_notification_box_basic.js
++++ b/devtools/client/shared/components/test/browser/browser_notification_box_basic.js
+@@ -10,20 +10,20 @@
+ Services.scriptloader.loadSubScript(
+   "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", this);
+ 
+ const TEST_URI = "data:text/html;charset=utf-8,Test page";
+ 
+ /**
+  * Basic test that checks existence of the Notification box.
+  */
+-add_task(function* () {
++add_task(async function() {
+   info("Test Notification box basic started");
+ 
+-  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
++  let toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
+ 
+   // Append a notification
+   let notificationBox = toolbox.getNotificationBox();
+   notificationBox.appendNotification(
+     "Info message",
+     "id1",
+     null,
+     notificationBox.PRIORITY_INFO_HIGH
+diff --git a/devtools/client/shared/components/test/mochitest/head.js b/devtools/client/shared/components/test/mochitest/head.js
+--- a/devtools/client/shared/components/test/mochitest/head.js
++++ b/devtools/client/shared/components/test/mochitest/head.js
+@@ -11,17 +11,16 @@ var { gDevTools } = require("devtools/cl
+ var { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
+ var promise = require("promise");
+ var defer = require("devtools/shared/defer");
+ var Services = require("Services");
+ var { DebuggerServer } = require("devtools/server/main");
+ var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ var flags = require("devtools/shared/flags");
+-var { Task } = require("devtools/shared/task");
+ var { TargetFactory } = require("devtools/client/framework/target");
+ var { Toolbox } = require("devtools/client/framework/toolbox");
+ 
+ flags.testing = true;
+ var { require: browserRequire } = BrowserLoader({
+   baseURI: "resource://devtools/client/shared/",
+   window
+ });
+diff --git a/devtools/client/shared/components/test/mochitest/test_HSplitBox_01.html b/devtools/client/shared/components/test/mochitest/test_HSplitBox_01.html
+--- a/devtools/client/shared/components/test/mochitest/test_HSplitBox_01.html
++++ b/devtools/client/shared/components/test/mochitest/test_HSplitBox_01.html
+@@ -25,17 +25,17 @@ Basic tests for the HSplitBox component.
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+ const FUDGE_FACTOR = .1;
+ function aboutEq(a, b) {
+   dumpn(`Checking ${a} ~= ${b}`);
+   return Math.abs(a - b) < FUDGE_FACTOR;
+ }
+ 
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     const React = browserRequire("devtools/client/shared/vendor/react");
+     const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ 
+     let HSplitBox = React.createFactory(browserRequire("devtools/client/shared/components/HSplitBox"));
+     ok(HSplitBox, "Should get HSplitBox");
+ 
+     const newSizes = [];
+@@ -120,13 +120,13 @@ window.onload = Task.async(function* () 
+     synthesizeMouse(container, middle, top, { type: "mousemove" }, window);
+     is(newSizes.length, 4, "Should still have 4 resizes");
+ 
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_frame_01.html b/devtools/client/shared/components/test/mochitest/test_frame_01.html
+--- a/devtools/client/shared/components/test/mochitest/test_frame_01.html
++++ b/devtools/client/shared/components/test/mochitest/test_frame_01.html
+@@ -12,274 +12,274 @@ with optional columns, unknown and non-U
+   <title>Frame component test</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     let React = browserRequire("devtools/client/shared/vendor/react");
+     let Frame = React.createFactory(browserRequire("devtools/client/shared/components/Frame"));
+     ok(Frame, "Should get Frame");
+ 
+     // Check when there's a column
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://myfile.com/mahscripts.js",
+         line: 55,
+         column: 10,
+       }
+     }, {
+       file: "mahscripts.js",
+       line: 55,
+       column: 10,
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js:55:10",
+     });
+ 
+     // Check when there's no column
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://myfile.com/mahscripts.js",
+         line: 55,
+       }
+     }, {
+       file: "mahscripts.js",
+       line: 55,
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js:55",
+     });
+ 
+     // Check when column === 0
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://myfile.com/mahscripts.js",
+         line: 55,
+         column: 0,
+       }
+     }, {
+       file: "mahscripts.js",
+       line: 55,
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js:55",
+     });
+ 
+     // Check when there's no parseable URL source;
+     // should not link but should render line/columns
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "self-hosted",
+         line: 1,
+       }
+     }, {
+       file: "self-hosted",
+       line: "1",
+       shouldLink: false,
+       tooltip: "self-hosted:1",
+     });
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "self-hosted",
+         line: 1,
+         column: 10,
+       }
+     }, {
+       file: "self-hosted",
+       line: "1",
+       column: "10",
+       shouldLink: false,
+       tooltip: "self-hosted:1:10",
+     });
+ 
+     // Check when there's no source;
+     // should not link but should render line/columns
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         line: 1,
+       }
+     }, {
+       file: "(unknown)",
+       line: "1",
+       shouldLink: false,
+       tooltip: "(unknown):1",
+     });
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         line: 1,
+         column: 10,
+       }
+     }, {
+       file: "(unknown)",
+       line: "1",
+       column: "10",
+       shouldLink: false,
+       tooltip: "(unknown):1:10",
+     });
+ 
+     // Check when there's a column, but no line;
+     // no line/column info should render
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://myfile.com/mahscripts.js",
+         column: 55,
+       }
+     }, {
+       file: "mahscripts.js",
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js",
+     });
+ 
+     // Check when line is 0; this should be an invalid
+     // line option, so don't render line/column
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://myfile.com/mahscripts.js",
+         line: 0,
+         column: 55,
+       }
+     }, {
+       file: "mahscripts.js",
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js",
+     });
+ 
+     // Check when source is via Scratchpad; we should render out the
+     // lines and columns as this is linkable.
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "Scratchpad/1",
+         line: 10,
+         column: 50,
+       }
+     }, {
+       file: "Scratchpad/1",
+       line: 10,
+       column: 50,
+       shouldLink: true,
+       tooltip: "View source in Debugger → Scratchpad/1:10:50",
+     });
+ 
+     // Check that line and column can be strings
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://myfile.com/mahscripts.js",
+         line: "10",
+         column: "55",
+       }
+     }, {
+       file: "mahscripts.js",
+       line: 10,
+       column: 55,
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js:10:55",
+     });
+ 
+     // Check that line and column can be strings,
+     // and that the `0` rendering rules apply when they are strings as well
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://myfile.com/mahscripts.js",
+         line: "0",
+         column: "55",
+       }
+     }, {
+       file: "mahscripts.js",
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js",
+     });
+ 
+     // Check that the showFullSourceUrl option works correctly
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://myfile.com/mahscripts.js",
+         line: 0,
+       },
+       showFullSourceUrl: true
+     }, {
+       file: "http://myfile.com/mahscripts.js",
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js",
+     });
+ 
+     // Check that the showFunctionName option works correctly
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         functionDisplayName: "myfun",
+         source: "http://myfile.com/mahscripts.js",
+         line: 0,
+       }
+     }, {
+       functionName: null,
+       file: "mahscripts.js",
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js",
+     });
+ 
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         functionDisplayName: "myfun",
+         source: "http://myfile.com/mahscripts.js",
+         line: 0,
+       },
+       showFunctionName: true
+     }, {
+       functionName: "myfun",
+       file: "mahscripts.js",
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js",
+     });
+ 
+     // Check that anonymous function name is not displayed unless explicitly enabled
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://myfile.com/mahscripts.js",
+         line: 0,
+       },
+       showFunctionName: true
+     }, {
+       functionName: null,
+       file: "mahscripts.js",
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js",
+     });
+ 
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://myfile.com/mahscripts.js",
+         line: 0,
+       },
+       showFunctionName: true,
+       showAnonymousFunctionName: true
+     }, {
+       functionName: "<anonymous>",
+       file: "mahscripts.js",
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js",
+     });
+ 
+     // Check if file is rendered with "/" for root documents when showEmptyPathAsHost is false
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://www.cnn.com/",
+         line: "1",
+       },
+       showEmptyPathAsHost: false,
+     }, {
+       file: "/",
+       line: "1",
+       shouldLink: true,
+       tooltip: "View source in Debugger → http://www.cnn.com/:1",
+     });
+ 
+     // Check if file is rendered with hostname for root documents when showEmptyPathAsHost is true
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+         source: "http://www.cnn.com/",
+         line: "1",
+       },
+       showEmptyPathAsHost: true,
+     }, {
+       file: "www.cnn.com",
+       line: "1",
+@@ -295,41 +295,41 @@ window.onload = Task.async(function* () 
+     let mockSourceMapService = {
+       subscribe: function (url, line, column, callback) {
+ 	// Resolve immediately.
+         callback(true, resolvedLocation.sourceUrl, resolvedLocation.line);
+       },
+       unsubscribe: function (url, line, column, callback) {
+       },
+     };
+-    yield checkFrameComponent({
++    await checkFrameComponent({
+       frame: {
+ 	line: 97,
+ 	source: "https://bugzilla.mozilla.org/bundle.js",
+       },
+       sourceMapService: mockSourceMapService,
+     }, {
+       file: "original.js",
+       line: resolvedLocation.line,
+       shouldLink: true,
+       tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:23",
+       source: "https://bugzilla.mozilla.org/original.js",
+     });
+ 
+-    function* checkFrameComponent(input, expected) {
++    function checkFrameComponent(input, expected) {
+       let props = Object.assign({ onClick: () => {} }, input);
+       let frame = ReactDOM.render(Frame(props), window.document.body);
+       let el = ReactDOM.findDOMNode(frame);
+       let { source } = input.frame;
+       checkFrameString(Object.assign({ el, source }, expected));
+       ReactDOM.unmountComponentAtNode(window.document.body);
+     }
+ 
+   } catch (e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_frame_02.html b/devtools/client/shared/components/test/mochitest/test_frame_02.html
+--- a/devtools/client/shared/components/test/mochitest/test_frame_02.html
++++ b/devtools/client/shared/components/test/mochitest/test_frame_02.html
+@@ -11,17 +11,17 @@ Test that the frame component reacts to 
+   <title>Frame component source-map test</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     const Services = browserRequire("Services");
+     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     let React = browserRequire("devtools/client/shared/vendor/react");
+     let Frame = React.createFactory(browserRequire("devtools/client/shared/components/Frame"));
+ 
+     const resolvedLocation = {
+       sourceId: "whatever",
+@@ -85,13 +85,13 @@ window.onload = Task.async(function* () 
+     checkFrameString(Object.assign({ el, source }, expectedGenerated));
+ 
+     Services.prefs.clearUserPref(PREF);
+   } catch (e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_notification_box_01.html b/devtools/client/shared/components/test/mochitest/test_notification_box_01.html
+--- a/devtools/client/shared/components/test/mochitest/test_notification_box_01.html
++++ b/devtools/client/shared/components/test/mochitest/test_notification_box_01.html
+@@ -15,17 +15,17 @@ Test for Notification Box. The test is c
+   <title>Notification Box</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     let React = browserRequire("devtools/client/shared/vendor/react");
+     let { NotificationBox, PriorityLevels } = browserRequire("devtools/client/shared/components/NotificationBox");
+ 
+     const renderedBox = shallowRenderComponent(NotificationBox, {});
+     is(renderedBox.type, "div", "NotificationBox is rendered as <div>");
+ 
+@@ -90,17 +90,17 @@ window.onload = Task.async(function* () 
+ 
+     notificationBox.getNotificationWithValue("id3").close();
+     checkNumberOfNotifications(notificationBox, 0);
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ 
+ function checkNumberOfNotifications(notificationBox, expected) {
+   is(TestUtils.scryRenderedDOMComponentsWithClass(
+     notificationBox, "notification").length, expected,
+     "The notification box must have expected number of notifications");
+ }
+ </script>
+ </pre>
+diff --git a/devtools/client/shared/components/test/mochitest/test_notification_box_02.html b/devtools/client/shared/components/test/mochitest/test_notification_box_02.html
+--- a/devtools/client/shared/components/test/mochitest/test_notification_box_02.html
++++ b/devtools/client/shared/components/test/mochitest/test_notification_box_02.html
+@@ -12,17 +12,17 @@ Test for Notification Box. The test is c
+   <title>Notification Box</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     let React = browserRequire("devtools/client/shared/vendor/react");
+     let { NotificationBox, PriorityLevels } = browserRequire("devtools/client/shared/components/NotificationBox");
+ 
+     // Test rendering
+     let boxElement = React.createElement(NotificationBox);
+     let notificationBox = TestUtils.renderIntoDocument(boxElement);
+@@ -58,13 +58,13 @@ window.onload = Task.async(function* () 
+       "The notification box must be empty now");
+ 
+     ok(callbackExecuted, "Event callback must be executed.");
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_notification_box_03.html b/devtools/client/shared/components/test/mochitest/test_notification_box_03.html
+--- a/devtools/client/shared/components/test/mochitest/test_notification_box_03.html
++++ b/devtools/client/shared/components/test/mochitest/test_notification_box_03.html
+@@ -12,17 +12,17 @@ Test for Notification Box. The test is c
+   <title>Notification Box</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     let React = browserRequire("devtools/client/shared/vendor/react");
+     let { NotificationBox, PriorityLevels } = browserRequire("devtools/client/shared/components/NotificationBox");
+ 
+     // Test rendering
+     let boxElement = React.createElement(NotificationBox);
+     let notificationBox = TestUtils.renderIntoDocument(boxElement);
+@@ -72,13 +72,13 @@ window.onload = Task.async(function* () 
+     is(TestUtils.scryRenderedDOMComponentsWithClass(
+       notificationBox, "notification").length, 0,
+       "The notification box must be empty now");
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_sidebar_toggle.html b/devtools/client/shared/components/test/mochitest/test_sidebar_toggle.html
+--- a/devtools/client/shared/components/test/mochitest/test_sidebar_toggle.html
++++ b/devtools/client/shared/components/test/mochitest/test_sidebar_toggle.html
+@@ -11,21 +11,21 @@ Test sidebar toggle button
+   <title>Sidebar toggle button test</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   let SidebarToggle = browserRequire("devtools/client/shared/components/SidebarToggle.js");
+ 
+   try {
+-    yield test();
++    await test();
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+ 
+   function test() {
+     const output1 = shallowRenderComponent(SidebarToggle, {
+@@ -44,13 +44,13 @@ window.onload = Task.async(function* () 
+       collapsePaneTitle: "Expand",
+       expandPaneTitle: "Collapse"
+     });
+ 
+     is(output2.props.title, "Collapse", "Proper title is set");
+     ok(output2.props.className.includes("pane-collapsed"),
+       "Proper class name is set");
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_stack-trace-source-maps.html b/devtools/client/shared/components/test/mochitest/test_stack-trace-source-maps.html
+--- a/devtools/client/shared/components/test/mochitest/test_stack-trace-source-maps.html
++++ b/devtools/client/shared/components/test/mochitest/test_stack-trace-source-maps.html
+@@ -22,17 +22,17 @@ Test the rendering of a stack trace with
+ window.onload = function () {
+   let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+   let React = browserRequire("devtools/client/shared/vendor/react");
+   let StackTrace = React.createFactory(
+     browserRequire("devtools/client/shared/components/StackTrace")
+   );
+   ok(StackTrace, "Got the StackTrace factory");
+ 
+-  add_task(function* () {
++  add_task(async function () {
+     let stacktrace = [
+       {
+         filename: "https://bugzilla.mozilla.org/bundle.js",
+         lineNumber: 99,
+         columnNumber: 10
+       },
+       {
+         functionName: "loadFunc",
+@@ -54,17 +54,17 @@ window.onload = function () {
+ 		   newLine, column);
+ 	},
+         unsubscribe: function (url, line, column, callback) {
+         },
+       },
+     };
+ 
+     let trace = ReactDOM.render(StackTrace(props), window.document.body);
+-    yield forceRender(trace);
++    await forceRender(trace);
+ 
+     let traceEl = ReactDOM.findDOMNode(trace);
+     ok(traceEl, "Rendered StackTrace has an element");
+ 
+     // Get the child nodes and filter out the text-only whitespace ones
+     let frameEls = Array.from(traceEl.childNodes)
+       .filter(n => n.className && n.className.includes("frame"));
+     ok(frameEls, "Rendered StackTrace has frames");
+diff --git a/devtools/client/shared/components/test/mochitest/test_stack-trace.html b/devtools/client/shared/components/test/mochitest/test_stack-trace.html
+--- a/devtools/client/shared/components/test/mochitest/test_stack-trace.html
++++ b/devtools/client/shared/components/test/mochitest/test_stack-trace.html
+@@ -22,17 +22,17 @@ Test the rendering of a stack trace
+ window.onload = function() {
+   let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+   let React = browserRequire("devtools/client/shared/vendor/react");
+   let StackTrace = React.createFactory(
+     browserRequire("devtools/client/shared/components/StackTrace")
+   );
+   ok(StackTrace, "Got the StackTrace factory");
+ 
+-  add_task(function* () {
++  add_task(async function() {
+     let stacktrace = [
+       {
+         filename: "http://myfile.com/mahscripts.js",
+         lineNumber: 55,
+         columnNumber: 10
+       },
+       {
+         asyncCause: "because",
+@@ -44,17 +44,17 @@ window.onload = function() {
+ 
+     let props = {
+       stacktrace,
+       onViewSourceInDebugger: () => {},
+       onViewSourceInScratchpad: () => {},
+     };
+ 
+     let trace = ReactDOM.render(StackTrace(props), window.document.body);
+-    yield forceRender(trace);
++    await forceRender(trace);
+ 
+     let traceEl = ReactDOM.findDOMNode(trace);
+     ok(traceEl, "Rendered StackTrace has an element");
+ 
+     // Get the child nodes and filter out the text-only whitespace ones
+     let frameEls = Array.from(traceEl.childNodes)
+       .filter(n => n.className && n.className.includes("frame"));
+     ok(frameEls, "Rendered StackTrace has frames");
+diff --git a/devtools/client/shared/components/test/mochitest/test_tabs_accessibility.html b/devtools/client/shared/components/test/mochitest/test_tabs_accessibility.html
+--- a/devtools/client/shared/components/test/mochitest/test_tabs_accessibility.html
++++ b/devtools/client/shared/components/test/mochitest/test_tabs_accessibility.html
+@@ -11,47 +11,47 @@ Test tabs accessibility.
+   <title>Tabs component accessibility test</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+     const InspectorTabPanel = createFactory(browserRequire("devtools/client/inspector/components/InspectorTabPanel"));
+     const Tabbar =
+       createFactory(browserRequire("devtools/client/shared/components/tabs/TabBar"));
+     const tabbar = Tabbar();
+     const tabbarReact = ReactDOM.render(tabbar, window.document.body);
+     const tabbarEl = ReactDOM.findDOMNode(tabbarReact);
+ 
+     // Setup for InspectorTabPanel
+     const tabpanels = document.createElement("div");
+     tabpanels.id = "tabpanels";
+     document.body.appendChild(tabpanels);
+ 
+-    yield addTabWithPanel(0);
+-    yield addTabWithPanel(1);
++    await addTabWithPanel(0);
++    await addTabWithPanel(1);
+ 
+     const tabAnchors = tabbarEl.querySelectorAll("li.tabs-menu-item a");
+ 
+     is(tabAnchors[0].parentElement.getAttribute("role"), "presentation", "li role is set correctly");
+     is(tabAnchors[0].getAttribute("role"), "tab", "Anchor role is set correctly");
+     is(tabAnchors[0].getAttribute("aria-selected"), "true", "Anchor aria-selected is set correctly by default");
+     is(tabAnchors[0].getAttribute("aria-controls"), "sidebar-0-panel", "Anchor aria-controls is set correctly");
+     is(tabAnchors[1].parentElement.getAttribute("role"), "presentation", "li role is set correctly");
+     is(tabAnchors[1].getAttribute("role"), "tab", "Anchor role is set correctly");
+     is(tabAnchors[1].getAttribute("aria-selected"), "false", "Anchor aria-selected is set correctly by default");
+     is(tabAnchors[1].getAttribute("aria-controls"), "sidebar-1-panel", "Anchor aria-controls is set correctly");
+ 
+-    yield setState(tabbarReact, Object.assign({}, tabbarReact.state, {
++    await setState(tabbarReact, Object.assign({}, tabbarReact.state, {
+       activeTab: 1
+     }));
+ 
+     is(tabAnchors[0].getAttribute("aria-selected"), "false", "Anchor aria-selected is reset correctly");
+     is(tabAnchors[1].getAttribute("aria-selected"), "true", "Anchor aria-selected is reset correctly");
+ 
+     function addTabWithPanel(tabId) {
+       // Setup for InspectorTabPanel
+@@ -67,13 +67,13 @@ window.onload = Task.async(function* () 
+         }),
+       }));
+     }
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_tabs_menu.html b/devtools/client/shared/components/test/mochitest/test_tabs_menu.html
+--- a/devtools/client/shared/components/test/mochitest/test_tabs_menu.html
++++ b/devtools/client/shared/components/test/mochitest/test_tabs_menu.html
+@@ -17,17 +17,17 @@ Test all-tabs menu.
+   <link rel="stylesheet" type="text/css" href="resource://devtools/client/shared/components/tabs/TabBar.css">
+   <link rel="stylesheet" type="text/css" href="resource://devtools/client/inspector/components/side-panel.css">
+   <link rel="stylesheet" type="text/css" href="resource://devtools/client/inspector/components/InspectorTabPanel.css">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     const { Component, createFactory } = browserRequire("devtools/client/shared/vendor/react");
+     const dom = require("devtools/client/shared/vendor/react-dom-factories");
+     const Tabbar = createFactory(browserRequire("devtools/client/shared/components/tabs/TabBar"));
+ 
+     // Create container for the TabBar. Set smaller width
+     // to ensure that tabs won't fit and the all-tabs menu
+@@ -50,34 +50,34 @@ window.onload = Task.async(function* () 
+         return dom.div({}, "content");
+       }
+     }
+ 
+     // Test panel.
+     let TabPanel = createFactory(TabPanelClass);
+ 
+     // Create a few panels.
+-    yield addTabWithPanel(1);
+-    yield addTabWithPanel(2);
+-    yield addTabWithPanel(3);
+-    yield addTabWithPanel(4);
+-    yield addTabWithPanel(5);
++    await addTabWithPanel(1);
++    await addTabWithPanel(2);
++    await addTabWithPanel(3);
++    await addTabWithPanel(4);
++    await addTabWithPanel(5);
+ 
+     // Make sure the all-tabs menu is there.
+     const allTabsMenu = tabBarBox.querySelector(".all-tabs-menu");
+     ok(allTabsMenu, "All-tabs menu must be rendered");
+ 
+     function addTabWithPanel(tabId) {
+       return setState(tabbarReact, Object.assign({}, tabbarReact.state, {
+         tabs: tabbarReact.state.tabs.concat({id: `${tabId}`,
+           title: `tab-${tabId}`, panel: TabPanel}),
+       }));
+     }
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_tree_01.html b/devtools/client/shared/components/test/mochitest/test_tree_01.html
+--- a/devtools/client/shared/components/test/mochitest/test_tree_01.html
++++ b/devtools/client/shared/components/test/mochitest/test_tree_01.html
+@@ -12,34 +12,34 @@ depth.
+   <title>Tree component test</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     let React = browserRequire("devtools/client/shared/vendor/react");
+     let Tree = React.createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+ 
+     ok(React, "Should get React");
+     ok(Tree, "Should get Tree");
+ 
+     const t = Tree(TEST_TREE_INTERFACE);
+     ok(t, "Should be able to create Tree instances");
+ 
+     const tree = ReactDOM.render(t, window.document.body);
+     ok(tree, "Should be able to mount Tree instances");
+     isAccessibleTree(tree);
+ 
+     TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:false",
+       "---K:false",
+       "---L:false",
+       "--F:false",
+@@ -53,13 +53,13 @@ window.onload = Task.async(function* () 
+       "-N:false",
+       "--O:false",
+     ], "Should get the items rendered and indented as expected");
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_tree_02.html b/devtools/client/shared/components/test/mochitest/test_tree_02.html
+--- a/devtools/client/shared/components/test/mochitest/test_tree_02.html
++++ b/devtools/client/shared/components/test/mochitest/test_tree_02.html
+@@ -11,36 +11,36 @@ Test that collapsed subtrees aren't rend
+   <title>Tree component test</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     let React = browserRequire("devtools/client/shared/vendor/react");
+     let Tree = React.createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+ 
+     const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
+ 
+     isAccessibleTree(tree);
+     TEST_TREE.expanded = new Set("MNO".split(""));
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "M:false",
+       "-N:false",
+       "--O:false",
+     ], "Collapsed subtrees shouldn't be rendered");
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_tree_03.html b/devtools/client/shared/components/test/mochitest/test_tree_03.html
+--- a/devtools/client/shared/components/test/mochitest/test_tree_03.html
++++ b/devtools/client/shared/components/test/mochitest/test_tree_03.html
+@@ -11,17 +11,17 @@ Test Tree's autoExpandDepth.
+   <title>Tree component test</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     let React = browserRequire("devtools/client/shared/vendor/react");
+     let Tree = React.createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+ 
+     const tree = ReactDOM.render(Tree(Object.assign({}, TEST_TREE_INTERFACE, {
+       autoExpandDepth: 1
+     })), window.document.body);
+@@ -35,13 +35,13 @@ window.onload = Task.async(function* () 
+       "M:false",
+       "-N:false",
+     ], "Tree should be auto expanded one level");
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_tree_04.html b/devtools/client/shared/components/test/mochitest/test_tree_04.html
+--- a/devtools/client/shared/components/test/mochitest/test_tree_04.html
++++ b/devtools/client/shared/components/test/mochitest/test_tree_04.html
+@@ -11,17 +11,17 @@ Test that we only render visible tree it
+   <title>Tree component test</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     function getSpacerHeights() {
+       return {
+         top: document.querySelector(".tree > div:first-of-type").clientHeight,
+         bottom: document.querySelector(".tree > div:last-of-type").clientHeight,
+       };
+     }
+ 
+@@ -32,17 +32,17 @@ window.onload = Task.async(function* () 
+     const Tree = React.createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+ 
+     const tree = ReactDOM.render(
+       Tree(Object.assign({}, TEST_TREE_INTERFACE, { itemHeight: ITEM_HEIGHT })),
+       window.document.body);
+ 
+     TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+ 
+-    yield setState(tree, {
++    await setState(tree, {
+       height: 3 * ITEM_HEIGHT,
+       scroll: 1 * ITEM_HEIGHT
+     });
+ 
+     isAccessibleTree(tree);
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+@@ -50,17 +50,17 @@ window.onload = Task.async(function* () 
+       "---K:false",
+       "---L:false",
+     ], "Tree should show the 2nd, 3rd, and 4th items + buffer of 1 item at each end");
+ 
+     let spacers = getSpacerHeights();
+     is(spacers.top, 0, "Top spacer has the correct height");
+     is(spacers.bottom, 10 * ITEM_HEIGHT, "Bottom spacer has the correct height");
+ 
+-    yield setState(tree, {
++    await setState(tree, {
+       height: 2 * ITEM_HEIGHT,
+       scroll: 3 * ITEM_HEIGHT
+     });
+ 
+     isAccessibleTree(tree);
+     isRenderedTree(document.body.textContent, [
+       "--E:false",
+       "---K:false",
+@@ -69,17 +69,17 @@ window.onload = Task.async(function* () 
+     ], "Tree should show the 4th and 5th item + buffer of 1 item at each end");
+ 
+     spacers = getSpacerHeights();
+     is(spacers.top, 2 * ITEM_HEIGHT, "Top spacer has the correct height");
+     is(spacers.bottom, 9 * ITEM_HEIGHT, "Bottom spacer has the correct height");
+ 
+     // Set height to 2 items + 1 pixel at each end, scroll so that 4 items are visible
+     // (2 fully, 2 partially with 1 visible pixel)
+-    yield setState(tree, {
++    await setState(tree, {
+       height: 2 * ITEM_HEIGHT + 2,
+       scroll: 3 * ITEM_HEIGHT - 1
+     });
+ 
+     isRenderedTree(document.body.textContent, [
+       "-B:false",
+       "--E:false",
+       "---K:false",
+@@ -87,17 +87,17 @@ window.onload = Task.async(function* () 
+       "--F:false",
+       "--G:false",
+     ], "Tree should show the 4 visible items + buffer of 1 item at each end");
+ 
+     spacers = getSpacerHeights();
+     is(spacers.top, 1 * ITEM_HEIGHT, "Top spacer has the correct height");
+     is(spacers.bottom, 8 * ITEM_HEIGHT, "Bottom spacer has the correct height");
+ 
+-    yield setState(tree, {
++    await setState(tree, {
+       height: 20 * ITEM_HEIGHT,
+       scroll: 0
+     });
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:false",
+@@ -118,13 +118,13 @@ window.onload = Task.async(function* () 
+     spacers = getSpacerHeights();
+     is(spacers.top, 0, "Top spacer has zero height");
+     is(spacers.bottom, 0, "Bottom spacer has zero height");
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_tree_06.html b/devtools/client/shared/components/test/mochitest/test_tree_06.html
+--- a/devtools/client/shared/components/test/mochitest/test_tree_06.html
++++ b/devtools/client/shared/components/test/mochitest/test_tree_06.html
+@@ -11,17 +11,17 @@ Test keyboard navigation with the Tree c
+   <title>Tree component test</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">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+     const { Simulate } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+     const Tree = createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+ 
+     function renderTree(props) {
+       const treeProps = Object.assign({},
+@@ -37,17 +37,17 @@ window.onload = Task.async(function* () 
+     isAccessibleTree(tree);
+     TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+ 
+     // UP ----------------------------------------------------------------------
+ 
+     info("Up to the previous sibling.");
+     renderTree({ focused: "L" });
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:false",
+       "---K:true",
+       "---L:false",
+       "--F:false",
+@@ -59,17 +59,17 @@ window.onload = Task.async(function* () 
+       "--J:false",
+       "M:false",
+       "-N:false",
+       "--O:false",
+     ], "After the UP, K should be focused.");
+ 
+     info("Up to the parent.");
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:true",
+       "---K:false",
+       "---L:false",
+       "--F:false",
+@@ -82,17 +82,17 @@ window.onload = Task.async(function* () 
+       "M:false",
+       "-N:false",
+       "--O:false",
+     ], "After the UP, E should be focused.");
+ 
+     info("Try and navigate up, past the first item.");
+     renderTree({ focused: "A" });
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:true",
+       "-B:false",
+       "--E:false",
+       "---K:false",
+       "---L:false",
+       "--F:false",
+@@ -107,17 +107,17 @@ window.onload = Task.async(function* () 
+       "--O:false",
+     ], "After the UP, A should be focused and we shouldn't have overflowed past it.");
+ 
+     // DOWN --------------------------------------------------------------------
+ 
+     info("Down to next sibling.");
+     renderTree({ focused: "K" });
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:false",
+       "---K:false",
+       "---L:true",
+       "--F:false",
+@@ -129,17 +129,17 @@ window.onload = Task.async(function* () 
+       "--J:false",
+       "M:false",
+       "-N:false",
+       "--O:false",
+     ], "After the DOWN, L should be focused.");
+ 
+     info("Down to parent's next sibling.");
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:false",
+       "---K:false",
+       "---L:false",
+       "--F:true",
+@@ -152,17 +152,17 @@ window.onload = Task.async(function* () 
+       "M:false",
+       "-N:false",
+       "--O:false",
+     ], "After the DOWN, F should be focused.");
+ 
+     info("Try and go down past the last item.");
+     renderTree({ focused: "O" });
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:false",
+       "---K:false",
+       "---L:false",
+       "--F:false",
+@@ -177,17 +177,17 @@ window.onload = Task.async(function* () 
+       "--O:true",
+     ], "After the DOWN, O should still be focused and we shouldn't have overflowed past it.");
+ 
+     // LEFT --------------------------------------------------------------------
+ 
+     info("Left to go to parent.");
+     renderTree({ focused: "L" });
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowLeft" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:true",
+       "---K:false",
+       "---L:false",
+       "--F:false",
+@@ -199,17 +199,17 @@ window.onload = Task.async(function* () 
+       "--J:false",
+       "M:false",
+       "-N:false",
+       "--O:false",
+     ], "After the LEFT, E should be focused.");
+ 
+     info("Left to collapse children.");
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowLeft" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:true",
+       "--F:false",
+       "--G:false",
+       "-C:false",
+@@ -221,17 +221,17 @@ window.onload = Task.async(function* () 
+       "-N:false",
+       "--O:false",
+     ], "After the LEFT, E's children should be collapsed.");
+ 
+     // RIGHT -------------------------------------------------------------------
+ 
+     info("Right to expand children.");
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:true",
+       "---K:false",
+       "---L:false",
+       "--F:false",
+@@ -243,17 +243,17 @@ window.onload = Task.async(function* () 
+       "--J:false",
+       "M:false",
+       "-N:false",
+       "--O:false",
+     ], "After the RIGHT, E's children should be expanded again.");
+ 
+     info("Right to go to next item.");
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:false",
+       "---K:true",
+       "---L:false",
+       "--F:false",
+@@ -272,17 +272,17 @@ window.onload = Task.async(function* () 
+     let keysWithModifier = [
+       { key: "ArrowDown", altKey: true },
+       { key: "ArrowDown", ctrlKey: true },
+       { key: "ArrowDown", metaKey: true },
+       { key: "ArrowDown", shiftKey: true },
+     ];
+     for (let key of keysWithModifier) {
+       Simulate.keyDown(document.querySelector(".tree"), key);
+-      yield forceRender(tree);
++      await forceRender(tree);
+ 
+       isRenderedTree(document.body.textContent, [
+         "A:false",
+         "-B:false",
+         "--E:false",
+         "---K:true",
+         "---L:false",
+         "--F:false",
+@@ -297,13 +297,13 @@ window.onload = Task.async(function* () 
+         "--O:false",
+       ], "After DOWN + (alt|ctrl|meta|shift), K should remain focused.");
+     }
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_tree_07.html b/devtools/client/shared/components/test/mochitest/test_tree_07.html
+--- a/devtools/client/shared/components/test/mochitest/test_tree_07.html
++++ b/devtools/client/shared/components/test/mochitest/test_tree_07.html
+@@ -12,17 +12,17 @@ Test that arrows get the open attribute 
+   <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">
+   <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     const React = browserRequire("devtools/client/shared/vendor/react");
+     const dom = require("devtools/client/shared/vendor/react-dom-factories");
+     const Tree =
+       React.createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+ 
+     const treeProps = Object.assign({}, TEST_TREE_INTERFACE, {
+@@ -35,32 +35,32 @@ window.onload = Task.async(function* () 
+           arrow,
+           item
+         );
+       }
+     });
+     const tree = ReactDOM.render(Tree(treeProps), window.document.body);
+ 
+     TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     let arrows = document.querySelectorAll(".arrow");
+     for (let a of arrows) {
+       ok(a.classList.contains("open"), "Every arrow should be open.");
+     }
+ 
+     TEST_TREE.expanded = new Set();
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     arrows = document.querySelectorAll(".arrow");
+     for (let a of arrows) {
+       ok(!a.classList.contains("open"), "Every arrow should be closed.");
+     }
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_tree_08.html b/devtools/client/shared/components/test/mochitest/test_tree_08.html
+--- a/devtools/client/shared/components/test/mochitest/test_tree_08.html
++++ b/devtools/client/shared/components/test/mochitest/test_tree_08.html
+@@ -13,17 +13,17 @@ other inputs.
+   <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">
+   <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+     const Tree = createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+ 
+     function renderTree(props) {
+       const treeProps = Object.assign({},
+         TEST_TREE_INTERFACE,
+@@ -37,22 +37,22 @@ window.onload = Task.async(function* () 
+ 
+     const input = document.createElement("input");
+     document.body.appendChild(input);
+ 
+     input.focus();
+     is(document.activeElement, input, "The text input should be focused.");
+ 
+     document.querySelector(".tree-node").click();
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isnot(document.activeElement, input,
+           "The input should have had it's focus stolen by clicking on a tree item.");
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_tree_09.html b/devtools/client/shared/components/test/mochitest/test_tree_09.html
+--- a/devtools/client/shared/components/test/mochitest/test_tree_09.html
++++ b/devtools/client/shared/components/test/mochitest/test_tree_09.html
+@@ -12,17 +12,17 @@ Test that when an item in the Tree compo
+   <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">
+   <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+     const { Simulate } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+     const Tree = createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+ 
+     let numberOfExpands = 0;
+     let lastExpandedItem = null;
+@@ -54,29 +54,29 @@ window.onload = Task.async(function* () 
+ 
+     const tree = renderTree({ focused: "A" });
+ 
+     is(lastExpandedItem, null);
+     is(lastCollapsedItem, null);
+ 
+     // Expand "A" via the keyboard and then let the component re-render.
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     is(lastExpandedItem, "A", "Our onExpand callback should have been fired.");
+     is(numberOfExpands, 1);
+ 
+     // Now collapse "A" via the keyboard and then let the component re-render.
+     Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowLeft" });
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     is(lastCollapsedItem, "A", "Our onCollapsed callback should have been fired.");
+     is(numberOfCollapses, 1);
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_tree_10.html b/devtools/client/shared/components/test/mochitest/test_tree_10.html
+--- a/devtools/client/shared/components/test/mochitest/test_tree_10.html
++++ b/devtools/client/shared/components/test/mochitest/test_tree_10.html
+@@ -12,17 +12,17 @@ Test that when an item in the Tree compo
+   <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">
+   <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+     const Tree = createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+ 
+     function renderTree(props) {
+       const treeProps = Object.assign({},
+         TEST_TREE_INTERFACE,
+@@ -42,13 +42,13 @@ window.onload = Task.async(function* () 
+       "M:false",
+       "-N:false",
+     ], "Should have auto-expanded one level.");
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/components/test/mochitest/test_tree_11.html b/devtools/client/shared/components/test/mochitest/test_tree_11.html
+--- a/devtools/client/shared/components/test/mochitest/test_tree_11.html
++++ b/devtools/client/shared/components/test/mochitest/test_tree_11.html
+@@ -23,17 +23,17 @@ Test that when an item in the Tree compo
+        overflow: auto;
+    }
+   </style>
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+ <script type="application/javascript">
+-window.onload = Task.async(function* () {
++window.onload = async function () {
+   try {
+     const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+     const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+     const { Simulate } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+     const Tree = createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+ 
+     TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+ 
+@@ -56,41 +56,41 @@ window.onload = Task.async(function* () 
+     isRenderedTree(document.body.textContent, [
+       "A:false",
+       "-B:false",
+       "--E:false",
+       "---K:true",
+       "---L:false",
+     ], "Should render initial correctly");
+ 
+-    yield new Promise(resolve => {
++    await new Promise(resolve => {
+       const treeElem = document.querySelector(".tree");
+       treeElem.addEventListener("scroll", function onScroll() {
+         dumpn("Got scroll event");
+         treeElem.removeEventListener("scroll", onScroll);
+         resolve();
+       });
+ 
+       dumpn("Sending ArrowDown key");
+       Simulate.keyDown(treeElem, { key: "ArrowDown" });
+     });
+ 
+     dumpn("Forcing re-render");
+-    yield forceRender(tree);
++    await forceRender(tree);
+ 
+     isRenderedTree(document.body.textContent, [
+       "-B:false",
+       "--E:false",
+       "---K:false",
+       "---L:true",
+       "--F:false",
+     ], "Should have scrolled down one");
+ 
+   } catch(e) {
+     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+   } finally {
+     SimpleTest.finish();
+   }
+-});
++};
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/devtools/client/shared/developer-toolbar.js b/devtools/client/shared/developer-toolbar.js
+--- a/devtools/client/shared/developer-toolbar.js
++++ b/devtools/client/shared/developer-toolbar.js
+@@ -6,17 +6,16 @@
+ 
+ const { Ci } = require("chrome");
+ const promise = require("promise");
+ const Services = require("Services");
+ const { TargetFactory } = require("devtools/client/framework/target");
+ const Telemetry = require("devtools/client/shared/telemetry");
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+-const {Task} = require("devtools/shared/task");
+ 
+ const NS_XHTML = "http://www.w3.org/1999/xhtml";
+ 
+ const { PluralForm } = require("devtools/shared/plural-form");
+ 
+ loader.lazyGetter(this, "prefBranch", function() {
+   return Services.prefs.getBranch(null)
+                     .QueryInterface(Ci.nsIPrefBranch);
+@@ -38,28 +37,28 @@ var CommandUtils = {
+    * Caches requisitions created when calling executeOnTarget:
+    * Target => Requisition Promise
+    */
+   _requisitions: new WeakMap(),
+ 
+   /**
+    * Utility to execute a command string on a given target
+    */
+-  executeOnTarget: Task.async(function* (target, command) {
++  async executeOnTarget(target, command) {
+     let requisitionPromise = this._requisitions.get(target);
+     if (!requisitionPromise) {
+       requisitionPromise = this.createRequisition(target, {
+         environment: CommandUtils.createEnvironment({ target }, "target")
+       });
+       // Store the promise to avoid races by storing the promise immediately
+       this._requisitions.set(target, requisitionPromise);
+     }
+-    let requisition = yield requisitionPromise;
++    let requisition = await requisitionPromise;
+     requisition.updateExec(command);
+-  }),
++  },
+ 
+   /**
+    * Utility to ensure that things are loaded in the correct order
+    */
+   createRequisition: function(target, options) {
+     if (!gcliInit) {
+       return promise.reject("Unable to load gcli");
+     }
+@@ -366,25 +365,25 @@ DeveloperToolbar.prototype.show = functi
+   // again; ensuring we're focusing the element too if `focus` argument is set to `true`.
+   if (this._showPromise !== null) {
+     if (focus) {
+       return this.focus();
+     }
+     return this._showPromise;
+   }
+ 
+-  this._showPromise = Task.spawn((function* () {
++  this._showPromise = ((async function() {
+     // hide() is async, so ensure we don't need to wait for hide() to
+     // finish.  We unconditionally yield here, even if _hidePromise is
+     // null, so that the spawn call returns a promise before starting
+     // to do any real work.
+-    yield this._hidePromise;
++    await this._hidePromise;
+ 
+     // Append the browser-level stylesheet to the browser document.
+-    yield gDevToolsBrowser.loadBrowserStyleSheet(this._chromeWindow);
++    await gDevToolsBrowser.loadBrowserStyleSheet(this._chromeWindow);
+ 
+     this.createToolbar();
+ 
+     Services.prefs.setBoolPref("devtools.toolbar.visible", true);
+ 
+     this._telemetry.toolOpened("developertoolbar");
+ 
+     this._notify(NOTIFICATIONS.LOAD);
+@@ -392,34 +391,34 @@ DeveloperToolbar.prototype.show = functi
+     this._input = this._doc.querySelector(".gclitoolbar-input-node");
+ 
+     // Initializing GCLI can only be done when we've got content windows to
+     // write to, so this needs to be done asynchronously.
+     let panelPromises = [
+       TooltipPanel.create(this),
+       OutputPanel.create(this)
+     ];
+-    let panels = yield promise.all(panelPromises);
++    let panels = await promise.all(panelPromises);
+ 
+     [ this.tooltipPanel, this.outputPanel ] = panels;
+ 
+     this._doc.getElementById("menu_devToolbar").setAttribute("checked", "true");
+ 
+     this.target = TargetFactory.forTab(this._chromeWindow.gBrowser.selectedTab);
+     const options = {
+       environment: CommandUtils.createEnvironment(this, "target"),
+       document: this.outputPanel.document,
+     };
+-    let requisition = yield CommandUtils.createRequisition(this.target, options);
++    let requisition = await CommandUtils.createRequisition(this.target, options);
+     this.requisition = requisition;
+ 
+     // The <textbox> `value` may still be undefined on the XUL binding if
+     // we fetch it early
+     let value = this._input.value || "";
+-    yield this.requisition.update(value);
++    await this.requisition.update(value);
+ 
+     const Inputter = require("gcli/mozui/inputter").Inputter;
+     const Completer = require("gcli/mozui/completer").Completer;
+     const Tooltip = require("gcli/mozui/tooltip").Tooltip;
+     const FocusManager = require("gcli/ui/focus").FocusManager;
+ 
+     this.onOutput = this.requisition.commandOutputManager.onOutput;
+ 
+@@ -465,28 +464,28 @@ DeveloperToolbar.prototype.show = functi
+     gDevTools.on("toolbox-ready", this._onToolboxReady);
+     gDevTools.on("toolbox-destroyed", this._onToolboxDestroyed);
+ 
+     this._initErrorsCount(tabbrowser.selectedTab);
+ 
+     this._element.hidden = false;
+ 
+     if (focus) {
+-      yield this.focus();
++      await this.focus();
+     }
+     this._notify(NOTIFICATIONS.SHOW);
+ 
+     if (!DeveloperToolbar.introShownThisSession) {
+       let intro = require("gcli/ui/intro");
+       intro.maybeShowIntro(this.requisition.commandOutputManager,
+                            this.requisition.conversionContext,
+                            this.outputPanel);
+       DeveloperToolbar.introShownThisSession = true;
+     }
+-  }).bind(this));
++  }).bind(this))();
+ 
+   return this._showPromise;
+ };
+ 
+ /**
+  * Hide the developer toolbar.
+  */
+ DeveloperToolbar.prototype.hide = function() {
+diff --git a/devtools/client/shared/doorhanger.js b/devtools/client/shared/doorhanger.js
+--- a/devtools/client/shared/doorhanger.js
++++ b/devtools/client/shared/doorhanger.js
+@@ -1,17 +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/. */
+ 
+ "use strict";
+ 
+ const Services = require("Services");
+ const { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
+-const { Task } = require("devtools/shared/task");
+ 
+ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ const DEV_EDITION_PROMO_URL = "chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.xul";
+ const DEV_EDITION_PROMO_ENABLED_PREF = "devtools.devedition.promo.enabled";
+ const DEV_EDITION_PROMO_SHOWN_PREF = "devtools.devedition.promo.shown";
+ const DEV_EDITION_PROMO_URL_PREF = "devtools.devedition.promo.url";
+ 
+ /**
+@@ -60,46 +59,46 @@ var panelAttrs = {
+  *        The window that should house the doorhanger.
+  * @param {String} type
+  *        The type of doorhanger to be displayed is, using the `TYPES`
+  *        definition.
+  * @param {String} selector
+  *        The selector that the doorhanger should be appended to within
+  *        `window`.  Defaults to a XUL Document's `window` element.
+  */
+-exports.showDoorhanger = Task.async(function* ({ window, type, anchor }) {
++exports.showDoorhanger = async function({ window, type, anchor }) {
+   let { predicate, success, url, action } = TYPES[type];
+   // Abort if predicate fails
+   if (!predicate()) {
+     return;
+   }
+ 
+   // Call success function to set preferences/cleanup immediately,
+   // so if triggered multiple times, only happens once (Windows/Linux)
+   success();
+ 
+   // Wait 200ms to prevent flickering where the popup is displayed
+   // before the underlying window (Windows 7, 64bit)
+-  yield wait(200);
++  await wait(200);
+ 
+   let document = window.document;
+ 
+   let panel = document.createElementNS(XULNS, "panel");
+   let frame = document.createElementNS(XULNS, "iframe");
+   let parentEl = document.querySelector("window");
+ 
+   frame.setAttribute("src", url);
+   let close = () => parentEl.removeChild(panel);
+ 
+   setDoorhangerStyle(panel, frame);
+ 
+   panel.appendChild(frame);
+   parentEl.appendChild(panel);
+ 
+-  yield onFrameLoad(frame);
++  await onFrameLoad(frame);
+ 
+   panel.openPopup(anchor);
+ 
+   let closeBtn = frame.contentDocument.querySelector("#close");
+   if (closeBtn) {
+     closeBtn.addEventListener("click", close);
+   }
+ 
+@@ -107,17 +106,17 @@ exports.showDoorhanger = Task.async(func
+   if (goBtn) {
+     goBtn.addEventListener("click", () => {
+       if (action) {
+         action();
+       }
+       close();
+     });
+   }
+-});
++};
+ 
+ function setDoorhangerStyle(panel, frame) {
+   Object.keys(panelAttrs).forEach(prop => {
+     return panel.setAttribute(prop, panelAttrs[prop]);
+   });
+   panel.style.margin = "20px";
+   panel.style.borderRadius = "5px";
+   panel.style.border = "none";
+diff --git a/devtools/client/shared/redux/middleware/test/test_middleware-task-01.js b/devtools/client/shared/redux/middleware/test/test_middleware-task-01.js
+--- a/devtools/client/shared/redux/middleware/test/test_middleware-task-01.js
++++ b/devtools/client/shared/redux/middleware/test/test_middleware-task-01.js
+@@ -11,36 +11,36 @@ const { task } = require("devtools/clien
+  * Tests that task middleware allows dispatching generators, promises and objects
+  * that return actions;
+  */
+ 
+ function run_test() {
+   run_next_test();
+ }
+ 
+-add_task(function* () {
++add_task(async function() {
+   let store = applyMiddleware(task)(createStore)(reducer);
+ 
+   store.dispatch(fetch1("generator"));
+-  yield waitUntilState(store, () => store.getState().length === 1);
++  await waitUntilState(store, () => store.getState().length === 1);
+   equal(store.getState()[0].data, "generator",
+         "task middleware async dispatches an action via generator");
+ 
+   store.dispatch(fetch2("sync"));
+-  yield waitUntilState(store, () => store.getState().length === 2);
++  await waitUntilState(store, () => store.getState().length === 2);
+   equal(store.getState()[1].data, "sync",
+         "task middleware sync dispatches an action via sync");
+ });
+ 
+ function fetch1(data) {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     equal(getState().length, 0, "`getState` is accessible in a generator action");
+-    let moreData = yield new Promise(resolve => resolve(data));
++    let moreData = await new Promise(resolve => resolve(data));
+     // Ensure it handles more than one yield
+-    moreData = yield new Promise(resolve => resolve(data));
++    moreData = await new Promise(resolve => resolve(data));
+     dispatch({ type: "fetch1", data: moreData });
+   };
+ }
+ 
+ function fetch2(data) {
+   return {
+     type: "fetch2",
+     data
+diff --git a/devtools/client/shared/redux/middleware/test/test_middleware-task-02.js b/devtools/client/shared/redux/middleware/test/test_middleware-task-02.js
+--- a/devtools/client/shared/redux/middleware/test/test_middleware-task-02.js
++++ b/devtools/client/shared/redux/middleware/test/test_middleware-task-02.js
+@@ -11,53 +11,53 @@
+ 
+ const { createStore, applyMiddleware } = require("devtools/client/shared/vendor/redux");
+ const { task } = require("devtools/client/shared/redux/middleware/task");
+ 
+ function run_test() {
+   run_next_test();
+ }
+ 
+-add_task(function* () {
++add_task(async function() {
+   let store = applyMiddleware(task)(createStore)(reducer);
+ 
+   store.dispatch(comboAction());
+-  yield waitUntilState(store, () => store.getState().length === 3);
++  await waitUntilState(store, () => store.getState().length === 3);
+ 
+   equal(store.getState()[0].type, "fetchAsync-start",
+         "Async dispatched actions in a generator task are fired");
+   equal(store.getState()[1].type, "fetchAsync-end",
+         "Async dispatched actions in a generator task are fired");
+   equal(store.getState()[2].type, "fetchSync",
+         "Return values of yielded sync dispatched actions are correct");
+   equal(store.getState()[3].type, "fetch-done",
+         "Return values of yielded async dispatched actions are correct");
+   equal(store.getState()[3].data.sync.data, "sync",
+         "Return values of dispatched sync values are correct");
+   equal(store.getState()[3].data.async, "async",
+         "Return values of dispatched async values are correct");
+ });
+ 
+ function comboAction() {
+-  return function* (dispatch, getState) {
++  return async function(dispatch, getState) {
+     let data = {};
+-    data.async = yield dispatch(fetchAsync("async"));
+-    data.sync = yield dispatch(fetchSync("sync"));
++    data.async = await dispatch(fetchAsync("async"));
++    data.sync = await dispatch(fetchSync("sync"));
+     dispatch({ type: "fetch-done", data });
+   };
+ }
+ 
+ function fetchSync(data) {
+   return { type: "fetchSync", data };
+ }
+ 
+ function fetchAsync(data) {
+-  return function* (dispatch) {
++  return async function(dispatch) {
+     dispatch({ type: "fetchAsync-start" });
+-    let val = yield new Promise(resolve => resolve(data));
++    let val = await new Promise(resolve => resolve(data));
+     dispatch({ type: "fetchAsync-end" });
+     return val;
+   };
+ }
+ 
+ function reducer(state = [], action) {
+   info("Action called: " + action.type);
+   if (/fetch/.test(action.type)) {
+diff --git a/devtools/client/shared/redux/middleware/test/test_middleware-task-03.js b/devtools/client/shared/redux/middleware/test/test_middleware-task-03.js
+--- a/devtools/client/shared/redux/middleware/test/test_middleware-task-03.js
++++ b/devtools/client/shared/redux/middleware/test/test_middleware-task-03.js
+@@ -10,21 +10,21 @@ const { task, ERROR_TYPE } = require("de
+ /**
+  * Tests that the middleware handles errors thrown in tasks, and rejected promises.
+  */
+ 
+ function run_test() {
+   run_next_test();
+ }
+ 
+-add_task(function* () {
++add_task(async function() {
+   let store = applyMiddleware(task)(createStore)(reducer);
+ 
+   store.dispatch(generatorError());
+-  yield waitUntilState(store, () => store.getState().length === 1);
++  await waitUntilState(store, () => store.getState().length === 1);
+   equal(store.getState()[0].type, ERROR_TYPE,
+         "generator errors dispatch ERROR_TYPE actions");
+   equal(store.getState()[0].error, "task-middleware-error-generator",
+         "generator errors dispatch ERROR_TYPE actions with error");
+ });
+ 
+ function generatorError() {
+   return function* (dispatch, getState) {
+diff --git a/devtools/client/shared/test/browser_css_angle.js b/devtools/client/shared/test/browser_css_angle.js
+--- a/devtools/client/shared/test/browser_css_angle.js
++++ b/devtools/client/shared/test/browser_css_angle.js
+@@ -1,19 +1,19 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /* import-globals-from head.js */
+ "use strict";
+ 
+ var {angleUtils} = require("devtools/client/shared/css-angle");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  let [host] = yield createHost("bottom");
++add_task(async function() {
++  await addTab("about:blank");
++  let [host] = await createHost("bottom");
+ 
+   info("Starting the test");
+   testAngleUtils();
+   testAngleValidity();
+ 
+   host.destroy();
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/shared/test/browser_css_color.js b/devtools/client/shared/test/browser_css_color.js
+--- a/devtools/client/shared/test/browser_css_color.js
++++ b/devtools/client/shared/test/browser_css_color.js
+@@ -2,19 +2,19 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ var {colorUtils} = require("devtools/shared/css/color");
+ /* global getFixtureColorData */
+ loadHelperScript("helper_color_data.js");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  let [host,, doc] = yield createHost("bottom");
++add_task(async function() {
++  await addTab("about:blank");
++  let [host,, doc] = await createHost("bottom");
+ 
+   info("Creating a test canvas element to test colors");
+   let canvas = createTestCanvas(doc);
+   info("Starting the test");
+   testColorUtils(canvas);
+ 
+   host.destroy();
+   gBrowser.removeCurrentTab();
+diff --git a/devtools/client/shared/test/browser_cubic-bezier-01.js b/devtools/client/shared/test/browser_cubic-bezier-01.js
+--- a/devtools/client/shared/test/browser_cubic-bezier-01.js
++++ b/devtools/client/shared/test/browser_cubic-bezier-01.js
+@@ -6,18 +6,18 @@
+ 
+ // Tests that the CubicBezierWidget generates content in a given parent node
+ 
+ const {CubicBezierWidget} =
+   require("devtools/client/shared/widgets/CubicBezierWidget");
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+ 
+-add_task(function* () {
+-  let [host,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [host,, doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Checking that the graph markup is created in the parent");
+   let container = doc.querySelector("#cubic-bezier-container");
+   let w = new CubicBezierWidget(container);
+ 
+   ok(container.querySelector(".display-wrap"),
+     "The display has been added");
+ 
+diff --git a/devtools/client/shared/test/browser_cubic-bezier-02.js b/devtools/client/shared/test/browser_cubic-bezier-02.js
+--- a/devtools/client/shared/test/browser_cubic-bezier-02.js
++++ b/devtools/client/shared/test/browser_cubic-bezier-02.js
+@@ -9,185 +9,185 @@
+ const {CubicBezierWidget} =
+   require("devtools/client/shared/widgets/CubicBezierWidget");
+ const {PREDEFINED} = require("devtools/client/shared/widgets/CubicBezierPresets");
+ 
+ // In this test we have to use a slightly more complete HTML tree, with <body>
+ // in order to remove its margin and prevent shifted positions
+ const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-02.html";
+ 
+-add_task(function* () {
+-  let [host, win, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [host, win, doc] = await createHost("bottom", TEST_URI);
+ 
+   // Required or widget will be clipped inside of 'bottom'
+   // host by -14. Setting `fixed` zeroes this which is needed for
+   // calculating offsets. Occurs in test env only.
+   doc.body.setAttribute("style", "position: fixed; margin: 0;");
+ 
+   let container = doc.querySelector("#cubic-bezier-container");
+   let w = new CubicBezierWidget(container, PREDEFINED.linear);
+ 
+   let rect = w.curve.getBoundingClientRect();
+   rect.graphTop = rect.height * w.bezierCanvas.padding[0];
+   rect.graphBottom = rect.height - rect.graphTop;
+   rect.graphHeight = rect.graphBottom - rect.graphTop;
+ 
+-  yield pointsCanBeDragged(w, win, doc, rect);
+-  yield curveCanBeClicked(w, win, doc, rect);
+-  yield pointsCanBeMovedWithKeyboard(w, win, doc, rect);
++  await pointsCanBeDragged(w, win, doc, rect);
++  await curveCanBeClicked(w, win, doc, rect);
++  await pointsCanBeMovedWithKeyboard(w, win, doc, rect);
+ 
+   w.destroy();
+   host.destroy();
+ });
+ 
+-function* pointsCanBeDragged(widget, win, doc, offsets) {
++async function pointsCanBeDragged(widget, win, doc, offsets) {
+   info("Checking that the control points can be dragged with the mouse");
+ 
+   info("Listening for the update event");
+   let onUpdated = widget.once("updated");
+ 
+   info("Generating a mousedown/move/up on P1");
+   widget._onPointMouseDown({target: widget.p1});
+   doc.onmousemove({pageX: offsets.left, pageY: offsets.graphTop});
+   doc.onmouseup();
+ 
+-  let bezier = yield onUpdated;
++  let bezier = await onUpdated;
+   ok(true, "The widget fired the updated event");
+   ok(bezier, "The updated event contains a bezier argument");
+   is(bezier.P1[0], 0, "The new P1 time coordinate is correct");
+   is(bezier.P1[1], 1, "The new P1 progress coordinate is correct");
+ 
+   info("Listening for the update event");
+   onUpdated = widget.once("updated");
+ 
+   info("Generating a mousedown/move/up on P2");
+   widget._onPointMouseDown({target: widget.p2});
+   doc.onmousemove({pageX: offsets.right, pageY: offsets.graphBottom});
+   doc.onmouseup();
+ 
+-  bezier = yield onUpdated;
++  bezier = await onUpdated;
+   is(bezier.P2[0], 1, "The new P2 time coordinate is correct");
+   is(bezier.P2[1], 0, "The new P2 progress coordinate is correct");
+ }
+ 
+-function* curveCanBeClicked(widget, win, doc, offsets) {
++async function curveCanBeClicked(widget, win, doc, offsets) {
+   info("Checking that clicking on the curve moves the closest control point");
+ 
+   info("Listening for the update event");
+   let onUpdated = widget.once("updated");
+ 
+   info("Click close to P1");
+   let x = offsets.left + (offsets.width / 4.0);
+   let y = offsets.graphTop + (offsets.graphHeight / 4.0);
+   widget._onCurveClick({pageX: x, pageY: y});
+ 
+-  let bezier = yield onUpdated;
++  let bezier = await onUpdated;
+   ok(true, "The widget fired the updated event");
+   is(bezier.P1[0], 0.25, "The new P1 time coordinate is correct");
+   is(bezier.P1[1], 0.75, "The new P1 progress coordinate is correct");
+   is(bezier.P2[0], 1, "P2 time coordinate remained unchanged");
+   is(bezier.P2[1], 0, "P2 progress coordinate remained unchanged");
+ 
+   info("Listening for the update event");
+   onUpdated = widget.once("updated");
+ 
+   info("Click close to P2");
+   x = offsets.right - (offsets.width / 4);
+   y = offsets.graphBottom - (offsets.graphHeight / 4);
+   widget._onCurveClick({pageX: x, pageY: y});
+ 
+-  bezier = yield onUpdated;
++  bezier = await onUpdated;
+   is(bezier.P2[0], 0.75, "The new P2 time coordinate is correct");
+   is(bezier.P2[1], 0.25, "The new P2 progress coordinate is correct");
+   is(bezier.P1[0], 0.25, "P1 time coordinate remained unchanged");
+   is(bezier.P1[1], 0.75, "P1 progress coordinate remained unchanged");
+ }
+ 
+-function* pointsCanBeMovedWithKeyboard(widget, win, doc, offsets) {
++async function pointsCanBeMovedWithKeyboard(widget, win, doc, offsets) {
+   info("Checking that points respond to keyboard events");
+ 
+   let singleStep = 3;
+   let shiftStep = 30;
+ 
+   info("Moving P1 to the left");
+   let newOffset = parseInt(widget.p1.style.left, 10) - singleStep;
+   let x = widget.bezierCanvas
+           .offsetsToCoordinates({style: {left: newOffset}})[0];
+ 
+   let onUpdated = widget.once("updated");
+   widget._onPointKeyDown(getKeyEvent(widget.p1, 37));
+-  let bezier = yield onUpdated;
++  let bezier = await onUpdated;
+ 
+   is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+   is(bezier.P1[1], 0.75, "The new P1 progress coordinate is correct");
+ 
+   info("Moving P1 to the left, fast");
+   newOffset = parseInt(widget.p1.style.left, 10) - shiftStep;
+   x = widget.bezierCanvas
+       .offsetsToCoordinates({style: {left: newOffset}})[0];
+ 
+   onUpdated = widget.once("updated");
+   widget._onPointKeyDown(getKeyEvent(widget.p1, 37, true));
+-  bezier = yield onUpdated;
++  bezier = await onUpdated;
+   is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+   is(bezier.P1[1], 0.75, "The new P1 progress coordinate is correct");
+ 
+   info("Moving P1 to the right, fast");
+   newOffset = parseInt(widget.p1.style.left, 10) + shiftStep;
+   x = widget.bezierCanvas
+     .offsetsToCoordinates({style: {left: newOffset}})[0];
+ 
+   onUpdated = widget.once("updated");
+   widget._onPointKeyDown(getKeyEvent(widget.p1, 39, true));
+-  bezier = yield onUpdated;
++  bezier = await onUpdated;
+   is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+   is(bezier.P1[1], 0.75, "The new P1 progress coordinate is correct");
+ 
+   info("Moving P1 to the bottom");
+   newOffset = parseInt(widget.p1.style.top, 10) + singleStep;
+   let y = widget.bezierCanvas
+     .offsetsToCoordinates({style: {top: newOffset}})[1];
+ 
+   onUpdated = widget.once("updated");
+   widget._onPointKeyDown(getKeyEvent(widget.p1, 40));
+-  bezier = yield onUpdated;
++  bezier = await onUpdated;
+   is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+   is(bezier.P1[1], y, "The new P1 progress coordinate is correct");
+ 
+   info("Moving P1 to the bottom, fast");
+   newOffset = parseInt(widget.p1.style.top, 10) + shiftStep;
+   y = widget.bezierCanvas
+     .offsetsToCoordinates({style: {top: newOffset}})[1];
+ 
+   onUpdated = widget.once("updated");
+   widget._onPointKeyDown(getKeyEvent(widget.p1, 40, true));
+-  bezier = yield onUpdated;
++  bezier = await onUpdated;
+   is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+   is(bezier.P1[1], y, "The new P1 progress coordinate is correct");
+ 
+   info("Moving P1 to the top, fast");
+   newOffset = parseInt(widget.p1.style.top, 10) - shiftStep;
+   y = widget.bezierCanvas
+     .offsetsToCoordinates({style: {top: newOffset}})[1];
+ 
+   onUpdated = widget.once("updated");
+   widget._onPointKeyDown(getKeyEvent(widget.p1, 38, true));
+-  bezier = yield onUpdated;
++  bezier = await onUpdated;
+   is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+   is(bezier.P1[1], y, "The new P1 progress coordinate is correct");
+ 
+   info("Checking that keyboard events also work with P2");
+   info("Moving P2 to the left");
+   newOffset = parseInt(widget.p2.style.left, 10) - singleStep;
+   x = widget.bezierCanvas
+     .offsetsToCoordinates({style: {left: newOffset}})[0];
+ 
+   onUpdated = widget.once("updated");
+   widget._onPointKeyDown(getKeyEvent(widget.p2, 37));
+-  bezier = yield onUpdated;
++  bezier = await onUpdated;
+   is(bezier.P2[0], x, "The new P2 time coordinate is correct");
+   is(bezier.P2[1], 0.25, "The new P2 progress coordinate is correct");
+ }
+ 
+ function getKeyEvent(target, keyCode, shift = false) {
+   return {
+     target: target,
+     keyCode: keyCode,
+diff --git a/devtools/client/shared/test/browser_cubic-bezier-03.js b/devtools/client/shared/test/browser_cubic-bezier-03.js
+--- a/devtools/client/shared/test/browser_cubic-bezier-03.js
++++ b/devtools/client/shared/test/browser_cubic-bezier-03.js
+@@ -7,62 +7,62 @@
+ // Tests that coordinates can be changed programatically in the CubicBezierWidget
+ 
+ const {CubicBezierWidget} =
+   require("devtools/client/shared/widgets/CubicBezierWidget");
+ const {PREDEFINED} = require("devtools/client/shared/widgets/CubicBezierPresets");
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+ 
+-add_task(function* () {
+-  let [host,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [host,, doc] = await createHost("bottom", TEST_URI);
+ 
+   let container = doc.querySelector("#cubic-bezier-container");
+   let w = new CubicBezierWidget(container, PREDEFINED.linear);
+ 
+-  yield coordinatesCanBeChangedByProvidingAnArray(w);
+-  yield coordinatesCanBeChangedByProvidingAValue(w);
++  await coordinatesCanBeChangedByProvidingAnArray(w);
++  await coordinatesCanBeChangedByProvidingAValue(w);
+ 
+   w.destroy();
+   host.destroy();
+ });
+ 
+-function* coordinatesCanBeChangedByProvidingAnArray(widget) {
++async function coordinatesCanBeChangedByProvidingAnArray(widget) {
+   info("Listening for the update event");
+   let onUpdated = widget.once("updated");
+ 
+   info("Setting new coordinates");
+   widget.coordinates = [0, 1, 1, 0];
+ 
+-  let bezier = yield onUpdated;
++  let bezier = await onUpdated;
+   ok(true, "The updated event was fired as a result of setting coordinates");
+ 
+   is(bezier.P1[0], 0, "The new P1 time coordinate is correct");
+   is(bezier.P1[1], 1, "The new P1 progress coordinate is correct");
+   is(bezier.P2[0], 1, "The new P2 time coordinate is correct");
+   is(bezier.P2[1], 0, "The new P2 progress coordinate is correct");
+ }
+ 
+-function* coordinatesCanBeChangedByProvidingAValue(widget) {
++async function coordinatesCanBeChangedByProvidingAValue(widget) {
+   info("Listening for the update event");
+   let onUpdated = widget.once("updated");
+ 
+   info("Setting linear css value");
+   widget.cssCubicBezierValue = "linear";
+-  let bezier = yield onUpdated;
++  let bezier = await onUpdated;
+   ok(true, "The updated event was fired as a result of setting cssValue");
+ 
+   is(bezier.P1[0], 0, "The new P1 time coordinate is correct");
+   is(bezier.P1[1], 0, "The new P1 progress coordinate is correct");
+   is(bezier.P2[0], 1, "The new P2 time coordinate is correct");
+   is(bezier.P2[1], 1, "The new P2 progress coordinate is correct");
+ 
+   info("Setting a custom cubic-bezier css value");
+   onUpdated = widget.once("updated");
+   widget.cssCubicBezierValue = "cubic-bezier(.25,-0.5, 1, 1.25)";
+-  bezier = yield onUpdated;
++  bezier = await onUpdated;
+   ok(true, "The updated event was fired as a result of setting cssValue");
+ 
+   is(bezier.P1[0], .25, "The new P1 time coordinate is correct");
+   is(bezier.P1[1], -.5, "The new P1 progress coordinate is correct");
+   is(bezier.P2[0], 1, "The new P2 time coordinate is correct");
+   is(bezier.P2[1], 1.25, "The new P2 progress coordinate is correct");
+ }
+diff --git a/devtools/client/shared/test/browser_cubic-bezier-04.js b/devtools/client/shared/test/browser_cubic-bezier-04.js
+--- a/devtools/client/shared/test/browser_cubic-bezier-04.js
++++ b/devtools/client/shared/test/browser_cubic-bezier-04.js
+@@ -7,18 +7,18 @@
+ // Tests that the CubicBezierPresetWidget generates markup.
+ 
+ const {CubicBezierPresetWidget} =
+   require("devtools/client/shared/widgets/CubicBezierWidget");
+ const {PRESETS} = require("devtools/client/shared/widgets/CubicBezierPresets");
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+ 
+-add_task(function* () {
+-  let [host,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [host,, doc] = await createHost("bottom", TEST_URI);
+ 
+   let container = doc.querySelector("#cubic-bezier-container");
+   let w = new CubicBezierPresetWidget(container);
+ 
+   info("Checking that the presets are created in the parent");
+   ok(container.querySelector(".preset-pane"),
+      "The preset pane has been added");
+ 
+diff --git a/devtools/client/shared/test/browser_cubic-bezier-05.js b/devtools/client/shared/test/browser_cubic-bezier-05.js
+--- a/devtools/client/shared/test/browser_cubic-bezier-05.js
++++ b/devtools/client/shared/test/browser_cubic-bezier-05.js
+@@ -8,18 +8,18 @@
+ 
+ const {CubicBezierPresetWidget} =
+   require("devtools/client/shared/widgets/CubicBezierWidget");
+ const {PREDEFINED, PRESETS, DEFAULT_PRESET_CATEGORY} =
+   require("devtools/client/shared/widgets/CubicBezierPresets");
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+ 
+-add_task(function* () {
+-  let [host,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [host,, doc] = await createHost("bottom", TEST_URI);
+ 
+   let container = doc.querySelector("#cubic-bezier-container");
+   let w = new CubicBezierPresetWidget(container);
+ 
+   info("Checking that preset is selected if coordinates are known");
+ 
+   w.refreshMenu([0, 0, 0, 0]);
+   is(w.activeCategory, container.querySelector(`#${DEFAULT_PRESET_CATEGORY}`),
+diff --git a/devtools/client/shared/test/browser_cubic-bezier-06.js b/devtools/client/shared/test/browser_cubic-bezier-06.js
+--- a/devtools/client/shared/test/browser_cubic-bezier-06.js
++++ b/devtools/client/shared/test/browser_cubic-bezier-06.js
+@@ -8,35 +8,35 @@
+ // Tests the integration between CubicBezierWidget and CubicBezierPresets
+ 
+ const {CubicBezierWidget} =
+   require("devtools/client/shared/widgets/CubicBezierWidget");
+ const {PRESETS} = require("devtools/client/shared/widgets/CubicBezierPresets");
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+ 
+-add_task(function* () {
+-  let [host, win, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [host, win, doc] = await createHost("bottom", TEST_URI);
+ 
+   let container = doc.querySelector("#cubic-bezier-container");
+   let w = new CubicBezierWidget(container,
+               PRESETS["ease-in"]["ease-in-sine"]);
+   w.presets.refreshMenu(PRESETS["ease-in"]["ease-in-sine"]);
+ 
+   let rect = w.curve.getBoundingClientRect();
+   rect.graphTop = rect.height * w.bezierCanvas.padding[0];
+ 
+-  yield adjustingBezierUpdatesPreset(w, win, doc, rect);
+-  yield selectingPresetUpdatesBezier(w, win, doc, rect);
++  await adjustingBezierUpdatesPreset(w, win, doc, rect);
++  await selectingPresetUpdatesBezier(w, win, doc, rect);
+ 
+   w.destroy();
+   host.destroy();
+ });
+ 
+-function* adjustingBezierUpdatesPreset(widget, win, doc, rect) {
++function adjustingBezierUpdatesPreset(widget, win, doc, rect) {
+   info("Checking that changing the bezier refreshes the preset menu");
+ 
+   is(widget.presets.activeCategory,
+      doc.querySelector("#ease-in"),
+      "The selected category is ease-in");
+ 
+   is(widget.presets._activePreset,
+      doc.querySelector("#ease-in-sine"),
+@@ -50,30 +50,30 @@ function* adjustingBezierUpdatesPreset(w
+   is(widget.presets.activeCategory,
+      doc.querySelector("#ease-in"),
+      "The selected category is still ease-in");
+ 
+   is(widget.presets._activePreset, null,
+      "There is no active preset");
+ }
+ 
+-function* selectingPresetUpdatesBezier(widget, win, doc, rect) {
++async function selectingPresetUpdatesBezier(widget, win, doc, rect) {
+   info("Checking that selecting a preset updates bezier curve");
+ 
+   info("Listening for the new coordinates event");
+   let onNewCoordinates = widget.presets.once("new-coordinates");
+   let onUpdated = widget.once("updated");
+ 
+   info("Click a preset");
+   let preset = doc.querySelector("#ease-in-sine");
+   widget.presets._onPresetClick({currentTarget: preset});
+ 
+-  yield onNewCoordinates;
++  await onNewCoordinates;
+   ok(true, "The preset widget fired the new-coordinates event");
+ 
+-  let bezier = yield onUpdated;
++  let bezier = await onUpdated;
+   ok(true, "The bezier canvas fired the updated event");
+ 
+   is(bezier.P1[0], preset.coordinates[0], "The new P1 time coordinate is correct");
+   is(bezier.P1[1], preset.coordinates[1], "The new P1 progress coordinate is correct");
+   is(bezier.P2[0], preset.coordinates[2], "P2 time coordinate is correct ");
+   is(bezier.P2[1], preset.coordinates[3], "P2 progress coordinate is correct");
+ }
+diff --git a/devtools/client/shared/test/browser_cubic-bezier-07.js b/devtools/client/shared/test/browser_cubic-bezier-07.js
+--- a/devtools/client/shared/test/browser_cubic-bezier-07.js
++++ b/devtools/client/shared/test/browser_cubic-bezier-07.js
+@@ -7,36 +7,36 @@
+ // Tests that changing the cubic-bezier curve in the widget does change the dot animation
+ // preview too.
+ 
+ const {CubicBezierWidget} = require("devtools/client/shared/widgets/CubicBezierWidget");
+ const {PREDEFINED} = require("devtools/client/shared/widgets/CubicBezierPresets");
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+ 
+-add_task(function* () {
+-  let [host,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [host,, doc] = await createHost("bottom", TEST_URI);
+ 
+   let container = doc.querySelector("#cubic-bezier-container");
+   let w = new CubicBezierWidget(container, PREDEFINED.linear);
+ 
+-  yield previewDotReactsToChanges(w, [0.6, -0.28, 0.74, 0.05]);
+-  yield previewDotReactsToChanges(w, [0.9, 0.03, 0.69, 0.22]);
+-  yield previewDotReactsToChanges(w, [0.68, -0.55, 0.27, 1.55]);
+-  yield previewDotReactsToChanges(w, PREDEFINED.ease, "ease");
+-  yield previewDotReactsToChanges(w, PREDEFINED["ease-in-out"], "ease-in-out");
++  await previewDotReactsToChanges(w, [0.6, -0.28, 0.74, 0.05]);
++  await previewDotReactsToChanges(w, [0.9, 0.03, 0.69, 0.22]);
++  await previewDotReactsToChanges(w, [0.68, -0.55, 0.27, 1.55]);
++  await previewDotReactsToChanges(w, PREDEFINED.ease, "ease");
++  await previewDotReactsToChanges(w, PREDEFINED["ease-in-out"], "ease-in-out");
+ 
+   w.destroy();
+   host.destroy();
+ });
+ 
+-function* previewDotReactsToChanges(widget, coords, expectedEasing) {
++async function previewDotReactsToChanges(widget, coords, expectedEasing) {
+   let onUpdated = widget.once("updated");
+   widget.coordinates = coords;
+-  yield onUpdated;
++  await onUpdated;
+ 
+   let animatedDot = widget.timingPreview.dot;
+   let animations = animatedDot.getAnimations();
+ 
+   if (!expectedEasing) {
+     expectedEasing =
+       `cubic-bezier(${coords[0]}, ${coords[1]}, ${coords[2]}, ${coords[3]})`;
+   }
+diff --git a/devtools/client/shared/test/browser_devices.js b/devtools/client/shared/test/browser_devices.js
+--- a/devtools/client/shared/test/browser_devices.js
++++ b/devtools/client/shared/test/browser_devices.js
+@@ -4,21 +4,21 @@
+ "use strict";
+ 
+ const {
+   getDevices,
+   getDeviceString,
+   addDevice
+ } = require("devtools/client/shared/devices");
+ 
+-add_task(function* () {
++add_task(async function() {
+   Services.prefs.setCharPref("devtools.devices.url",
+                              TEST_URI_ROOT + "browser_devices.json");
+ 
+-  let devices = yield getDevices();
++  let devices = await getDevices();
+ 
+   is(devices.TYPES.length, 1, "Found 1 device type.");
+ 
+   let type1 = devices.TYPES[0];
+ 
+   is(devices[type1].length, 2, "Found 2 devices of type #1.");
+ 
+   let string = getDeviceString(type1);
+@@ -29,29 +29,29 @@ add_task(function* () {
+     width: 320,
+     height: 320,
+     pixelRatio: 2,
+     userAgent: "Mozilla/5.0 (Mobile; rv:42.0)",
+     touch: true,
+     firefoxOS: true
+   };
+   addDevice(device1, type1);
+-  devices = yield getDevices();
++  devices = await getDevices();
+ 
+   is(devices[type1].length, 3, "Added new device of type #1.");
+   ok(devices[type1].filter(d => d.name === device1.name), "Found the new device.");
+ 
+   let type2 = "appliances";
+   let device2 = {
+     name: "Mr Freezer",
+     width: 800,
+     height: 600,
+     pixelRatio: 5,
+     userAgent: "Mozilla/5.0 (Appliance; rv:42.0)",
+     touch: true,
+     firefoxOS: true
+   };
+   addDevice(device2, type2);
+-  devices = yield getDevices();
++  devices = await getDevices();
+ 
+   is(devices.TYPES.length, 2, "Added device type #2.");
+   is(devices[type2].length, 1, "Added new device of type #2.");
+ });
+diff --git a/devtools/client/shared/test/browser_filter-editor-01.js b/devtools/client/shared/test/browser_filter-editor-01.js
+--- a/devtools/client/shared/test/browser_filter-editor-01.js
++++ b/devtools/client/shared/test/browser_filter-editor-01.js
+@@ -20,18 +20,18 @@ function verifyURL(string) {
+   let token = lexer.nextToken();
+   if (!token || token.tokenType !== "url") {
+     return false;
+   }
+ 
+   return lexer.nextToken() === null;
+ }
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
+ 
+   info("Test parsing of a valid CSS Filter value");
+   widget.setCssValue("blur(2px) contrast(200%)");
+   is(widget.getCssValue(),
+diff --git a/devtools/client/shared/test/browser_filter-editor-02.js b/devtools/client/shared/test/browser_filter-editor-02.js
+--- a/devtools/client/shared/test/browser_filter-editor-02.js
++++ b/devtools/client/shared/test/browser_filter-editor-02.js
+@@ -9,18 +9,18 @@ const {CSSFilterEditorWidget} = require(
+ const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
+ 
+ const { LocalizationHelper } = require("devtools/shared/l10n");
+ const STRINGS_URI = "devtools/client/locales/filterwidget.properties";
+ const L10N = new LocalizationHelper(STRINGS_URI);
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const TEST_DATA = [
+     {
+       cssValue: "blur(2px) contrast(200%) hue-rotate(20.2deg) drop-shadow(5px 5px black)",
+       expected: [
+         {
+           label: "blur",
+diff --git a/devtools/client/shared/test/browser_filter-editor-03.js b/devtools/client/shared/test/browser_filter-editor-03.js
+--- a/devtools/client/shared/test/browser_filter-editor-03.js
++++ b/devtools/client/shared/test/browser_filter-editor-03.js
+@@ -7,18 +7,18 @@
+ 
+ const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+ const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
+ const GRAYSCALE_MAX = 100;
+ const INVERT_MIN = 0;
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
+ 
+   info("Test add method");
+   const blur = widget.add("blur", "10.2px");
+   is(widget.getCssValue(), "blur(10.2px)",
+diff --git a/devtools/client/shared/test/browser_filter-editor-04.js b/devtools/client/shared/test/browser_filter-editor-04.js
+--- a/devtools/client/shared/test/browser_filter-editor-04.js
++++ b/devtools/client/shared/test/browser_filter-editor-04.js
+@@ -6,18 +6,18 @@
+ // Tests the Filter Editor Widget's drag-drop re-ordering
+ 
+ const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+ const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
+ const LIST_ITEM_HEIGHT = 32;
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   const initialValue = "blur(2px) contrast(200%) brightness(200%)";
+   let widget = new CSSFilterEditorWidget(container, initialValue, cssIsValid);
+ 
+   const filters = widget.el.querySelector("#filters");
+   function first() {
+diff --git a/devtools/client/shared/test/browser_filter-editor-05.js b/devtools/client/shared/test/browser_filter-editor-05.js
+--- a/devtools/client/shared/test/browser_filter-editor-05.js
++++ b/devtools/client/shared/test/browser_filter-editor-05.js
+@@ -14,18 +14,18 @@ const FAST_VALUE_MULTIPLIER = 10;
+ const SLOW_VALUE_MULTIPLIER = 0.1;
+ const DEFAULT_VALUE_MULTIPLIER = 1;
+ 
+ const GRAYSCALE_MAX = 100,
+   GRAYSCALE_MIN = 0;
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   let widget = new CSSFilterEditorWidget(
+     container, "grayscale(0%) url(test.svg)", cssIsValid
+   );
+ 
+   const filters = widget.el.querySelector("#filters");
+diff --git a/devtools/client/shared/test/browser_filter-editor-06.js b/devtools/client/shared/test/browser_filter-editor-06.js
+--- a/devtools/client/shared/test/browser_filter-editor-06.js
++++ b/devtools/client/shared/test/browser_filter-editor-06.js
+@@ -9,18 +9,18 @@ const {CSSFilterEditorWidget} = require(
+ const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
+ 
+ const { LocalizationHelper } = require("devtools/shared/l10n");
+ const STRINGS_URI = "devtools/client/locales/filterwidget.properties";
+ const L10N = new LocalizationHelper(STRINGS_URI);
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
+ 
+   const select = widget.el.querySelector("select"),
+     add = widget.el.querySelector("#add-filter");
+ 
+diff --git a/devtools/client/shared/test/browser_filter-editor-07.js b/devtools/client/shared/test/browser_filter-editor-07.js
+--- a/devtools/client/shared/test/browser_filter-editor-07.js
++++ b/devtools/client/shared/test/browser_filter-editor-07.js
+@@ -5,18 +5,18 @@
+ 
+ // Tests the Filter Editor Widget's remove button
+ 
+ const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+ const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   let widget = new CSSFilterEditorWidget(
+     container, "blur(2px) contrast(200%)", cssIsValid
+   );
+ 
+   info("Test removing filters with remove button");
+diff --git a/devtools/client/shared/test/browser_filter-editor-08.js b/devtools/client/shared/test/browser_filter-editor-08.js
+--- a/devtools/client/shared/test/browser_filter-editor-08.js
++++ b/devtools/client/shared/test/browser_filter-editor-08.js
+@@ -10,18 +10,18 @@ const {CSSFilterEditorWidget} = require(
+ const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
+ 
+ const FAST_VALUE_MULTIPLIER = 10;
+ const SLOW_VALUE_MULTIPLIER = 0.1;
+ const DEFAULT_VALUE_MULTIPLIER = 1;
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   const initialValue = "blur(2px)";
+   let widget = new CSSFilterEditorWidget(container, initialValue, cssIsValid);
+ 
+   let value = 2;
+ 
+diff --git a/devtools/client/shared/test/browser_filter-editor-09.js b/devtools/client/shared/test/browser_filter-editor-09.js
+--- a/devtools/client/shared/test/browser_filter-editor-09.js
++++ b/devtools/client/shared/test/browser_filter-editor-09.js
+@@ -10,18 +10,18 @@ const {CSSFilterEditorWidget} = require(
+ const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
+ 
+ const FAST_VALUE_MULTIPLIER = 10;
+ const SLOW_VALUE_MULTIPLIER = 0.1;
+ const DEFAULT_VALUE_MULTIPLIER = 1;
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   const initialValue = "drop-shadow(rgb(0, 0, 0) 1px 1px 0px)";
+   let widget = new CSSFilterEditorWidget(container, initialValue, cssIsValid);
+   widget.el.querySelector("#filters input").setSelectionRange(13, 13);
+ 
+   let value = 1;
+diff --git a/devtools/client/shared/test/browser_filter-editor-10.js b/devtools/client/shared/test/browser_filter-editor-10.js
+--- a/devtools/client/shared/test/browser_filter-editor-10.js
++++ b/devtools/client/shared/test/browser_filter-editor-10.js
+@@ -8,18 +8,18 @@
+ 
+ const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+ const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
+ 
+ const DEFAULT_VALUE_MULTIPLIER = 1;
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   const initialValue = "drop-shadow(rgb(0, 0, 0) 10px 1px 0px)";
+   let widget = new CSSFilterEditorWidget(container, initialValue, cssIsValid);
+   const input = widget.el.querySelector("#filters input");
+ 
+   let value = 10;
+diff --git a/devtools/client/shared/test/browser_filter-presets-01.js b/devtools/client/shared/test/browser_filter-presets-01.js
+--- a/devtools/client/shared/test/browser_filter-presets-01.js
++++ b/devtools/client/shared/test/browser_filter-presets-01.js
+@@ -5,86 +5,86 @@
+ 
+ // Tests saving presets
+ 
+ const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+ const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
+   // First render
+-  yield widget.once("render");
++  await widget.once("render");
+ 
+   const VALUE = "blur(2px) contrast(150%)";
+   const NAME = "Test";
+ 
+-  yield showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
++  await showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
+ 
+   let preset = widget.el.querySelector(".preset");
+   is(preset.querySelector("label").textContent, NAME,
+      "Should show preset name correctly");
+   is(preset.querySelector("span").textContent, VALUE,
+      "Should show preset value preview correctly");
+ 
+-  let list = yield widget.getPresets();
++  let list = await widget.getPresets();
+   let input = widget.el.querySelector(".presets-list .footer input");
+   let data = list[0];
+ 
+   is(data.name, NAME,
+      "Should add the preset to asyncStorage - name property");
+   is(data.value, VALUE,
+      "Should add the preset to asyncStorage - name property");
+ 
+   info("Test overriding preset by using the same name");
+ 
+   const VALUE_2 = "saturate(50%) brightness(10%)";
+ 
+   widget.setCssValue(VALUE_2);
+ 
+-  yield savePreset(widget);
++  await savePreset(widget);
+ 
+   is(widget.el.querySelectorAll(".preset").length, 1,
+      "Should override the preset with the same name - render");
+ 
+-  list = yield widget.getPresets();
++  list = await widget.getPresets();
+   data = list[0];
+ 
+   is(list.length, 1,
+      "Should override the preset with the same name - asyncStorage");
+ 
+   is(data.name, NAME,
+      "Should override the preset with the same name - prop name");
+   is(data.value, VALUE_2,
+      "Should override the preset with the same name - prop value");
+ 
+-  yield widget.setPresets([]);
++  await widget.setPresets([]);
+ 
+   info("Test saving a preset without name");
+   input.value = "";
+ 
+-  yield savePreset(widget, "preset-save-error");
++  await savePreset(widget, "preset-save-error");
+ 
+-  list = yield widget.getPresets();
++  list = await widget.getPresets();
+   is(list.length, 0,
+      "Should not add a preset without name");
+ 
+   info("Test saving a preset without filters");
+ 
+   input.value = NAME;
+   widget.setCssValue("none");
+ 
+-  yield savePreset(widget, "preset-save-error");
++  await savePreset(widget, "preset-save-error");
+ 
+-  list = yield widget.getPresets();
++  list = await widget.getPresets();
+   is(list.length, 0,
+      "Should not add a preset without filters (value: none)");
+ });
+ 
+ /**
+  * Call savePreset on widget and wait for the specified event to emit
+  * @param {CSSFilterWidget} widget
+  * @param {string} expectEvent="render" The event to listen on
+diff --git a/devtools/client/shared/test/browser_filter-presets-02.js b/devtools/client/shared/test/browser_filter-presets-02.js
+--- a/devtools/client/shared/test/browser_filter-presets-02.js
++++ b/devtools/client/shared/test/browser_filter-presets-02.js
+@@ -5,41 +5,41 @@
+ 
+ // Tests loading presets
+ 
+ const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+ const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
+   // First render
+-  yield widget.once("render");
++  await widget.once("render");
+ 
+   const VALUE = "blur(2px) contrast(150%)";
+   const NAME = "Test";
+ 
+-  yield showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
++  await showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
+ 
+   let onRender = widget.once("render");
+   // reset value
+   widget.setCssValue("saturate(100%) brightness(150%)");
+-  yield onRender;
++  await onRender;
+ 
+   let preset = widget.el.querySelector(".preset");
+ 
+   onRender = widget.once("render");
+   widget._presetClick({
+     target: preset
+   });
+ 
+-  yield onRender;
++  await onRender;
+ 
+   is(widget.getCssValue(), VALUE,
+      "Should set widget's value correctly");
+   is(widget.el.querySelector(".presets-list .footer input").value, NAME,
+      "Should set input's value to name");
+ });
+diff --git a/devtools/client/shared/test/browser_filter-presets-03.js b/devtools/client/shared/test/browser_filter-presets-03.js
+--- a/devtools/client/shared/test/browser_filter-presets-03.js
++++ b/devtools/client/shared/test/browser_filter-presets-03.js
+@@ -5,36 +5,36 @@
+ 
+ // Tests deleting presets
+ 
+ const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+ const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   const cssIsValid = getClientCssProperties().getValidityChecker(doc);
+ 
+   const container = doc.querySelector("#filter-container");
+   let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
+   // First render
+-  yield widget.once("render");
++  await widget.once("render");
+ 
+   const NAME = "Test";
+   const VALUE = "blur(2px) contrast(150%)";
+ 
+-  yield showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
++  await showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
+ 
+   let removeButton = widget.el.querySelector(".preset .remove-button");
+   let onRender = widget.once("render");
+   widget._presetClick({
+     target: removeButton
+   });
+ 
+-  yield onRender;
++  await onRender;
+   is(widget.el.querySelector(".preset"), null,
+      "Should re-render after removing preset");
+ 
+-  let list = yield widget.getPresets();
++  let list = await widget.getPresets();
+   is(list.length, 0,
+      "Should remove presets from asyncStorage");
+ });
+diff --git a/devtools/client/shared/test/browser_flame-graph-01.js b/devtools/client/shared/test/browser_flame-graph-01.js
+--- a/devtools/client/shared/test/browser_flame-graph-01.js
++++ b/devtools/client/shared/test/browser_flame-graph-01.js
+@@ -2,40 +2,40 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // Tests that flame graph widget works properly.
+ 
+ const {FlameGraph} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new FlameGraph(doc.body);
+ 
+   let readyEventEmitted;
+   graph.once("ready", () => {
+     readyEventEmitted = true;
+   });
+ 
+-  yield graph.ready();
++  await graph.ready();
+   ok(readyEventEmitted, "The 'ready' event should have been emitted");
+ 
+   testGraph(host, graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(host, graph) {
+   ok(graph._container.classList.contains("flame-graph-widget-container"),
+     "The correct graph container was created.");
+   ok(graph._canvas.classList.contains("flame-graph-widget-canvas"),
+     "The correct graph container was created.");
+diff --git a/devtools/client/shared/test/browser_flame-graph-02.js b/devtools/client/shared/test/browser_flame-graph-02.js
+--- a/devtools/client/shared/test/browser_flame-graph-02.js
++++ b/devtools/client/shared/test/browser_flame-graph-02.js
+@@ -2,35 +2,35 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // Tests that flame graph widgets may have a fixed width or height.
+ 
+ const {FlameGraph} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new FlameGraph(doc.body);
+   graph.fixedWidth = 200;
+   graph.fixedHeight = 100;
+ 
+-  yield graph.ready();
++  await graph.ready();
+   testGraph(host, graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(host, graph) {
+   let bounds = host.frame.getBoundingClientRect();
+ 
+   isnot(graph.width, bounds.width * window.devicePixelRatio,
+     "The graph should not span all the parent node's width.");
+diff --git a/devtools/client/shared/test/browser_flame-graph-03a.js b/devtools/client/shared/test/browser_flame-graph-03a.js
+--- a/devtools/client/shared/test/browser_flame-graph-03a.js
++++ b/devtools/client/shared/test/browser_flame-graph-03a.js
+@@ -21,38 +21,38 @@ const TEST_DATA = [
+   }
+ ];
+ const TEST_BOUNDS = { startTime: 0, endTime: 150 };
+ const TEST_WIDTH = 200;
+ const TEST_HEIGHT = 100;
+ 
+ const {FlameGraph} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new FlameGraph(doc.body, 1);
+   graph.fixedWidth = TEST_WIDTH;
+   graph.fixedHeight = TEST_HEIGHT;
+   graph.horizontalPanThreshold = 0;
+   graph.verticalPanThreshold = 0;
+ 
+-  yield graph.ready();
++  await graph.ready();
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   graph.setData({ data: TEST_DATA, bounds: TEST_BOUNDS });
+ 
+   is(graph.getViewRange().startTime, 0,
+     "The selection start boundary is correct (1).");
+diff --git a/devtools/client/shared/test/browser_flame-graph-03b.js b/devtools/client/shared/test/browser_flame-graph-03b.js
+--- a/devtools/client/shared/test/browser_flame-graph-03b.js
++++ b/devtools/client/shared/test/browser_flame-graph-03b.js
+@@ -22,36 +22,36 @@ const TEST_DATA = [
+ ];
+ const TEST_BOUNDS = { startTime: 0, endTime: 150 };
+ const TEST_WIDTH = 200;
+ const TEST_HEIGHT = 100;
+ const TEST_DPI_DENSITIY = 2;
+ 
+ var {FlameGraph} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new FlameGraph(doc.body, TEST_DPI_DENSITIY);
+   graph.fixedWidth = TEST_WIDTH;
+   graph.fixedHeight = TEST_HEIGHT;
+ 
+-  yield graph.ready();
++  await graph.ready();
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   graph.setData({ data: TEST_DATA, bounds: TEST_BOUNDS });
+ 
+   is(graph.getViewRange().startTime, 0,
+     "The selection start boundary is correct on HiDPI (1).");
+diff --git a/devtools/client/shared/test/browser_flame-graph-03c.js b/devtools/client/shared/test/browser_flame-graph-03c.js
+--- a/devtools/client/shared/test/browser_flame-graph-03c.js
++++ b/devtools/client/shared/test/browser_flame-graph-03c.js
+@@ -22,36 +22,36 @@ const TEST_DATA = [
+ ];
+ const TEST_BOUNDS = { startTime: 0, endTime: 150 };
+ const TEST_WIDTH = 200;
+ const TEST_HEIGHT = 100;
+ const TEST_DPI_DENSITIY = 2;
+ 
+ const {FlameGraph} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new FlameGraph(doc.body, TEST_DPI_DENSITIY);
+   graph.fixedWidth = TEST_WIDTH;
+   graph.fixedHeight = TEST_HEIGHT;
+ 
+-  yield graph.ready();
++  await graph.ready();
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   graph.setData({ data: TEST_DATA, bounds: TEST_BOUNDS });
+ 
+   // Drag up vertically only.
+ 
+diff --git a/devtools/client/shared/test/browser_flame-graph-04.js b/devtools/client/shared/test/browser_flame-graph-04.js
+--- a/devtools/client/shared/test/browser_flame-graph-04.js
++++ b/devtools/client/shared/test/browser_flame-graph-04.js
+@@ -6,30 +6,30 @@
+ // Tests that text metrics in the flame graph widget work properly.
+ 
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const {ELLIPSIS} = require("devtools/shared/l10n");
+ const {FlameGraph} = require("devtools/client/shared/widgets/FlameGraph");
+ const {FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE} = require("devtools/client/shared/widgets/FlameGraph");
+ const {FLAME_GRAPH_BLOCK_TEXT_FONT_FAMILY} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new FlameGraph(doc.body, 1);
+-  yield graph.ready();
++  await graph.ready();
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   is(graph._averageCharWidth, getAverageCharWidth(),
+     "The average char width was calculated correctly.");
+   is(graph._overflowCharWidth, getCharWidth(ELLIPSIS),
+     "The ellipsis char width was calculated correctly.");
+diff --git a/devtools/client/shared/test/browser_flame-graph-05.js b/devtools/client/shared/test/browser_flame-graph-05.js
+--- a/devtools/client/shared/test/browser_flame-graph-05.js
++++ b/devtools/client/shared/test/browser_flame-graph-05.js
+@@ -25,67 +25,67 @@ const TEST_BOUNDS = { startTime: 0, endT
+ const TEST_DPI_DENSITIY = 2;
+ 
+ const KEY_CODE_UP = 38;
+ const KEY_CODE_LEFT = 37;
+ const KEY_CODE_RIGHT = 39;
+ 
+ var {FlameGraph} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new FlameGraph(doc.body, TEST_DPI_DENSITIY);
+-  yield graph.ready();
++  await graph.ready();
+ 
+-  yield testGraph(host, graph);
++  await testGraph(host, graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(host, graph) {
++async function testGraph(host, graph) {
+   graph.setData({ data: TEST_DATA, bounds: TEST_BOUNDS });
+ 
+   is(graph._selection.start, 0,
+     "The graph's selection start value is initially correct.");
+   is(graph._selection.end, TEST_BOUNDS.endTime * TEST_DPI_DENSITIY,
+     "The graph's selection end value is initially correct.");
+ 
+-  yield pressKeyForTime(graph, KEY_CODE_LEFT, 1000);
++  await pressKeyForTime(graph, KEY_CODE_LEFT, 1000);
+ 
+   is(graph._selection.start, 0,
+     "The graph's selection start value is correct after pressing LEFT.");
+   ok(graph._selection.end < TEST_BOUNDS.endTime * TEST_DPI_DENSITIY,
+     "The graph's selection end value is correct after pressing LEFT.");
+ 
+   graph._selection.start = 0;
+   graph._selection.end = TEST_BOUNDS.endTime * TEST_DPI_DENSITIY;
+   info("Graph selection was reset (1).");
+ 
+-  yield pressKeyForTime(graph, KEY_CODE_RIGHT, 1000);
++  await pressKeyForTime(graph, KEY_CODE_RIGHT, 1000);
+ 
+   ok(graph._selection.start > 0,
+     "The graph's selection start value is correct after pressing RIGHT.");
+   is(graph._selection.end, TEST_BOUNDS.endTime * TEST_DPI_DENSITIY,
+     "The graph's selection end value is correct after pressing RIGHT.");
+ 
+   graph._selection.start = 0;
+   graph._selection.end = TEST_BOUNDS.endTime * TEST_DPI_DENSITIY;
+   info("Graph selection was reset (2).");
+ 
+-  yield pressKeyForTime(graph, KEY_CODE_UP, 1000);
++  await pressKeyForTime(graph, KEY_CODE_UP, 1000);
+ 
+   ok(graph._selection.start > 0,
+     "The graph's selection start value is correct after pressing UP.");
+   ok(graph._selection.end < TEST_BOUNDS.endTime * TEST_DPI_DENSITIY,
+     "The graph's selection end value is correct after pressing UP.");
+ 
+   let distanceLeft = graph._selection.start;
+   let distanceRight = TEST_BOUNDS.endTime * TEST_DPI_DENSITIY - graph._selection.end;
+diff --git a/devtools/client/shared/test/browser_flame-graph-utils-01.js b/devtools/client/shared/test/browser_flame-graph-utils-01.js
+--- a/devtools/client/shared/test/browser_flame-graph-utils-01.js
++++ b/devtools/client/shared/test/browser_flame-graph-utils-01.js
+@@ -4,23 +4,23 @@
+ "use strict";
+ 
+ // Tests that text metrics and data conversion from profiler samples
+ // widget work properly in the flame graph.
+ 
+ const {FlameGraphUtils} = require("devtools/client/shared/widgets/FlameGraph");
+ const {PALLETTE_SIZE} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
++function performTest() {
+   let out = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA);
+ 
+   ok(out, "Some data was outputted properly");
+   is(out.length, PALLETTE_SIZE, "The outputted length is correct.");
+ 
+   info("Got flame graph data:\n" + out.toSource() + "\n");
+ 
+   for (let i = 0; i < out.length; i++) {
+diff --git a/devtools/client/shared/test/browser_flame-graph-utils-02.js b/devtools/client/shared/test/browser_flame-graph-utils-02.js
+--- a/devtools/client/shared/test/browser_flame-graph-utils-02.js
++++ b/devtools/client/shared/test/browser_flame-graph-utils-02.js
+@@ -3,23 +3,23 @@
+ 
+ "use strict";
+ 
+ // Tests consecutive duplicate frames are removed from the flame graph data.
+ 
+ const {FlameGraphUtils} = require("devtools/client/shared/widgets/FlameGraph");
+ const {PALLETTE_SIZE} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
++function performTest() {
+   let out = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA, {
+     flattenRecursion: true
+   });
+ 
+   ok(out, "Some data was outputted properly");
+   is(out.length, PALLETTE_SIZE, "The outputted length is correct.");
+ 
+   info("Got flame graph data:\n" + out.toSource() + "\n");
+diff --git a/devtools/client/shared/test/browser_flame-graph-utils-03.js b/devtools/client/shared/test/browser_flame-graph-utils-03.js
+--- a/devtools/client/shared/test/browser_flame-graph-utils-03.js
++++ b/devtools/client/shared/test/browser_flame-graph-utils-03.js
+@@ -3,23 +3,23 @@
+ 
+ "use strict";
+ 
+ // Tests if platform frames are removed from the flame graph data.
+ 
+ const {FlameGraphUtils} = require("devtools/client/shared/widgets/FlameGraph");
+ const {PALLETTE_SIZE} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
++function performTest() {
+   let out = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA, {
+     contentOnly: true
+   });
+ 
+   ok(out, "Some data was outputted properly");
+   is(out.length, PALLETTE_SIZE, "The outputted length is correct.");
+ 
+   info("Got flame graph data:\n" + out.toSource() + "\n");
+diff --git a/devtools/client/shared/test/browser_flame-graph-utils-04.js b/devtools/client/shared/test/browser_flame-graph-utils-04.js
+--- a/devtools/client/shared/test/browser_flame-graph-utils-04.js
++++ b/devtools/client/shared/test/browser_flame-graph-utils-04.js
+@@ -3,23 +3,23 @@
+ 
+ "use strict";
+ 
+ // Tests if (idle) nodes are added when necessary in the flame graph data.
+ 
+ const {FlameGraphUtils} = require("devtools/client/shared/widgets/FlameGraph");
+ const {PALLETTE_SIZE} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
++function performTest() {
+   let out = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA, {
+     flattenRecursion: true,
+     contentOnly: true,
+     showIdleBlocks: "\m/"
+   });
+ 
+   ok(out, "Some data was outputted properly");
+   is(out.length, PALLETTE_SIZE, "The outputted length is correct.");
+diff --git a/devtools/client/shared/test/browser_flame-graph-utils-05.js b/devtools/client/shared/test/browser_flame-graph-utils-05.js
+--- a/devtools/client/shared/test/browser_flame-graph-utils-05.js
++++ b/devtools/client/shared/test/browser_flame-graph-utils-05.js
+@@ -2,23 +2,23 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // Tests that flame graph data is cached, and that the cache may be cleared.
+ 
+ const {FlameGraphUtils} = require("devtools/client/shared/widgets/FlameGraph");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
++function performTest() {
+   let out1 = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA);
+   let out2 = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA);
+   is(out1, out2, "The outputted data is identical.");
+ 
+   let out3 = FlameGraphUtils.createFlameGraphDataFromThread(
+     TEST_DATA, { flattenRecursion: true }
+   );
+   is(out2, out3, "The outputted data is still identical.");
+diff --git a/devtools/client/shared/test/browser_flame-graph-utils-06.js b/devtools/client/shared/test/browser_flame-graph-utils-06.js
+--- a/devtools/client/shared/test/browser_flame-graph-utils-06.js
++++ b/devtools/client/shared/test/browser_flame-graph-utils-06.js
+@@ -6,23 +6,23 @@
+ // Tests that the text displayed is the function name, file name and line number
+ // if applicable and demangling.
+ 
+ const {FlameGraphUtils} = require("devtools/client/shared/widgets/FlameGraph");
+ const {PALLETTE_SIZE} = require("devtools/client/shared/widgets/FlameGraph");
+ const MANGLED_FN = "__Z3FooIiEvv";
+ const UNMANGLED_FN = "void Foo<int>()";
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
++function performTest() {
+   let out = FlameGraphUtils.createFlameGraphDataFromThread(TEST_DATA, {
+     flattenRecursion: true
+   });
+ 
+   ok(out, "Some data was outputted properly");
+   is(out.length, PALLETTE_SIZE, "The outputted length is correct.");
+ 
+   info("Got flame graph data:\n" + out.toSource() + "\n");
+diff --git a/devtools/client/shared/test/browser_graphs-01.js b/devtools/client/shared/test/browser_graphs-01.js
+--- a/devtools/client/shared/test/browser_graphs-01.js
++++ b/devtools/client/shared/test/browser_graphs-01.js
+@@ -2,41 +2,41 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // Tests that graph widgets works properly.
+ 
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+   finish();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new LineGraphWidget(doc.body, "fps");
+ 
+   let readyEventEmitted;
+   graph.once("ready", () => {
+     readyEventEmitted = true;
+   });
+ 
+-  yield graph.ready();
++  await graph.ready();
+   ok(readyEventEmitted, "The 'ready' event should have been emitted");
+ 
+   testGraph(host, graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(host, graph) {
+   ok(graph._container.classList.contains("line-graph-widget-container"),
+     "The correct graph container was created.");
+   ok(graph._canvas.classList.contains("line-graph-widget-canvas"),
+     "The correct graph container was created.");
+diff --git a/devtools/client/shared/test/browser_graphs-02.js b/devtools/client/shared/test/browser_graphs-02.js
+--- a/devtools/client/shared/test/browser_graphs-02.js
++++ b/devtools/client/shared/test/browser_graphs-02.js
+@@ -25,31 +25,31 @@ const TEST_DATA = [
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const TEST_REGIONS = [{ start: 320, end: 460 }, { start: 780, end: 860 }];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   testDataAndRegions(graph);
+   testHighlights(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testDataAndRegions(graph) {
+   let thrown1;
+   try {
+     graph.setRegions(TEST_REGIONS);
+   } catch (e) {
+diff --git a/devtools/client/shared/test/browser_graphs-03.js b/devtools/client/shared/test/browser_graphs-03.js
+--- a/devtools/client/shared/test/browser_graphs-03.js
++++ b/devtools/client/shared/test/browser_graphs-03.js
+@@ -3,46 +3,46 @@
+ 
+ "use strict";
+ 
+ // Tests that graph widgets can handle clients getting/setting the
+ // selection or cursor.
+ 
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+-  yield testSelection(graph);
+-  yield testCursor(graph);
++  await testSelection(graph);
++  await testCursor(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testSelection(graph) {
++async function testSelection(graph) {
+   ok(graph.getSelection().start === null,
+     "The graph's selection should initially have a null start value.");
+   ok(graph.getSelection().end === null,
+     "The graph's selection should initially have a null end value.");
+   ok(!graph.hasSelection(),
+     "There shouldn't initially be any selection.");
+ 
+   let selected = graph.once("selecting");
+   graph.setSelection({ start: 100, end: 200 });
+ 
+-  yield selected;
++  await selected;
+   ok(true, "A 'selecting' event has been fired.");
+ 
+   ok(graph.hasSelection(),
+     "There should now be a selection.");
+   is(graph.getSelection().start, 100,
+     "The graph's selection now has an updated start value.");
+   is(graph.getSelection().end, 200,
+     "The graph's selection now has an updated end value.");
+@@ -56,28 +56,28 @@ function* testSelection(graph) {
+   ok(thrown, "Setting a null selection shouldn't work.");
+ 
+   ok(graph.hasSelection(),
+     "There should still be a selection.");
+ 
+   let deselected = graph.once("deselecting");
+   graph.dropSelection();
+ 
+-  yield deselected;
++  await deselected;
+   ok(true, "A 'deselecting' event has been fired.");
+ 
+   ok(!graph.hasSelection(),
+     "There shouldn't be any selection anymore.");
+   ok(graph.getSelection().start === null,
+     "The graph's selection now has a null start value.");
+   ok(graph.getSelection().end === null,
+     "The graph's selection now has a null end value.");
+ }
+ 
+-function* testCursor(graph) {
++function testCursor(graph) {
+   ok(graph.getCursor().x === null,
+     "The graph's cursor should initially have a null X value.");
+   ok(graph.getCursor().y === null,
+     "The graph's cursor should initially have a null Y value.");
+   ok(!graph.hasCursor(),
+     "There shouldn't initially be any cursor.");
+ 
+   graph.setCursor({ x: 100, y: 50 });
+diff --git a/devtools/client/shared/test/browser_graphs-04.js b/devtools/client/shared/test/browser_graphs-04.js
+--- a/devtools/client/shared/test/browser_graphs-04.js
++++ b/devtools/client/shared/test/browser_graphs-04.js
+@@ -2,30 +2,30 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // Tests that graph widgets can correctly compare selections and cursors.
+ 
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   ok(!graph.hasSelection(),
+     "There shouldn't initially be any selection.");
+   is(graph.getSelectionWidth(), 0,
+     "The selection width should be 0 when there's no selection.");
+diff --git a/devtools/client/shared/test/browser_graphs-05.js b/devtools/client/shared/test/browser_graphs-05.js
+--- a/devtools/client/shared/test/browser_graphs-05.js
++++ b/devtools/client/shared/test/browser_graphs-05.js
+@@ -25,30 +25,30 @@ const TEST_DATA = [
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const TEST_REGIONS = [{ start: 320, end: 460 }, { start: 780, end: 860 }];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   ok(!graph.getHoveredRegion(),
+     "There should be no hovered region yet because there's no regions.");
+ 
+   ok(!graph._isHoveringStartBoundary(),
+diff --git a/devtools/client/shared/test/browser_graphs-06.js b/devtools/client/shared/test/browser_graphs-06.js
+--- a/devtools/client/shared/test/browser_graphs-06.js
++++ b/devtools/client/shared/test/browser_graphs-06.js
+@@ -25,30 +25,30 @@ const TEST_DATA = [
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const TEST_REGIONS = [{ start: 320, end: 460 }, { start: 780, end: 860 }];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   graph.setData(TEST_DATA);
+   graph.setRegions(TEST_REGIONS);
+ 
+   click(graph, (graph._regions[0].start + graph._regions[0].end) / 2);
+diff --git a/devtools/client/shared/test/browser_graphs-07a.js b/devtools/client/shared/test/browser_graphs-07a.js
+--- a/devtools/client/shared/test/browser_graphs-07a.js
++++ b/devtools/client/shared/test/browser_graphs-07a.js
+@@ -24,33 +24,33 @@ const TEST_DATA = [
+   { delta: 3380, value: 35 }, { delta: 3480, value: 27 },
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+   testGraph(graph, normalDragStop);
+-  yield graph.destroy();
++  await graph.destroy();
+ 
+   let graph2 = new LineGraphWidget(doc.body, "fps");
+-  yield graph2.once("ready");
++  await graph2.once("ready");
+   testGraph(graph2, buggyDragStop);
+-  yield graph2.destroy();
++  await graph2.destroy();
+ 
+   host.destroy();
+ }
+ 
+ function testGraph(graph, dragStop) {
+   graph.setData(TEST_DATA);
+ 
+   info("Making a selection.");
+diff --git a/devtools/client/shared/test/browser_graphs-07b.js b/devtools/client/shared/test/browser_graphs-07b.js
+--- a/devtools/client/shared/test/browser_graphs-07b.js
++++ b/devtools/client/shared/test/browser_graphs-07b.js
+@@ -24,30 +24,30 @@ const TEST_DATA = [
+   { delta: 3380, value: 35 }, { delta: 3480, value: 27 },
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ var LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   graph.setData(TEST_DATA);
+   graph.selectionEnabled = false;
+ 
+   info("Attempting to make a selection.");
+diff --git a/devtools/client/shared/test/browser_graphs-07c.js b/devtools/client/shared/test/browser_graphs-07c.js
+--- a/devtools/client/shared/test/browser_graphs-07c.js
++++ b/devtools/client/shared/test/browser_graphs-07c.js
+@@ -26,28 +26,28 @@ const TEST_DATA = [
+   { delta: 3380, value: 35 }, { delta: 3480, value: 27 },
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+   testGraph(graph);
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   graph.setData(TEST_DATA);
+ 
+   info("Making a selection.");
+ 
+diff --git a/devtools/client/shared/test/browser_graphs-07d.js b/devtools/client/shared/test/browser_graphs-07d.js
+--- a/devtools/client/shared/test/browser_graphs-07d.js
++++ b/devtools/client/shared/test/browser_graphs-07d.js
+@@ -25,30 +25,30 @@ const TEST_DATA = [
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const TEST_REGIONS = [{ start: 320, end: 460 }, { start: 780, end: 860 }];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   graph.setData(TEST_DATA);
+   graph.setRegions(TEST_REGIONS);
+ 
+   // Measure the color of the first pixel before any selection is made.
+diff --git a/devtools/client/shared/test/browser_graphs-07e.js b/devtools/client/shared/test/browser_graphs-07e.js
+--- a/devtools/client/shared/test/browser_graphs-07e.js
++++ b/devtools/client/shared/test/browser_graphs-07e.js
+@@ -25,40 +25,40 @@ const TEST_DATA = [
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ let CURRENT_ZOOM = 1;
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+   graph.setData(TEST_DATA);
+ 
+   info("Testing with normal zoom.");
+   testGraph(graph);
+ 
+   info("Testing while zoomed out.");
+   setZoom(host.frame, .5);
+   testGraph(graph);
+ 
+   info("Testing while zoomed in.");
+   setZoom(host.frame, 2);
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   graph.dropSelection();
+ 
+   info("Making a selection.");
+ 
+diff --git a/devtools/client/shared/test/browser_graphs-08.js b/devtools/client/shared/test/browser_graphs-08.js
+--- a/devtools/client/shared/test/browser_graphs-08.js
++++ b/devtools/client/shared/test/browser_graphs-08.js
+@@ -24,30 +24,30 @@ const TEST_DATA = [
+   { delta: 3380, value: 35 }, { delta: 3480, value: 27 },
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   graph.setData(TEST_DATA);
+ 
+   dragStart(graph, 300);
+   dragStop(graph, 500);
+diff --git a/devtools/client/shared/test/browser_graphs-09a.js b/devtools/client/shared/test/browser_graphs-09a.js
+--- a/devtools/client/shared/test/browser_graphs-09a.js
++++ b/devtools/client/shared/test/browser_graphs-09a.js
+@@ -24,36 +24,36 @@ const TEST_DATA = [
+   { delta: 3380, value: 35 }, { delta: 3480, value: 27 },
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, { metric: "fps" });
+ 
+-  yield testGraph(graph);
++  await testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(graph) {
++async function testGraph(graph) {
+   info("Should be able to set the graph data before waiting for the ready event.");
+ 
+-  yield graph.setDataWhenReady(TEST_DATA);
++  await graph.setDataWhenReady(TEST_DATA);
+   ok(graph.hasData(), "Data was set successfully.");
+ 
+   is(graph._gutter.hidden, false,
+     "The gutter should not be hidden because the tooltips have arrows.");
+   is(graph._maxTooltip.hidden, false,
+     "The max tooltip should not be hidden.");
+   is(graph._avgTooltip.hidden, false,
+     "The avg tooltip should not be hidden.");
+diff --git a/devtools/client/shared/test/browser_graphs-09b.js b/devtools/client/shared/test/browser_graphs-09b.js
+--- a/devtools/client/shared/test/browser_graphs-09b.js
++++ b/devtools/client/shared/test/browser_graphs-09b.js
+@@ -24,36 +24,36 @@ const TEST_DATA = [
+   { delta: 3380, value: 35 }, { delta: 3480, value: 27 },
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+   graph.withTooltipArrows = false;
+   graph.withFixedTooltipPositions = true;
+ 
+-  yield testGraph(graph);
++  await testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(graph) {
+-  yield graph.setDataWhenReady(TEST_DATA);
++async function testGraph(graph) {
++  await graph.setDataWhenReady(TEST_DATA);
+ 
+   is(graph._gutter.hidden, false,
+     "The gutter should be visible even if the tooltips don't have arrows.");
+   is(graph._maxTooltip.hidden, false,
+     "The max tooltip should not be hidden.");
+   is(graph._avgTooltip.hidden, false,
+     "The avg tooltip should not be hidden.");
+   is(graph._minTooltip.hidden, false,
+diff --git a/devtools/client/shared/test/browser_graphs-09c.js b/devtools/client/shared/test/browser_graphs-09c.js
+--- a/devtools/client/shared/test/browser_graphs-09c.js
++++ b/devtools/client/shared/test/browser_graphs-09c.js
+@@ -3,34 +3,34 @@
+ 
+ "use strict";
+ 
+ // Tests that line graphs hide the tooltips when there's no data available.
+ 
+ const TEST_DATA = [];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+ 
+-  yield testGraph(graph);
++  await testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(graph) {
+-  yield graph.setDataWhenReady(TEST_DATA);
++async function testGraph(graph) {
++  await graph.setDataWhenReady(TEST_DATA);
+ 
+   is(graph._gutter.hidden, true,
+     "The gutter should be hidden, since there's no data available.");
+   is(graph._maxTooltip.hidden, true,
+     "The max tooltip should be hidden.");
+   is(graph._avgTooltip.hidden, true,
+     "The avg tooltip should be hidden.");
+   is(graph._minTooltip.hidden, true,
+diff --git a/devtools/client/shared/test/browser_graphs-09d.js b/devtools/client/shared/test/browser_graphs-09d.js
+--- a/devtools/client/shared/test/browser_graphs-09d.js
++++ b/devtools/client/shared/test/browser_graphs-09d.js
+@@ -4,34 +4,34 @@
+ "use strict";
+ 
+ // Tests that line graphs hide the 'max' tooltip when the distance between
+ // the 'min' and 'max' tooltip is too small.
+ 
+ const TEST_DATA = [{ delta: 100, value: 60 }, { delta: 200, value: 59.9 }];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+ 
+-  yield testGraph(graph);
++  await testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(graph) {
+-  yield graph.setDataWhenReady(TEST_DATA);
++async function testGraph(graph) {
++  await graph.setDataWhenReady(TEST_DATA);
+ 
+   is(graph._gutter.hidden, false,
+     "The gutter should not be hidden.");
+   is(graph._maxTooltip.hidden, true,
+     "The max tooltip should be hidden.");
+   is(graph._avgTooltip.hidden, false,
+     "The avg tooltip should not be hidden.");
+   is(graph._minTooltip.hidden, false,
+diff --git a/devtools/client/shared/test/browser_graphs-09e.js b/devtools/client/shared/test/browser_graphs-09e.js
+--- a/devtools/client/shared/test/browser_graphs-09e.js
++++ b/devtools/client/shared/test/browser_graphs-09e.js
+@@ -27,56 +27,56 @@ const TEST_DATA = [
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ 
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+ 
+-  yield testGraph(graph);
++  await testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(graph) {
+-  yield graph.setDataWhenReady(NO_DATA);
++async function testGraph(graph) {
++  await graph.setDataWhenReady(NO_DATA);
+ 
+   is(graph._gutter.hidden, true,
+     "The gutter should be hidden when there's no data available.");
+   is(graph._maxTooltip.hidden, true,
+     "The max tooltip should be hidden when there's no data available.");
+   is(graph._avgTooltip.hidden, true,
+     "The avg tooltip should be hidden when there's no data available.");
+   is(graph._minTooltip.hidden, true,
+     "The min tooltip should be hidden when there's no data available.");
+ 
+-  yield graph.setDataWhenReady(TEST_DATA);
++  await graph.setDataWhenReady(TEST_DATA);
+ 
+   is(graph._gutter.hidden, false,
+     "The gutter should be visible now.");
+   is(graph._maxTooltip.hidden, false,
+     "The max tooltip should be visible now.");
+   is(graph._avgTooltip.hidden, false,
+     "The avg tooltip should be visible now.");
+   is(graph._minTooltip.hidden, false,
+     "The min tooltip should be visible now.");
+ 
+-  yield graph.setDataWhenReady(NO_DATA);
++  await graph.setDataWhenReady(NO_DATA);
+ 
+   is(graph._gutter.hidden, true,
+     "The gutter should be hidden again.");
+   is(graph._maxTooltip.hidden, true,
+     "The max tooltip should be hidden again.");
+   is(graph._avgTooltip.hidden, true,
+     "The avg tooltip should be hidden again.");
+   is(graph._minTooltip.hidden, true,
+diff --git a/devtools/client/shared/test/browser_graphs-09f.js b/devtools/client/shared/test/browser_graphs-09f.js
+--- a/devtools/client/shared/test/browser_graphs-09f.js
++++ b/devtools/client/shared/test/browser_graphs-09f.js
+@@ -4,38 +4,38 @@
+ "use strict";
+ 
+ // Tests the constructor options for `min`, `max` and `avg` on displaying the
+ // gutter/tooltips and lines.
+ 
+ const TEST_DATA = [{ delta: 100, value: 60 }, { delta: 200, value: 1 }];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+ 
+-  yield testGraph(doc.body, { avg: false });
+-  yield testGraph(doc.body, { min: false });
+-  yield testGraph(doc.body, { max: false });
+-  yield testGraph(doc.body, { min: false, max: false, avg: false });
+-  yield testGraph(doc.body, {});
++  await testGraph(doc.body, { avg: false });
++  await testGraph(doc.body, { min: false });
++  await testGraph(doc.body, { max: false });
++  await testGraph(doc.body, { min: false, max: false, avg: false });
++  await testGraph(doc.body, {});
+ 
+   host.destroy();
+ }
+ 
+-function* testGraph(parent, options) {
++async function testGraph(parent, options) {
+   options.metric = "fps";
+   let graph = new LineGraphWidget(parent, options);
+-  yield graph.setDataWhenReady(TEST_DATA);
++  await graph.setDataWhenReady(TEST_DATA);
+   let shouldGutterShow = options.min === false && options.max === false;
+ 
+   is(graph._gutter.hidden, shouldGutterShow,
+     `The gutter should ${shouldGutterShow ? "" : "not "}be shown`);
+ 
+   is(graph._maxTooltip.hidden, options.max === false,
+     `The max tooltip should ${options.max === false ? "not " : ""}be shown`);
+   is(graph._maxGutterLine.hidden, options.max === false,
+@@ -44,10 +44,10 @@ function* testGraph(parent, options) {
+     `The min tooltip should ${options.min === false ? "not " : ""}be shown`);
+   is(graph._minGutterLine.hidden, options.min === false,
+     `The min gutter should ${options.min === false ? "not " : ""}be shown`);
+   is(graph._avgTooltip.hidden, options.avg === false,
+     `The avg tooltip should ${options.avg === false ? "not " : ""}be shown`);
+   is(graph._avgGutterLine.hidden, options.avg === false,
+     `The avg gutter should ${options.avg === false ? "not " : ""}be shown`);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+ }
+diff --git a/devtools/client/shared/test/browser_graphs-10a.js b/devtools/client/shared/test/browser_graphs-10a.js
+--- a/devtools/client/shared/test/browser_graphs-10a.js
++++ b/devtools/client/shared/test/browser_graphs-10a.js
+@@ -24,47 +24,47 @@ const TEST_DATA = [
+   { delta: 3380, value: 35 }, { delta: 3480, value: 27 },
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost("window");
++async function performTest() {
++  let [host,, doc] = await createHost("window");
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   let refreshCount = 0;
+   graph.on("refresh", () => refreshCount++);
+ 
+-  yield testGraph(host, graph);
++  await testGraph(host, graph);
+ 
+   is(refreshCount, 2, "The graph should've been refreshed 2 times.");
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(host, graph) {
++async function testGraph(host, graph) {
+   graph.setData(TEST_DATA);
+   let initialBounds = host.frame.getBoundingClientRect();
+ 
+   host._window.resizeBy(-100, -100);
+-  yield graph.once("refresh");
++  await graph.once("refresh");
+   let newBounds = host.frame.getBoundingClientRect();
+ 
+   is(initialBounds.width - newBounds.width, 100,
+     "The window was properly resized (1).");
+   is(initialBounds.height - newBounds.height, 100,
+     "The window was properly resized (2).");
+ 
+   is(graph.width, newBounds.width * window.devicePixelRatio,
+@@ -94,17 +94,17 @@ function* testGraph(host, graph) {
+   ok(!graph.hasSelectionInProgress(),
+     "The selection should have stopped (3).");
+   is(graph.getSelection().start, 300,
+     "The current selection start value is correct (3).");
+   is(graph.getSelection().end, 500,
+     "The current selection end value is correct (3).");
+ 
+   host._window.resizeBy(100, 100);
+-  yield graph.once("refresh");
++  await graph.once("refresh");
+   let newerBounds = host.frame.getBoundingClientRect();
+ 
+   is(initialBounds.width - newerBounds.width, 0,
+     "The window was properly resized (3).");
+   is(initialBounds.height - newerBounds.height, 0,
+     "The window was properly resized (4).");
+ 
+   is(graph.width, newerBounds.width * window.devicePixelRatio,
+diff --git a/devtools/client/shared/test/browser_graphs-10b.js b/devtools/client/shared/test/browser_graphs-10b.js
+--- a/devtools/client/shared/test/browser_graphs-10b.js
++++ b/devtools/client/shared/test/browser_graphs-10b.js
+@@ -25,47 +25,47 @@ const TEST_DATA = [
+   { delta: 3380, value: 35 }, { delta: 3480, value: 27 },
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost("window");
++async function performTest() {
++  let [host,, doc] = await createHost("window");
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new LineGraphWidget(doc.body, "fps");
+   graph.fixedWidth = 200;
+   graph.fixedHeight = 100;
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   let refreshCount = 0;
+   let refreshCancelledCount = 0;
+   graph.on("refresh", () => refreshCount++);
+   graph.on("refresh-cancelled", () => refreshCancelledCount++);
+ 
+-  yield testGraph(host, graph);
++  await testGraph(host, graph);
+ 
+   is(refreshCount, 0, "The graph shouldn't have been refreshed at all.");
+   is(refreshCancelledCount, 2, "The graph should've had 2 refresh attempts.");
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(host, graph) {
++async function testGraph(host, graph) {
+   graph.setData(TEST_DATA);
+ 
+   host._window.resizeBy(-100, -100);
+-  yield graph.once("refresh-cancelled");
++  await graph.once("refresh-cancelled");
+ 
+   host._window.resizeBy(100, 100);
+-  yield graph.once("refresh-cancelled");
++  await graph.once("refresh-cancelled");
+ }
+diff --git a/devtools/client/shared/test/browser_graphs-10c.js b/devtools/client/shared/test/browser_graphs-10c.js
+--- a/devtools/client/shared/test/browser_graphs-10c.js
++++ b/devtools/client/shared/test/browser_graphs-10c.js
+@@ -22,46 +22,46 @@ const TEST_DATA = [
+   { delta: 3380, value: 35 }, { delta: 3480, value: 27 },
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost("window");
++async function performTest() {
++  let [host,, doc] = await createHost("window");
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new LineGraphWidget(doc.body, "fps");
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   let refreshCount = 0;
+   graph.on("refresh", () => refreshCount++);
+ 
+-  yield testGraph(host, graph);
++  await testGraph(host, graph);
+ 
+   is(refreshCount, 2, "The graph should've been refreshed 2 times.");
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(host, graph) {
++async function testGraph(host, graph) {
+   graph.setData(TEST_DATA);
+ 
+   host._window.resizeTo(500, 500);
+-  yield graph.once("refresh");
++  await graph.once("refresh");
+   let oldBounds = host.frame.getBoundingClientRect();
+ 
+   is(graph._width, oldBounds.width * window.devicePixelRatio,
+     "The window was properly resized (1).");
+   is(graph._height, oldBounds.height * window.devicePixelRatio,
+     "The window was properly resized (1).");
+ 
+   dragStart(graph, 100);
+@@ -70,17 +70,17 @@ function* testGraph(host, graph) {
+   is(graph.getSelection().start, 100,
+     "The current selection start value is correct (1).");
+   is(graph.getSelection().end, 400,
+     "The current selection end value is correct (1).");
+ 
+   info("Making sure the selection updates when the window is resized");
+ 
+   host._window.resizeTo(250, 250);
+-  yield graph.once("refresh");
++  await graph.once("refresh");
+   let newBounds = host.frame.getBoundingClientRect();
+ 
+   is(graph._width, newBounds.width * window.devicePixelRatio,
+     "The window was properly resized (2).");
+   is(graph._height, newBounds.height * window.devicePixelRatio,
+     "The window was properly resized (2).");
+ 
+   let ratio = oldBounds.width / newBounds.width;
+diff --git a/devtools/client/shared/test/browser_graphs-11a.js b/devtools/client/shared/test/browser_graphs-11a.js
+--- a/devtools/client/shared/test/browser_graphs-11a.js
++++ b/devtools/client/shared/test/browser_graphs-11a.js
+@@ -8,30 +8,30 @@
+ const BarGraphWidget = require("devtools/client/shared/widgets/BarGraphWidget");
+ 
+ const CATEGORIES = [
+   { color: "#46afe3", label: "Foo" },
+   { color: "#eb5368", label: "Bar" },
+   { color: "#70bf53", label: "Baz" }
+ ];
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new BarGraphWidget(doc.body);
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   graph.format = CATEGORIES;
+   graph.setData([{ delta: 0, values: [] }]);
+ 
+   let legendContainer = graph._document.querySelector(".bar-graph-widget-legend");
+diff --git a/devtools/client/shared/test/browser_graphs-11b.js b/devtools/client/shared/test/browser_graphs-11b.js
+--- a/devtools/client/shared/test/browser_graphs-11b.js
++++ b/devtools/client/shared/test/browser_graphs-11b.js
+@@ -8,39 +8,39 @@
+ const BarGraphWidget = require("devtools/client/shared/widgets/BarGraphWidget");
+ 
+ const CATEGORIES = [
+   { color: "#46afe3", label: "Foo" },
+   { color: "#eb5368", label: "Bar" },
+   { color: "#70bf53", label: "Baz" }
+ ];
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new BarGraphWidget(doc.body, 1);
+   graph.fixedWidth = 200;
+   graph.fixedHeight = 100;
+ 
+-  yield graph.once("ready");
+-  yield testGraph(graph);
++  await graph.once("ready");
++  await testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(graph) {
++async function testGraph(graph) {
+   graph.format = CATEGORIES;
+   graph.dataOffsetX = 1000;
+   graph.setData([{
+     delta: 1100, values: [0, 2, 3]
+   }, {
+     delta: 1200, values: [1, 0, 2]
+   }, {
+     delta: 1300, values: [2, 1, 0]
+@@ -55,72 +55,72 @@ function* testGraph(graph) {
+   /* eslint-disable max-len */
+   is(graph._blocksBoundingRects.toSource(), "[{type:1, start:0, end:33.33333333333333, top:70, bottom:100}, {type:2, start:0, end:33.33333333333333, top:24, bottom:69}, {type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100}, {type:2, start:34.33333333333333, end:66.66666666666666, top:54, bottom:84}, {type:0, start:67.66666666666666, end:100, top:70, bottom:100}, {type:1, start:67.66666666666666, end:100, top:54, bottom:69}, {type:1, start:101, end:133.33333333333331, top:55, bottom:100}, {type:2, start:101, end:133.33333333333331, top:39, bottom:54}, {type:0, start:134.33333333333331, end:166.66666666666666, top:55, bottom:100}, {type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54}, {type:0, start:167.66666666666666, end:200, top:55, bottom:100}, {type:1, start:167.66666666666666, end:200, top:24, bottom:54}]",
+     "The correct blocks bounding rects were calculated for the bar graph.");
+ 
+   let legendItems = graph._document.querySelectorAll(".bar-graph-widget-legend-item");
+   is(legendItems.length, 3,
+     "Three legend items should exist in the entire graph.");
+ 
+-  yield testLegend(graph, 0, {
++  await testLegend(graph, 0, {
+     highlights: "[{type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100}, {type:0, start:67.66666666666666, end:100, top:70, bottom:100}, {type:0, start:134.33333333333331, end:166.66666666666666, top:55, bottom:100}, {type:0, start:167.66666666666666, end:200, top:55, bottom:100}]",
+     selection: "({start:34.33333333333333, end:200})",
+     leftmost: "({type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100})",
+     rightmost: "({type:0, start:167.66666666666666, end:200, top:55, bottom:100})"
+   });
+-  yield testLegend(graph, 1, {
++  await testLegend(graph, 1, {
+     highlights: "[{type:1, start:0, end:33.33333333333333, top:70, bottom:100}, {type:1, start:67.66666666666666, end:100, top:54, bottom:69}, {type:1, start:101, end:133.33333333333331, top:55, bottom:100}, {type:1, start:167.66666666666666, end:200, top:24, bottom:54}]",
+     selection: "({start:0, end:200})",
+     leftmost: "({type:1, start:0, end:33.33333333333333, top:70, bottom:100})",
+     rightmost: "({type:1, start:167.66666666666666, end:200, top:24, bottom:54})"
+   });
+-  yield testLegend(graph, 2, {
++  await testLegend(graph, 2, {
+     highlights: "[{type:2, start:0, end:33.33333333333333, top:24, bottom:69}, {type:2, start:34.33333333333333, end:66.66666666666666, top:54, bottom:84}, {type:2, start:101, end:133.33333333333331, top:39, bottom:54}, {type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54}]",
+     selection: "({start:0, end:166.66666666666666})",
+     leftmost: "({type:2, start:0, end:33.33333333333333, top:24, bottom:69})",
+     rightmost: "({type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54})"
+   });
+   /* eslint-enable max-len */
+ }
+ 
+-function* testLegend(graph, index, { highlights, selection, leftmost, rightmost }) {
++async function testLegend(graph, index, { highlights, selection, leftmost, rightmost }) {
+   // Hover.
+ 
+   let legendItems = graph._document.querySelectorAll(".bar-graph-widget-legend-item");
+   let colorBlock = legendItems[index].querySelector("[view=color]");
+ 
+   let debounced = graph.once("legend-hover");
+   graph._onLegendMouseOver({ target: colorBlock });
+   ok(!graph.hasMask(), "The graph shouldn't get highlights immediately.");
+ 
+-  let [type, rects] = yield debounced;
++  let [type, rects] = await debounced;
+   ok(graph.hasMask(), "The graph should now have highlights.");
+ 
+   is(type, index,
+     "The legend item was correctly hovered.");
+   is(rects.toSource(), highlights,
+     "The legend item highlighted the correct regions.");
+ 
+   // Unhover.
+ 
+   let unhovered = graph.once("legend-unhover");
+   graph._onLegendMouseOut();
+   ok(!graph.hasMask(), "The graph shouldn't have highlights anymore.");
+ 
+-  yield unhovered;
++  await unhovered;
+   ok(true, "The 'legend-mouseout' event was emitted.");
+ 
+   // Select.
+ 
+   let selected = graph.once("legend-selection");
+   graph._onLegendMouseDown(mockEvent(colorBlock));
+   ok(graph.hasSelection(), "The graph should now have a selection.");
+   is(graph.getSelection().toSource(), selection, "The graph has a correct selection.");
+ 
+-  let [left, right] = yield selected;
++  let [left, right] = await selected;
+   is(left.toSource(), leftmost, "The correct leftmost data block was found.");
+   is(right.toSource(), rightmost, "The correct rightmost data block was found.");
+ 
+   // Deselect.
+ 
+   graph.dropSelection();
+ }
+ 
+diff --git a/devtools/client/shared/test/browser_graphs-12.js b/devtools/client/shared/test/browser_graphs-12.js
+--- a/devtools/client/shared/test/browser_graphs-12.js
++++ b/devtools/client/shared/test/browser_graphs-12.js
+@@ -4,24 +4,24 @@
+ "use strict";
+ 
+ // Tests that canvas graphs can have their selection linked.
+ 
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ const BarGraphWidget = require("devtools/client/shared/widgets/BarGraphWidget");
+ const {CanvasGraphUtils} = require("devtools/client/shared/widgets/Graphs");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let first = document.createElement("div");
+   first.setAttribute("style", "display: inline-block; width: 100%; height: 50%;");
+   doc.body.appendChild(first);
+ 
+   let second = document.createElement("div");
+@@ -29,23 +29,23 @@ function* performTest() {
+   doc.body.appendChild(second);
+ 
+   let graph1 = new LineGraphWidget(first, "js");
+   let graph2 = new BarGraphWidget(second);
+ 
+   CanvasGraphUtils.linkAnimation(graph1, graph2);
+   CanvasGraphUtils.linkSelection(graph1, graph2);
+ 
+-  yield graph1.ready();
+-  yield graph2.ready();
++  await graph1.ready();
++  await graph2.ready();
+ 
+   testGraphs(graph1, graph2);
+ 
+-  yield graph1.destroy();
+-  yield graph2.destroy();
++  await graph1.destroy();
++  await graph2.destroy();
+   host.destroy();
+ }
+ 
+ function testGraphs(graph1, graph2) {
+   info("Making a selection in the first graph.");
+ 
+   dragStart(graph1, 300);
+   ok(graph1.hasSelectionInProgress(),
+diff --git a/devtools/client/shared/test/browser_graphs-13.js b/devtools/client/shared/test/browser_graphs-13.js
+--- a/devtools/client/shared/test/browser_graphs-13.js
++++ b/devtools/client/shared/test/browser_graphs-13.js
+@@ -2,35 +2,35 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // Tests that graph widgets may have a fixed width or height.
+ 
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   doc.body.setAttribute("style",
+                         "position: fixed; width: 100%; height: 100%; margin: 0;");
+ 
+   let graph = new LineGraphWidget(doc.body, "fps");
+   graph.fixedWidth = 200;
+   graph.fixedHeight = 100;
+ 
+-  yield graph.ready();
++  await graph.ready();
+   testGraph(host, graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(host, graph) {
+   let bounds = host.frame.getBoundingClientRect();
+ 
+   isnot(graph.width, bounds.width * window.devicePixelRatio,
+     "The graph should not span all the parent node's width.");
+diff --git a/devtools/client/shared/test/browser_graphs-14.js b/devtools/client/shared/test/browser_graphs-14.js
+--- a/devtools/client/shared/test/browser_graphs-14.js
++++ b/devtools/client/shared/test/browser_graphs-14.js
+@@ -24,41 +24,41 @@ const TEST_DATA = [
+   { delta: 3380, value: 35 }, { delta: 3480, value: 27 },
+   { delta: 3580, value: 39 }, { delta: 3680, value: 42 },
+   { delta: 3780, value: 49 }, { delta: 3880, value: 55 },
+   { delta: 3980, value: 60 }, { delta: 4080, value: 60 },
+   { delta: 4180, value: 60 }
+ ];
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+ 
+-  yield testGraph(graph);
++  await testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(graph) {
++async function testGraph(graph) {
+   let mouseDownEvents = 0;
+   let mouseUpEvents = 0;
+   let scrollEvents = 0;
+   graph.on("mousedown", () => mouseDownEvents++);
+   graph.on("mouseup", () => mouseUpEvents++);
+   graph.on("scroll", () => scrollEvents++);
+ 
+-  yield graph.setDataWhenReady(TEST_DATA);
++  await graph.setDataWhenReady(TEST_DATA);
+ 
+   info("Making a selection.");
+ 
+   dragStart(graph, 300);
+   dragStop(graph, 500);
+   is(graph.getSelection().start, 300,
+     "The current selection start value is correct (1).");
+   is(graph.getSelection().end, 500,
+diff --git a/devtools/client/shared/test/browser_graphs-15.js b/devtools/client/shared/test/browser_graphs-15.js
+--- a/devtools/client/shared/test/browser_graphs-15.js
++++ b/devtools/client/shared/test/browser_graphs-15.js
+@@ -20,30 +20,30 @@ for (let frameRate of FRAMES) {
+     let delta = Math.floor(1000 / frameRate);
+     t += delta;
+     TEST_DATA.push(t);
+   }
+ }
+ 
+ const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new LineGraphWidget(doc.body, "fps");
+ 
+-  yield testGraph(graph);
++  await testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+-function* testGraph(graph) {
++async function testGraph(graph) {
+   console.log("test data", TEST_DATA);
+-  yield graph.setDataFromTimestamps(TEST_DATA, INTERVAL, DURATION);
++  await graph.setDataFromTimestamps(TEST_DATA, INTERVAL, DURATION);
+   is(graph._avgTooltip.querySelector("[text=value]").textContent, "50",
+     "The average tooltip displays the correct value.");
+ }
+diff --git a/devtools/client/shared/test/browser_graphs-16.js b/devtools/client/shared/test/browser_graphs-16.js
+--- a/devtools/client/shared/test/browser_graphs-16.js
++++ b/devtools/client/shared/test/browser_graphs-16.js
+@@ -16,30 +16,30 @@ const TEST_DATA = [
+ ];
+ 
+ const SECTIONS = [
+   { color: "red" },
+   { color: "green" },
+   { color: "blue" }
+ ];
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host,, doc] = yield createHost();
++async function performTest() {
++  let [host,, doc] = await createHost();
+   let graph = new MountainGraphWidget(doc.body);
+-  yield graph.once("ready");
++  await graph.once("ready");
+ 
+   testGraph(graph);
+ 
+-  yield graph.destroy();
++  await graph.destroy();
+   host.destroy();
+ }
+ 
+ function testGraph(graph) {
+   graph.format = SECTIONS;
+   graph.setData(TEST_DATA);
+   ok(true, "The graph didn't throw any erorrs.");
+ }
+diff --git a/devtools/client/shared/test/browser_html_tooltip-01.js b/devtools/client/shared/test/browser_html_tooltip-01.js
+--- a/devtools/client/shared/test/browser_html_tooltip-01.js
++++ b/devtools/client/shared/test/browser_html_tooltip-01.js
+@@ -19,59 +19,59 @@ let useXulWrapper;
+ function getTooltipContent(doc) {
+   let div = doc.createElementNS(HTML_NS, "div");
+   div.style.height = "50px";
+   div.style.boxSizing = "border-box";
+   div.textContent = "tooltip";
+   return div;
+ }
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Run tests for a Tooltip without using a XUL panel");
+   useXulWrapper = false;
+-  yield runTests(doc);
++  await runTests(doc);
+ 
+   info("Run tests for a Tooltip with a XUL panel");
+   useXulWrapper = true;
+-  yield runTests(doc);
++  await runTests(doc);
+ });
+ 
+-function* runTests(doc) {
+-  yield addTab("about:blank");
++async function runTests(doc) {
++  await addTab("about:blank");
+   let tooltip = new HTMLTooltip(doc, {useXulWrapper});
+ 
+   info("Set tooltip content");
+   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
+ 
+   is(tooltip.isVisible(), false, "Tooltip is not visible");
+ 
+   info("Show the tooltip and check the expected events are fired.");
+ 
+   let shown = 0;
+   tooltip.on("shown", () => shown++);
+ 
+   let onShown = tooltip.once("shown");
+   tooltip.show(doc.getElementById("box1"));
+ 
+-  yield onShown;
++  await onShown;
+   is(shown, 1, "Event shown was fired once");
+ 
+-  yield waitForReflow(tooltip);
++  await waitForReflow(tooltip);
+   is(tooltip.isVisible(), true, "Tooltip is visible");
+ 
+   info("Hide the tooltip and check the expected events are fired.");
+ 
+   let hidden = 0;
+   tooltip.on("hidden", () => hidden++);
+ 
+   let onPopupHidden = tooltip.once("hidden");
+   tooltip.hide();
+ 
+-  yield onPopupHidden;
++  await onPopupHidden;
+   is(hidden, 1, "Event hidden was fired once");
+ 
+-  yield waitForReflow(tooltip);
++  await waitForReflow(tooltip);
+   is(tooltip.isVisible(), false, "Tooltip is not visible");
+ 
+   tooltip.destroy();
+ }
+diff --git a/devtools/client/shared/test/browser_html_tooltip-02.js b/devtools/client/shared/test/browser_html_tooltip-02.js
+--- a/devtools/client/shared/test/browser_html_tooltip-02.js
++++ b/devtools/client/shared/test/browser_html_tooltip-02.js
+@@ -10,146 +10,146 @@
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip-02.xul";
+ 
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ loadHelperScript("helper_html_tooltip.js");
+ 
+ let useXulWrapper;
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  await addTab("about:blank");
++  let [,, doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Run tests for a Tooltip without using a XUL panel");
+   useXulWrapper = false;
+-  yield runTests(doc);
++  await runTests(doc);
+ 
+   info("Run tests for a Tooltip with a XUL panel");
+   useXulWrapper = true;
+-  yield runTests(doc);
++  await runTests(doc);
+ });
+ 
+-function* runTests(doc) {
+-  yield testClickInTooltipContent(doc);
+-  yield testConsumeOutsideClicksFalse(doc);
+-  yield testConsumeOutsideClicksTrue(doc);
+-  yield testConsumeWithRightClick(doc);
+-  yield testClickInOuterIframe(doc);
+-  yield testClickInInnerIframe(doc);
++async function runTests(doc) {
++  await testClickInTooltipContent(doc);
++  await testConsumeOutsideClicksFalse(doc);
++  await testConsumeOutsideClicksTrue(doc);
++  await testConsumeWithRightClick(doc);
++  await testClickInOuterIframe(doc);
++  await testClickInInnerIframe(doc);
+ }
+ 
+-function* testClickInTooltipContent(doc) {
++async function testClickInTooltipContent(doc) {
+   info("Test a tooltip is not closed when clicking inside itself");
+ 
+   let tooltip = new HTMLTooltip(doc, {useXulWrapper});
+   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+ 
+   let onTooltipContainerClick = once(tooltip.container, "click");
+   EventUtils.synthesizeMouseAtCenter(tooltip.container, {}, doc.defaultView);
+-  yield onTooltipContainerClick;
++  await onTooltipContainerClick;
+   is(tooltip.isVisible(), true, "Tooltip is still visible");
+ 
+   tooltip.destroy();
+ }
+ 
+-function* testConsumeOutsideClicksFalse(doc) {
++async function testConsumeOutsideClicksFalse(doc) {
+   info("Test closing a tooltip via click with consumeOutsideClicks: false");
+   let box4 = doc.getElementById("box4");
+ 
+   let tooltip = new HTMLTooltip(doc, {consumeOutsideClicks: false, useXulWrapper});
+   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+ 
+   let onBox4Clicked = once(box4, "click");
+   let onHidden = once(tooltip, "hidden");
+   EventUtils.synthesizeMouseAtCenter(box4, {}, doc.defaultView);
+-  yield onHidden;
+-  yield onBox4Clicked;
++  await onHidden;
++  await onBox4Clicked;
+ 
+   is(tooltip.isVisible(), false, "Tooltip is hidden");
+ 
+   tooltip.destroy();
+ }
+ 
+-function* testConsumeOutsideClicksTrue(doc) {
++async function testConsumeOutsideClicksTrue(doc) {
+   info("Test closing a tooltip via click with consumeOutsideClicks: true");
+   let box4 = doc.getElementById("box4");
+ 
+   // Count clicks on box4
+   let box4clicks = 0;
+   box4.addEventListener("click", () => box4clicks++);
+ 
+   let tooltip = new HTMLTooltip(doc, {consumeOutsideClicks: true, useXulWrapper});
+   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+ 
+   let onHidden = once(tooltip, "hidden");
+   EventUtils.synthesizeMouseAtCenter(box4, {}, doc.defaultView);
+-  yield onHidden;
++  await onHidden;
+ 
+   is(box4clicks, 0, "box4 catched no click event");
+   is(tooltip.isVisible(), false, "Tooltip is hidden");
+ 
+   tooltip.destroy();
+ }
+ 
+-function* testConsumeWithRightClick(doc) {
++async function testConsumeWithRightClick(doc) {
+   info("Test closing a tooltip with a right-click, with consumeOutsideClicks: true");
+   let box4 = doc.getElementById("box4");
+ 
+   let tooltip = new HTMLTooltip(doc, {consumeOutsideClicks: true, useXulWrapper});
+   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+ 
+   // Only left-click events should be consumed, so we expect to catch a click when using
+   // {button: 2}, which simulates a right-click.
+   info("Right click on box4, expect tooltip to be hidden, event should not be consumed");
+   let onBox4Clicked = once(box4, "click");
+   let onHidden = once(tooltip, "hidden");
+   EventUtils.synthesizeMouseAtCenter(box4, {button: 2}, doc.defaultView);
+-  yield onHidden;
+-  yield onBox4Clicked;
++  await onHidden;
++  await onBox4Clicked;
+ 
+   is(tooltip.isVisible(), false, "Tooltip is hidden");
+ 
+   tooltip.destroy();
+ }
+ 
+-function* testClickInOuterIframe(doc) {
++async function testClickInOuterIframe(doc) {
+   info("Test clicking an iframe outside of the tooltip closes the tooltip");
+   let frame = doc.getElementById("frame");
+ 
+   let tooltip = new HTMLTooltip(doc, {useXulWrapper});
+   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+ 
+   let onHidden = once(tooltip, "hidden");
+   EventUtils.synthesizeMouseAtCenter(frame, {}, doc.defaultView);
+-  yield onHidden;
++  await onHidden;
+ 
+   is(tooltip.isVisible(), false, "Tooltip is hidden");
+   tooltip.destroy();
+ }
+ 
+-function* testClickInInnerIframe(doc) {
++async function testClickInInnerIframe(doc) {
+   info("Test clicking an iframe inside the tooltip content does not close the tooltip");
+ 
+   let tooltip = new HTMLTooltip(doc, {consumeOutsideClicks: false, useXulWrapper});
+ 
+   let iframe = doc.createElementNS(HTML_NS, "iframe");
+   iframe.style.width = "100px";
+   iframe.style.height = "50px";
+   tooltip.setContent(iframe, {width: 100, height: 50});
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+ 
+   let onTooltipContainerClick = once(tooltip.container, "click");
+   EventUtils.synthesizeMouseAtCenter(tooltip.container, {}, doc.defaultView);
+-  yield onTooltipContainerClick;
++  await onTooltipContainerClick;
+ 
+   is(tooltip.isVisible(), true, "Tooltip is still visible");
+ 
+   tooltip.destroy();
+ }
+ 
+ function getTooltipContent(doc) {
+   let div = doc.createElementNS(HTML_NS, "div");
+diff --git a/devtools/client/shared/test/browser_html_tooltip-03.js b/devtools/client/shared/test/browser_html_tooltip-03.js
+--- a/devtools/client/shared/test/browser_html_tooltip-03.js
++++ b/devtools/client/shared/test/browser_html_tooltip-03.js
+@@ -11,91 +11,91 @@
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip-03.xul";
+ 
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ loadHelperScript("helper_html_tooltip.js");
+ 
+ let useXulWrapper;
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  let [, , doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  await addTab("about:blank");
++  let [, , doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Run tests for a Tooltip without using a XUL panel");
+   useXulWrapper = false;
+-  yield runTests(doc);
++  await runTests(doc);
+ 
+   info("Run tests for a Tooltip with a XUL panel");
+   useXulWrapper = true;
+-  yield runTests(doc);
++  await runTests(doc);
+ });
+ 
+-function* runTests(doc) {
+-  yield testNoAutoFocus(doc);
+-  yield testAutoFocus(doc);
+-  yield testAutoFocusPreservesFocusChange(doc);
++async function runTests(doc) {
++  await testNoAutoFocus(doc);
++  await testAutoFocus(doc);
++  await testAutoFocusPreservesFocusChange(doc);
+ }
+ 
+-function* testNoAutoFocus(doc) {
+-  yield focusNode(doc, "#box4-input");
++async function testNoAutoFocus(doc) {
++  await focusNode(doc, "#box4-input");
+   ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
+ 
+   info("Test a tooltip without autofocus will not take focus");
+-  let tooltip = yield createTooltip(doc, false);
++  let tooltip = await createTooltip(doc, false);
+ 
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+   ok(doc.activeElement.closest("#box4-input"), "Focus is still in the #box4-input");
+ 
+-  yield hideTooltip(tooltip);
+-  yield blurNode(doc, "#box4-input");
++  await hideTooltip(tooltip);
++  await blurNode(doc, "#box4-input");
+ 
+   tooltip.destroy();
+ }
+ 
+-function* testAutoFocus(doc) {
+-  yield focusNode(doc, "#box4-input");
++async function testAutoFocus(doc) {
++  await focusNode(doc, "#box4-input");
+   ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
+ 
+   info("Test autofocus tooltip takes focus when displayed, " +
+     "and restores the focus when hidden");
+-  let tooltip = yield createTooltip(doc, true);
++  let tooltip = await createTooltip(doc, true);
+ 
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+   ok(doc.activeElement.closest(".tooltip-content"), "Focus is in the tooltip");
+ 
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+   ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
+ 
+   info("Blur the textbox before moving to the next test to reset the state.");
+-  yield blurNode(doc, "#box4-input");
++  await blurNode(doc, "#box4-input");
+ 
+   tooltip.destroy();
+ }
+ 
+-function* testAutoFocusPreservesFocusChange(doc) {
+-  yield focusNode(doc, "#box4-input");
++async function testAutoFocusPreservesFocusChange(doc) {
++  await focusNode(doc, "#box4-input");
+   ok(doc.activeElement.closest("#box4-input"), "Focus is still in the #box3-input");
+ 
+   info("Test autofocus tooltip takes focus when displayed, " +
+     "but does not try to restore the active element if it is not focused when hidden");
+-  let tooltip = yield createTooltip(doc, true);
++  let tooltip = await createTooltip(doc, true);
+ 
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+   ok(doc.activeElement.closest(".tooltip-content"), "Focus is in the tooltip");
+ 
+   info("Move the focus to #box3-input while the tooltip is displayed");
+-  yield focusNode(doc, "#box3-input");
++  await focusNode(doc, "#box3-input");
+   ok(doc.activeElement.closest("#box3-input"), "Focus moved to the #box3-input");
+ 
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+   ok(doc.activeElement.closest("#box3-input"), "Focus is still in the #box3-input");
+ 
+   info("Blur the textbox before moving to the next test to reset the state.");
+-  yield blurNode(doc, "#box3-input");
++  await blurNode(doc, "#box3-input");
+ 
+   tooltip.destroy();
+ }
+ 
+ /**
+  * Fpcus the node corresponding to the provided selector in the provided document. Returns
+  * a promise that will resolve when receiving the focus event on the node.
+  */
+@@ -121,17 +121,17 @@ function blurNode(doc, selector) {
+  * Create an HTMLTooltip instance with the provided autofocus setting.
+  *
+  * @param {Document} doc
+  *        Document in which the tooltip should be created
+  * @param {Boolean} autofocus
+  * @return {Promise} promise that will resolve the HTMLTooltip instance created when the
+  *         tooltip content will be ready.
+  */
+-function* createTooltip(doc, autofocus) {
++function createTooltip(doc, autofocus) {
+   let tooltip = new HTMLTooltip(doc, {autofocus, useXulWrapper});
+   let div = doc.createElementNS(HTML_NS, "div");
+   div.classList.add("tooltip-content");
+   div.style.height = "50px";
+ 
+   let input = doc.createElementNS(HTML_NS, "input");
+   input.setAttribute("type", "text");
+   div.appendChild(input);
+diff --git a/devtools/client/shared/test/browser_html_tooltip-04.js b/devtools/client/shared/test/browser_html_tooltip-04.js
+--- a/devtools/client/shared/test/browser_html_tooltip-04.js
++++ b/devtools/client/shared/test/browser_html_tooltip-04.js
+@@ -13,84 +13,84 @@ const HTML_NS = "http://www.w3.org/1999/
+ const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip-04.xul";
+ 
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ loadHelperScript("helper_html_tooltip.js");
+ 
+ const TOOLTIP_HEIGHT = 30;
+ const TOOLTIP_WIDTH = 100;
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Force the toolbox to be 400px high;
+-  yield pushPref("devtools.toolbox.footer.height", 400);
++  await pushPref("devtools.toolbox.footer.height", 400);
+ 
+-  yield addTab("about:blank");
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++  await addTab("about:blank");
++  let [,, doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Create HTML tooltip");
+   let tooltip = new HTMLTooltip(doc, {useXulWrapper: false});
+   let div = doc.createElementNS(HTML_NS, "div");
+   div.style.height = "100%";
+   tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
+ 
+   let box1 = doc.getElementById("box1");
+   let box2 = doc.getElementById("box2");
+   let box3 = doc.getElementById("box3");
+   let box4 = doc.getElementById("box4");
+   let height = TOOLTIP_HEIGHT, width = TOOLTIP_WIDTH;
+ 
+   // box1: Can only fit below box1
+   info("Display the tooltip on box1.");
+-  yield showTooltip(tooltip, box1);
++  await showTooltip(tooltip, box1);
+   let expectedTooltipGeometry = {position: "bottom", height, width};
+   checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   info("Try to display the tooltip on top of box1.");
+-  yield showTooltip(tooltip, box1, {position: "top"});
++  await showTooltip(tooltip, box1, {position: "top"});
+   expectedTooltipGeometry = {position: "bottom", height, width};
+   checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   // box2: Can fit above or below, will default to bottom, more height
+   // available.
+   info("Try to display the tooltip on box2.");
+-  yield showTooltip(tooltip, box2);
++  await showTooltip(tooltip, box2);
+   expectedTooltipGeometry = {position: "bottom", height, width};
+   checkTooltipGeometry(tooltip, box2, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   info("Try to display the tooltip on top of box2.");
+-  yield showTooltip(tooltip, box2, {position: "top"});
++  await showTooltip(tooltip, box2, {position: "top"});
+   expectedTooltipGeometry = {position: "top", height, width};
+   checkTooltipGeometry(tooltip, box2, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   // box3: Can fit above or below, will default to top, more height available.
+   info("Try to display the tooltip on box3.");
+-  yield showTooltip(tooltip, box3);
++  await showTooltip(tooltip, box3);
+   expectedTooltipGeometry = {position: "top", height, width};
+   checkTooltipGeometry(tooltip, box3, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   info("Try to display the tooltip on bottom of box3.");
+-  yield showTooltip(tooltip, box3, {position: "bottom"});
++  await showTooltip(tooltip, box3, {position: "bottom"});
+   expectedTooltipGeometry = {position: "bottom", height, width};
+   checkTooltipGeometry(tooltip, box3, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   // box4: Can only fit above box4
+   info("Display the tooltip on box4.");
+-  yield showTooltip(tooltip, box4);
++  await showTooltip(tooltip, box4);
+   expectedTooltipGeometry = {position: "top", height, width};
+   checkTooltipGeometry(tooltip, box4, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   info("Try to display the tooltip on bottom of box4.");
+-  yield showTooltip(tooltip, box4, {position: "bottom"});
++  await showTooltip(tooltip, box4, {position: "bottom"});
+   expectedTooltipGeometry = {position: "top", height, width};
+   checkTooltipGeometry(tooltip, box4, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   is(tooltip.isVisible(), false, "Tooltip is not visible");
+ 
+   tooltip.destroy();
+ });
+diff --git a/devtools/client/shared/test/browser_html_tooltip-05.js b/devtools/client/shared/test/browser_html_tooltip-05.js
+--- a/devtools/client/shared/test/browser_html_tooltip-05.js
++++ b/devtools/client/shared/test/browser_html_tooltip-05.js
+@@ -13,86 +13,86 @@ const HTML_NS = "http://www.w3.org/1999/
+ const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip-05.xul";
+ 
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ loadHelperScript("helper_html_tooltip.js");
+ 
+ const TOOLTIP_HEIGHT = 200;
+ const TOOLTIP_WIDTH = 200;
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Force the toolbox to be 200px high;
+-  yield pushPref("devtools.toolbox.footer.height", 200);
+-  yield addTab("about:blank");
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++  await pushPref("devtools.toolbox.footer.height", 200);
++  await addTab("about:blank");
++  let [,, doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Create HTML tooltip");
+   let tooltip = new HTMLTooltip(doc, {useXulWrapper: false});
+   let div = doc.createElementNS(HTML_NS, "div");
+   div.style.height = "100%";
+   tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
+ 
+   let box1 = doc.getElementById("box1");
+   let box2 = doc.getElementById("box2");
+   let box3 = doc.getElementById("box3");
+   let box4 = doc.getElementById("box4");
+   let width = TOOLTIP_WIDTH;
+ 
+   // box1: Can not fit above or below box1, default to bottom with a reduced
+   // height of 150px.
+   info("Display the tooltip on box1.");
+-  yield showTooltip(tooltip, box1);
++  await showTooltip(tooltip, box1);
+   let expectedTooltipGeometry = {position: "bottom", height: 150, width};
+   checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   info("Try to display the tooltip on top of box1.");
+-  yield showTooltip(tooltip, box1, {position: "top"});
++  await showTooltip(tooltip, box1, {position: "top"});
+   expectedTooltipGeometry = {position: "bottom", height: 150, width};
+   checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   // box2: Can not fit above or below box2, default to bottom with a reduced
+   // height of 100px.
+   info("Try to display the tooltip on box2.");
+-  yield showTooltip(tooltip, box2);
++  await showTooltip(tooltip, box2);
+   expectedTooltipGeometry = {position: "bottom", height: 100, width};
+   checkTooltipGeometry(tooltip, box2, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   info("Try to display the tooltip on top of box2.");
+-  yield showTooltip(tooltip, box2, {position: "top"});
++  await showTooltip(tooltip, box2, {position: "top"});
+   expectedTooltipGeometry = {position: "bottom", height: 100, width};
+   checkTooltipGeometry(tooltip, box2, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   // box3: Can not fit above or below box3, default to top with a reduced height
+   // of 100px.
+   info("Try to display the tooltip on box3.");
+-  yield showTooltip(tooltip, box3);
++  await showTooltip(tooltip, box3);
+   expectedTooltipGeometry = {position: "top", height: 100, width};
+   checkTooltipGeometry(tooltip, box3, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   info("Try to display the tooltip on bottom of box3.");
+-  yield showTooltip(tooltip, box3, {position: "bottom"});
++  await showTooltip(tooltip, box3, {position: "bottom"});
+   expectedTooltipGeometry = {position: "top", height: 100, width};
+   checkTooltipGeometry(tooltip, box3, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   // box4: Can not fit above or below box4, default to top with a reduced height
+   // of 150px.
+   info("Display the tooltip on box4.");
+-  yield showTooltip(tooltip, box4);
++  await showTooltip(tooltip, box4);
+   expectedTooltipGeometry = {position: "top", height: 150, width};
+   checkTooltipGeometry(tooltip, box4, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   info("Try to display the tooltip on bottom of box4.");
+-  yield showTooltip(tooltip, box4, {position: "bottom"});
++  await showTooltip(tooltip, box4, {position: "bottom"});
+   expectedTooltipGeometry = {position: "top", height: 150, width};
+   checkTooltipGeometry(tooltip, box4, expectedTooltipGeometry);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   is(tooltip.isVisible(), false, "Tooltip is not visible");
+ 
+   tooltip.destroy();
+ });
+diff --git a/devtools/client/shared/test/browser_html_tooltip_arrow-01.js b/devtools/client/shared/test/browser_html_tooltip_arrow-01.js
+--- a/devtools/client/shared/test/browser_html_tooltip_arrow-01.js
++++ b/devtools/client/shared/test/browser_html_tooltip_arrow-01.js
+@@ -12,45 +12,45 @@
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_arrow-01.xul";
+ 
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ loadHelperScript("helper_html_tooltip.js");
+ 
+ let useXulWrapper;
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Force the toolbox to be 200px high;
+-  yield pushPref("devtools.toolbox.footer.height", 200);
++  await pushPref("devtools.toolbox.footer.height", 200);
+ 
+-  yield addTab("about:blank");
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++  await addTab("about:blank");
++  let [,, doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Run tests for a Tooltip without using a XUL panel");
+   useXulWrapper = false;
+-  yield runTests(doc);
++  await runTests(doc);
+ 
+   info("Run tests for a Tooltip with a XUL panel");
+   useXulWrapper = true;
+-  yield runTests(doc);
++  await runTests(doc);
+ });
+ 
+-function* runTests(doc) {
++async function runTests(doc) {
+   info("Create HTML tooltip");
+   let tooltip = new HTMLTooltip(doc, {type: "arrow", useXulWrapper});
+   let div = doc.createElementNS(HTML_NS, "div");
+   div.style.height = "35px";
+   tooltip.setContent(div, {width: 200, height: 35});
+ 
+   let {right: docRight} = doc.documentElement.getBoundingClientRect();
+ 
+   let elements = [...doc.querySelectorAll(".anchor")];
+   for (let el of elements) {
+     info("Display the tooltip on an anchor.");
+-    yield showTooltip(tooltip, el);
++    await showTooltip(tooltip, el);
+ 
+     let arrow = tooltip.arrow;
+     ok(arrow, "Tooltip has an arrow");
+ 
+     // Get the geometry of the anchor, the tooltip panel & arrow.
+     let arrowBounds = arrow.getBoxQuads({relativeTo: doc})[0].bounds;
+     let panelBounds = tooltip.panel.getBoxQuads({relativeTo: doc})[0].bounds;
+     let anchorBounds = el.getBoxQuads({relativeTo: doc})[0].bounds;
+@@ -62,13 +62,13 @@ function* runTests(doc) {
+     ok(intersects || isBlockedByViewport,
+       "Tooltip arrow is aligned with the anchor, or stuck on viewport's edge.");
+ 
+     let isInPanel = arrowBounds.left >= panelBounds.left &&
+                     arrowBounds.right <= panelBounds.right;
+     ok(isInPanel,
+       "The tooltip arrow remains inside the tooltip panel horizontally");
+ 
+-    yield hideTooltip(tooltip);
++    await hideTooltip(tooltip);
+   }
+ 
+   tooltip.destroy();
+ }
+diff --git a/devtools/client/shared/test/browser_html_tooltip_arrow-02.js b/devtools/client/shared/test/browser_html_tooltip_arrow-02.js
+--- a/devtools/client/shared/test/browser_html_tooltip_arrow-02.js
++++ b/devtools/client/shared/test/browser_html_tooltip_arrow-02.js
+@@ -12,44 +12,44 @@
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_arrow-02.xul";
+ 
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ loadHelperScript("helper_html_tooltip.js");
+ 
+ let useXulWrapper;
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Force the toolbox to be 200px high;
+-  yield pushPref("devtools.toolbox.footer.height", 200);
++  await pushPref("devtools.toolbox.footer.height", 200);
+ 
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++  let [,, doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Run tests for a Tooltip without using a XUL panel");
+   useXulWrapper = false;
+-  yield runTests(doc);
++  await runTests(doc);
+ 
+   info("Run tests for a Tooltip with a XUL panel");
+   useXulWrapper = true;
+-  yield runTests(doc);
++  await runTests(doc);
+ });
+ 
+-function* runTests(doc) {
++async function runTests(doc) {
+   info("Create HTML tooltip");
+   let tooltip = new HTMLTooltip(doc, {type: "arrow", useXulWrapper});
+   let div = doc.createElementNS(HTML_NS, "div");
+   div.style.height = "35px";
+   tooltip.setContent(div, {width: 200, height: 35});
+ 
+   let {right: docRight} = doc.documentElement.getBoundingClientRect();
+ 
+   let elements = [...doc.querySelectorAll(".anchor")];
+   for (let el of elements) {
+     info("Display the tooltip on an anchor.");
+-    yield showTooltip(tooltip, el);
++    await showTooltip(tooltip, el);
+ 
+     let arrow = tooltip.arrow;
+     ok(arrow, "Tooltip has an arrow");
+ 
+     // Get the geometry of the anchor, the tooltip panel & arrow.
+     let arrowBounds = arrow.getBoxQuads({relativeTo: doc})[0].bounds;
+     let panelBounds = tooltip.panel.getBoxQuads({relativeTo: doc})[0].bounds;
+     let anchorBounds = el.getBoxQuads({relativeTo: doc})[0].bounds;
+@@ -60,13 +60,13 @@ function* runTests(doc) {
+                               arrowBounds.right == docRight;
+     ok(intersects || isBlockedByViewport,
+       "Tooltip arrow is aligned with the anchor, or stuck on viewport's edge.");
+ 
+     let isInPanel = arrowBounds.left >= panelBounds.left &&
+                     arrowBounds.right <= panelBounds.right;
+     ok(isInPanel,
+       "The tooltip arrow remains inside the tooltip panel horizontally");
+-    yield hideTooltip(tooltip);
++    await hideTooltip(tooltip);
+   }
+ 
+   tooltip.destroy();
+ }
+diff --git a/devtools/client/shared/test/browser_html_tooltip_consecutive-show.js b/devtools/client/shared/test/browser_html_tooltip_consecutive-show.js
+--- a/devtools/client/shared/test/browser_html_tooltip_consecutive-show.js
++++ b/devtools/client/shared/test/browser_html_tooltip_consecutive-show.js
+@@ -17,18 +17,18 @@ loadHelperScript("helper_html_tooltip.js
+ 
+ function getTooltipContent(doc) {
+   let div = doc.createElementNS(HTML_NS, "div");
+   div.style.height = "50px";
+   div.textContent = "tooltip";
+   return div;
+ }
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+ 
+   let box1 = doc.getElementById("box1");
+   let box2 = doc.getElementById("box2");
+   let box3 = doc.getElementById("box3");
+   let box4 = doc.getElementById("box4");
+ 
+   let width = 100, height = 50;
+ 
+@@ -49,12 +49,12 @@ add_task(function* () {
+   tooltip.show(box3);
+   checkTooltipGeometry(tooltip, box3, {position: "top", width, height});
+ 
+   info("Show tooltip on box4");
+   tooltip.show(box4);
+   checkTooltipGeometry(tooltip, box4, {position: "top", width, height});
+ 
+   info("Hide tooltip before leaving test");
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   tooltip.destroy();
+ });
+diff --git a/devtools/client/shared/test/browser_html_tooltip_hover.js b/devtools/client/shared/test/browser_html_tooltip_hover.js
+--- a/devtools/client/shared/test/browser_html_tooltip_hover.js
++++ b/devtools/client/shared/test/browser_html_tooltip_hover.js
+@@ -9,45 +9,45 @@
+  */
+ 
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_hover.xul";
+ 
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ loadHelperScript("helper_html_tooltip.js");
+ 
+-add_task(function* () {
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [,, doc] = await createHost("bottom", TEST_URI);
+   // Wait for full page load before synthesizing events on the page.
+-  yield waitUntil(() => doc.readyState === "complete");
++  await waitUntil(() => doc.readyState === "complete");
+ 
+   let width = 100, height = 50;
+   let tooltipContent = doc.createElementNS(HTML_NS, "div");
+   tooltipContent.textContent = "tooltip";
+   let tooltip = new HTMLTooltip(doc, {useXulWrapper: false});
+   tooltip.setContent(tooltipContent, {width, height});
+ 
+   let container = doc.getElementById("container");
+   tooltip.startTogglingOnHover(container, () => true);
+ 
+   info("Hover on each of the 4 boxes, expect the tooltip to appear");
+-  function* showAndCheck(boxId, position) {
++  async function showAndCheck(boxId, position) {
+     info(`Show tooltip on ${boxId}`);
+     let box = doc.getElementById(boxId);
+     let shown = tooltip.once("shown");
+     EventUtils.synthesizeMouseAtCenter(box, { type: "mousemove" }, doc.defaultView);
+-    yield shown;
++    await shown;
+     checkTooltipGeometry(tooltip, box, {position, width, height});
+   }
+ 
+-  yield showAndCheck("box1", "bottom");
+-  yield showAndCheck("box2", "bottom");
+-  yield showAndCheck("box3", "top");
+-  yield showAndCheck("box4", "top");
++  await showAndCheck("box1", "bottom");
++  await showAndCheck("box2", "bottom");
++  await showAndCheck("box3", "top");
++  await showAndCheck("box4", "top");
+ 
+   info("Move out of the container");
+   let hidden = tooltip.once("hidden");
+   EventUtils.synthesizeMouseAtCenter(container, { type: "mousemove" }, doc.defaultView);
+-  yield hidden;
++  await hidden;
+ 
+   info("Destroy the tooltip and finish");
+   tooltip.destroy();
+ });
+diff --git a/devtools/client/shared/test/browser_html_tooltip_offset.js b/devtools/client/shared/test/browser_html_tooltip_offset.js
+--- a/devtools/client/shared/test/browser_html_tooltip_offset.js
++++ b/devtools/client/shared/test/browser_html_tooltip_offset.js
+@@ -8,21 +8,21 @@
+  */
+ 
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip.xul";
+ 
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ loadHelperScript("helper_html_tooltip.js");
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Force the toolbox to be 200px high;
+-  yield pushPref("devtools.toolbox.footer.height", 200);
++  await pushPref("devtools.toolbox.footer.height", 200);
+ 
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++  let [,, doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Test a tooltip is not closed when clicking inside itself");
+ 
+   let box1 = doc.getElementById("box1");
+   let box2 = doc.getElementById("box2");
+   let box3 = doc.getElementById("box3");
+   let box4 = doc.getElementById("box4");
+ 
+@@ -30,57 +30,57 @@ add_task(function* () {
+ 
+   let div = doc.createElementNS(HTML_NS, "div");
+   div.style.height = "100px";
+   div.style.boxSizing = "border-box";
+   div.textContent = "tooltip";
+   tooltip.setContent(div, {width: 50, height: 100});
+ 
+   info("Display the tooltip on box1.");
+-  yield showTooltip(tooltip, box1, {x: 5, y: 10});
++  await showTooltip(tooltip, box1, {x: 5, y: 10});
+ 
+   let panelRect = tooltip.container.getBoundingClientRect();
+   let anchorRect = box1.getBoundingClientRect();
+ 
+   // Tooltip will be displayed below box1
+   is(panelRect.top, anchorRect.bottom + 10, "Tooltip top has 10px offset");
+   is(panelRect.left, anchorRect.left + 5, "Tooltip left has 5px offset");
+   is(panelRect.height, 100, "Tooltip height is at 100px as expected");
+ 
+   info("Display the tooltip on box2.");
+-  yield showTooltip(tooltip, box2, {x: 5, y: 10});
++  await showTooltip(tooltip, box2, {x: 5, y: 10});
+ 
+   panelRect = tooltip.container.getBoundingClientRect();
+   anchorRect = box2.getBoundingClientRect();
+ 
+   // Tooltip will be displayed below box2, but can't be fully displayed because of the
+   // offset
+   is(panelRect.top, anchorRect.bottom + 10, "Tooltip top has 10px offset");
+   is(panelRect.left, anchorRect.left + 5, "Tooltip left has 5px offset");
+   is(panelRect.height, 90, "Tooltip height is only 90px");
+ 
+   info("Display the tooltip on box3.");
+-  yield showTooltip(tooltip, box3, {x: 5, y: 10});
++  await showTooltip(tooltip, box3, {x: 5, y: 10});
+ 
+   panelRect = tooltip.container.getBoundingClientRect();
+   anchorRect = box3.getBoundingClientRect();
+ 
+   // Tooltip will be displayed above box3, but can't be fully displayed because of the
+   // offset
+   is(panelRect.bottom, anchorRect.top - 10, "Tooltip bottom is 10px above anchor");
+   is(panelRect.left, anchorRect.left + 5, "Tooltip left has 5px offset");
+   is(panelRect.height, 90, "Tooltip height is only 90px");
+ 
+   info("Display the tooltip on box4.");
+-  yield showTooltip(tooltip, box4, {x: 5, y: 10});
++  await showTooltip(tooltip, box4, {x: 5, y: 10});
+ 
+   panelRect = tooltip.container.getBoundingClientRect();
+   anchorRect = box4.getBoundingClientRect();
+ 
+   // Tooltip will be displayed above box4
+   is(panelRect.bottom, anchorRect.top - 10, "Tooltip bottom is 10px above anchor");
+   is(panelRect.left, anchorRect.left + 5, "Tooltip left has 5px offset");
+   is(panelRect.height, 100, "Tooltip height is at 100px as expected");
+ 
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   tooltip.destroy();
+ });
+diff --git a/devtools/client/shared/test/browser_html_tooltip_rtl.js b/devtools/client/shared/test/browser_html_tooltip_rtl.js
+--- a/devtools/client/shared/test/browser_html_tooltip_rtl.js
++++ b/devtools/client/shared/test/browser_html_tooltip_rtl.js
+@@ -14,38 +14,38 @@ const TEST_URI = CHROME_URL_ROOT + "doc_
+ 
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ loadHelperScript("helper_html_tooltip.js");
+ 
+ const TOOLBOX_WIDTH = 500;
+ const TOOLTIP_WIDTH = 150;
+ const TOOLTIP_HEIGHT = 30;
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Force the toolbox to be 500px wide (min width is 465px);
+-  yield pushPref("devtools.toolbox.sidebar.width", TOOLBOX_WIDTH);
++  await pushPref("devtools.toolbox.sidebar.width", TOOLBOX_WIDTH);
+ 
+-  let [,, doc] = yield createHost("side", TEST_URI);
++  let [,, doc] = await createHost("side", TEST_URI);
+ 
+   info("Test a tooltip is not closed when clicking inside itself");
+ 
+   let tooltip = new HTMLTooltip(doc, {useXulWrapper: false});
+   let div = doc.createElementNS(HTML_NS, "div");
+   div.textContent = "tooltip";
+   div.style.cssText = "box-sizing: border-box; border: 1px solid black";
+   tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
+ 
+-  yield testRtlAnchors(doc, tooltip);
+-  yield testLtrAnchors(doc, tooltip);
+-  yield hideTooltip(tooltip);
++  await testRtlAnchors(doc, tooltip);
++  await testLtrAnchors(doc, tooltip);
++  await hideTooltip(tooltip);
+ 
+   tooltip.destroy();
+ });
+ 
+-function* testRtlAnchors(doc, tooltip) {
++async function testRtlAnchors(doc, tooltip) {
+   /*
+    * The layout of the test page is as follows:
+    *   _______________________________
+    *  | toolbox                       |
+    *  | _____   _____   _____   _____ |
+    *  ||     | |     | |     | |     ||
+    *  || box1| | box2| | box3| | box4||
+    *  ||_____| |_____| |_____| |_____||
+@@ -55,40 +55,40 @@ function* testRtlAnchors(doc, tooltip) {
+    * - box2 is displayed right after box1
+    * - total toolbox width is 500px so each box is 125px wide
+   */
+ 
+   let box1 = doc.getElementById("box1");
+   let box2 = doc.getElementById("box2");
+ 
+   info("Display the tooltip on box1.");
+-  yield showTooltip(tooltip, box1, {position: "bottom"});
++  await showTooltip(tooltip, box1, {position: "bottom"});
+ 
+   let panelRect = tooltip.container.getBoundingClientRect();
+   let anchorRect = box1.getBoundingClientRect();
+ 
+   // box1 uses RTL direction, so the tooltip should be aligned with the right edge of the
+   // anchor, but it is shifted to the right to fit in the toolbox.
+   is(panelRect.left, 0, "Tooltip is aligned with left edge of the toolbox");
+   is(panelRect.top, anchorRect.bottom, "Tooltip aligned with the anchor bottom edge");
+   is(panelRect.height, TOOLTIP_HEIGHT, "Tooltip height is at 100px as expected");
+ 
+   info("Display the tooltip on box2.");
+-  yield showTooltip(tooltip, box2, {position: "bottom"});
++  await showTooltip(tooltip, box2, {position: "bottom"});
+ 
+   panelRect = tooltip.container.getBoundingClientRect();
+   anchorRect = box2.getBoundingClientRect();
+ 
+   // box2 uses RTL direction, so the tooltip is aligned with the right edge of the anchor
+   is(panelRect.right, anchorRect.right, "Tooltip is aligned with right edge of anchor");
+   is(panelRect.top, anchorRect.bottom, "Tooltip aligned with the anchor bottom edge");
+   is(panelRect.height, TOOLTIP_HEIGHT, "Tooltip height is at 100px as expected");
+ }
+ 
+-function* testLtrAnchors(doc, tooltip) {
++async function testLtrAnchors(doc, tooltip) {
+     /*
+    * The layout of the test page is as follows:
+    *   _______________________________
+    *  | toolbox                       |
+    *  | _____   _____   _____   _____ |
+    *  ||     | |     | |     | |     ||
+    *  || box1| | box2| | box3| | box4||
+    *  ||_____| |_____| |_____| |_____||
+@@ -98,28 +98,28 @@ function* testLtrAnchors(doc, tooltip) {
+    * - box4 is aligned with the right edge of the toolbox
+    * - total toolbox width is 500px so each box is 125px wide
+   */
+ 
+   let box3 = doc.getElementById("box3");
+   let box4 = doc.getElementById("box4");
+ 
+   info("Display the tooltip on box3.");
+-  yield showTooltip(tooltip, box3, {position: "bottom"});
++  await showTooltip(tooltip, box3, {position: "bottom"});
+ 
+   let panelRect = tooltip.container.getBoundingClientRect();
+   let anchorRect = box3.getBoundingClientRect();
+ 
+   // box3 uses LTR direction, so the tooltip is aligned with the left edge of the anchor.
+   is(panelRect.left, anchorRect.left, "Tooltip is aligned with left edge of anchor");
+   is(panelRect.top, anchorRect.bottom, "Tooltip aligned with the anchor bottom edge");
+   is(panelRect.height, TOOLTIP_HEIGHT, "Tooltip height is at 100px as expected");
+ 
+   info("Display the tooltip on box4.");
+-  yield showTooltip(tooltip, box4, {position: "bottom"});
++  await showTooltip(tooltip, box4, {position: "bottom"});
+ 
+   panelRect = tooltip.container.getBoundingClientRect();
+   anchorRect = box4.getBoundingClientRect();
+ 
+   // box4 uses LTR direction, so the tooltip should be aligned with the left edge of the
+   // anchor, but it is shifted to the left to fit in the toolbox.
+   is(panelRect.right, TOOLBOX_WIDTH, "Tooltip is aligned with right edge of toolbox");
+   is(panelRect.top, anchorRect.bottom, "Tooltip aligned with the anchor bottom edge");
+diff --git a/devtools/client/shared/test/browser_html_tooltip_variable-height.js b/devtools/client/shared/test/browser_html_tooltip_variable-height.js
+--- a/devtools/client/shared/test/browser_html_tooltip_variable-height.js
++++ b/devtools/client/shared/test/browser_html_tooltip_variable-height.js
+@@ -13,52 +13,52 @@ const TEST_URI = CHROME_URL_ROOT + "doc_
+ 
+ const CONTAINER_HEIGHT = 300;
+ const CONTAINER_WIDTH = 200;
+ const TOOLTIP_HEIGHT = 50;
+ 
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ loadHelperScript("helper_html_tooltip.js");
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Force the toolbox to be 400px tall => 50px for each box.
+-  yield pushPref("devtools.toolbox.footer.height", 400);
++  await pushPref("devtools.toolbox.footer.height", 400);
+ 
+-  yield addTab("about:blank");
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++  await addTab("about:blank");
++  let [,, doc] = await createHost("bottom", TEST_URI);
+ 
+   let tooltip = new HTMLTooltip(doc, {useXulWrapper: false});
+   info("Set tooltip content 50px tall, but request a container 200px tall");
+   let tooltipContent = doc.createElementNS(HTML_NS, "div");
+   tooltipContent.style.cssText = "height: " + TOOLTIP_HEIGHT + "px; background: red;";
+   tooltip.setContent(tooltipContent, {width: CONTAINER_WIDTH, height: Infinity});
+ 
+   info("Show the tooltip and check the container and panel height.");
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+ 
+   let containerRect = tooltip.container.getBoundingClientRect();
+   let panelRect = tooltip.panel.getBoundingClientRect();
+   is(containerRect.height, CONTAINER_HEIGHT,
+     "Tooltip container has the expected height.");
+   is(panelRect.height, TOOLTIP_HEIGHT, "Tooltip panel has the expected height.");
+ 
+   info("Click below the tooltip panel but in the tooltip filler element.");
+   let onHidden = once(tooltip, "hidden");
+   EventUtils.synthesizeMouse(tooltip.container, 100, 100, {}, doc.defaultView);
+-  yield onHidden;
++  await onHidden;
+ 
+   info("Show the tooltip one more time, and increase the content height");
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+   tooltipContent.style.height = (2 * CONTAINER_HEIGHT) + "px";
+ 
+   info("Click at the same coordinates as earlier, this time it should hit the tooltip.");
+   let onPanelClick = once(tooltip.panel, "click");
+   EventUtils.synthesizeMouse(tooltip.container, 100, 100, {}, doc.defaultView);
+-  yield onPanelClick;
++  await onPanelClick;
+   is(tooltip.isVisible(), true, "Tooltip is still visible");
+ 
+   info("Click above the tooltip container, the tooltip should be closed.");
+   onHidden = once(tooltip, "hidden");
+   EventUtils.synthesizeMouse(tooltip.container, 100, -10, {}, doc.defaultView);
+-  yield onHidden;
++  await onHidden;
+ 
+   tooltip.destroy();
+ });
+diff --git a/devtools/client/shared/test/browser_html_tooltip_width-auto.js b/devtools/client/shared/test/browser_html_tooltip_width-auto.js
+--- a/devtools/client/shared/test/browser_html_tooltip_width-auto.js
++++ b/devtools/client/shared/test/browser_html_tooltip_width-auto.js
+@@ -11,40 +11,40 @@
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip.xul";
+ 
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ loadHelperScript("helper_html_tooltip.js");
+ 
+ let useXulWrapper;
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  let [,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  await addTab("about:blank");
++  let [,, doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Run tests for a Tooltip without using a XUL panel");
+   useXulWrapper = false;
+-  yield runTests(doc);
++  await runTests(doc);
+ 
+   info("Run tests for a Tooltip with a XUL panel");
+   useXulWrapper = true;
+-  yield runTests(doc);
++  await runTests(doc);
+ });
+ 
+-function* runTests(doc) {
++async function runTests(doc) {
+   let tooltip = new HTMLTooltip(doc, {useXulWrapper});
+   info("Create tooltip content width to 150px");
+   let tooltipContent = doc.createElementNS(HTML_NS, "div");
+   tooltipContent.style.cssText = "height: 100%; width: 150px; background: red;";
+ 
+   info("Set tooltip content using width:auto");
+   tooltip.setContent(tooltipContent, {width: "auto", height: 50});
+ 
+   info("Show the tooltip and check the tooltip panel width.");
+-  yield showTooltip(tooltip, doc.getElementById("box1"));
++  await showTooltip(tooltip, doc.getElementById("box1"));
+ 
+   let panelRect = tooltip.panel.getBoundingClientRect();
+   is(panelRect.width, 150, "Tooltip panel has the expected width.");
+ 
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   tooltip.destroy();
+ }
+diff --git a/devtools/client/shared/test/browser_html_tooltip_xul-wrapper.js b/devtools/client/shared/test/browser_html_tooltip_xul-wrapper.js
+--- a/devtools/client/shared/test/browser_html_tooltip_xul-wrapper.js
++++ b/devtools/client/shared/test/browser_html_tooltip_xul-wrapper.js
+@@ -15,21 +15,21 @@ const {HTMLTooltip} = require("devtools/
+ loadHelperScript("helper_html_tooltip.js");
+ 
+ // The test toolbox will be 200px tall, the anchors are 50px tall, therefore, the maximum
+ // tooltip height that could fit in the toolbox is 150px. Setting 160px, the tooltip will
+ // either have to overflow or to be resized.
+ const TOOLTIP_HEIGHT = 160;
+ const TOOLTIP_WIDTH = 200;
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Force the toolbox to be 200px high;
+-  yield pushPref("devtools.toolbox.footer.height", 200);
++  await pushPref("devtools.toolbox.footer.height", 200);
+ 
+-  let [, win, doc] = yield createHost("bottom", TEST_URI);
++  let [, win, doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Resize and move the window to have space below.");
+   let originalWidth = win.top.outerWidth;
+   let originalHeight = win.top.outerHeight;
+   win.top.resizeBy(-100, -200);
+   let originalTop = win.top.screenTop;
+   let originalLeft = win.top.screenLeft;
+   win.top.moveTo(100, 100);
+@@ -46,25 +46,25 @@ add_task(function* () {
+   div.style.height = "200px";
+   div.style.background = "red";
+   tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
+ 
+   let box1 = doc.getElementById("box1");
+ 
+   // Above box1: check that the tooltip can overflow onto the content page.
+   info("Display the tooltip above box1.");
+-  yield showTooltip(tooltip, box1, {position: "top"});
++  await showTooltip(tooltip, box1, {position: "top"});
+   checkTooltip(tooltip, "top", TOOLTIP_HEIGHT);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   // Below box1: check that the tooltip can overflow out of the browser window.
+   info("Display the tooltip below box1.");
+-  yield showTooltip(tooltip, box1, {position: "bottom"});
++  await showTooltip(tooltip, box1, {position: "bottom"});
+   checkTooltip(tooltip, "bottom", TOOLTIP_HEIGHT);
+-  yield hideTooltip(tooltip);
++  await hideTooltip(tooltip);
+ 
+   is(tooltip.isVisible(), false, "Tooltip is not visible");
+ 
+   tooltip.destroy();
+ });
+ 
+ function checkTooltip(tooltip, position, height) {
+   is(tooltip.position, position, "Actual tooltip position is " + position);
+diff --git a/devtools/client/shared/test/browser_inplace-editor-01.js b/devtools/client/shared/test/browser_inplace-editor-01.js
+--- a/devtools/client/shared/test/browser_inplace-editor-01.js
++++ b/devtools/client/shared/test/browser_inplace-editor-01.js
+@@ -4,26 +4,26 @@
+ /* import-globals-from helper_inplace_editor.js */
+ 
+ "use strict";
+ 
+ loadHelperScript("helper_inplace_editor.js");
+ 
+ // Test the inplace-editor behavior.
+ 
+-add_task(function* () {
+-  yield addTab("data:text/html;charset=utf-8,inline editor tests");
+-  let [host, , doc] = yield createHost();
++add_task(async function() {
++  await addTab("data:text/html;charset=utf-8,inline editor tests");
++  let [host, , doc] = await createHost();
+ 
+-  yield testMultipleInitialization(doc);
+-  yield testReturnCommit(doc);
+-  yield testBlurCommit(doc);
+-  yield testAdvanceCharCommit(doc);
+-  yield testAdvanceCharsFunction(doc);
+-  yield testEscapeCancel(doc);
++  await testMultipleInitialization(doc);
++  await testReturnCommit(doc);
++  await testBlurCommit(doc);
++  await testAdvanceCharCommit(doc);
++  await testAdvanceCharsFunction(doc);
++  await testEscapeCancel(doc);
+ 
+   host.destroy();
+   gBrowser.removeCurrentTab();
+ });
+ 
+ function testMultipleInitialization(doc) {
+   doc.body.innerHTML = "";
+   let options = {};
+diff --git a/devtools/client/shared/test/browser_inplace-editor-02.js b/devtools/client/shared/test/browser_inplace-editor-02.js
+--- a/devtools/client/shared/test/browser_inplace-editor-02.js
++++ b/devtools/client/shared/test/browser_inplace-editor-02.js
+@@ -4,22 +4,22 @@
+ /* import-globals-from helper_inplace_editor.js */
+ 
+ "use strict";
+ 
+ loadHelperScript("helper_inplace_editor.js");
+ 
+ // Test that the trimOutput option for the inplace editor works correctly.
+ 
+-add_task(function* () {
+-  yield addTab("data:text/html;charset=utf-8,inline editor tests");
+-  let [host, , doc] = yield createHost();
++add_task(async function() {
++  await addTab("data:text/html;charset=utf-8,inline editor tests");
++  let [host, , doc] = await createHost();
+ 
+-  yield testNonTrimmed(doc);
+-  yield testTrimmed(doc);
++  await testNonTrimmed(doc);
++  await testTrimmed(doc);
+ 
+   host.destroy();
+   gBrowser.removeCurrentTab();
+ });
+ 
+ function testNonTrimmed(doc) {
+   info("Testing the trimOutput=false option");
+   let def = defer();
+diff --git a/devtools/client/shared/test/browser_inplace-editor_autocomplete_01.js b/devtools/client/shared/test/browser_inplace-editor_autocomplete_01.js
+--- a/devtools/client/shared/test/browser_inplace-editor_autocomplete_01.js
++++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_01.js
+@@ -37,39 +37,39 @@ const mockGetCSSPropertyList = function(
+     "border",
+     "box-sizing",
+     "color",
+     "display",
+     "visibility",
+   ];
+ };
+ 
+-add_task(function* () {
+-  yield addTab("data:text/html;charset=utf-8," +
++add_task(async function() {
++  await addTab("data:text/html;charset=utf-8," +
+     "inplace editor CSS property autocomplete");
+-  let [host, win, doc] = yield createHost();
++  let [host, win, doc] = await createHost();
+ 
+   let xulDocument = win.top.document;
+   let popup = new AutocompletePopup(xulDocument, { autoSelect: true });
+-  yield new Promise(resolve => {
++  await new Promise(resolve => {
+     createInplaceEditorAndClick({
+       start: runPropertyAutocompletionTest,
+       contentType: InplaceEditor.CONTENT_TYPES.CSS_PROPERTY,
+       done: resolve,
+       popup: popup
+     }, doc);
+   });
+ 
+   popup.destroy();
+   host.destroy();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-let runPropertyAutocompletionTest = Task.async(function* (editor) {
++let runPropertyAutocompletionTest = async function(editor) {
+   info("Starting to test for css property completion");
+   editor._getCSSPropertyList = mockGetCSSPropertyList;
+ 
+   for (let data of testData) {
+-    yield testCompletion(data, editor);
++    await testCompletion(data, editor);
+   }
+ 
+   EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
+-});
++};
+diff --git a/devtools/client/shared/test/browser_inplace-editor_autocomplete_02.js b/devtools/client/shared/test/browser_inplace-editor_autocomplete_02.js
+--- a/devtools/client/shared/test/browser_inplace-editor_autocomplete_02.js
++++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_02.js
+@@ -38,43 +38,43 @@ const mockGetCSSValuesForPropertyName = 
+       "inline",
+       "inline-block",
+       "none",
+     ]
+   };
+   return values[propertyName] || [];
+ };
+ 
+-add_task(function* () {
+-  yield addTab("data:text/html;charset=utf-8," +
++add_task(async function() {
++  await addTab("data:text/html;charset=utf-8," +
+     "inplace editor CSS value autocomplete");
+-  let [host, win, doc] = yield createHost();
++  let [host, win, doc] = await createHost();
+ 
+   let xulDocument = win.top.document;
+   let popup = new AutocompletePopup(xulDocument, { autoSelect: true });
+ 
+-  yield new Promise(resolve => {
++  await new Promise(resolve => {
+     createInplaceEditorAndClick({
+       start: runAutocompletionTest,
+       contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
+       property: {
+         name: "display"
+       },
+       done: resolve,
+       popup: popup
+     }, doc);
+   });
+ 
+   popup.destroy();
+   host.destroy();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-let runAutocompletionTest = Task.async(function* (editor) {
++let runAutocompletionTest = async function(editor) {
+   info("Starting to test for css property completion");
+   editor._getCSSValuesForPropertyName = mockGetCSSValuesForPropertyName;
+ 
+   for (let data of testData) {
+-    yield testCompletion(data, editor);
++    await testCompletion(data, editor);
+   }
+ 
+   EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
+-});
++};
+diff --git a/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js b/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js
+--- a/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js
++++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js
+@@ -41,44 +41,44 @@ const mockGetCSSValuesForPropertyName = 
+ 
+ const mockGetCSSVariableNames = function() {
+   return [
+     "--abc",
+     "--def",
+   ];
+ };
+ 
+-add_task(function* () {
+-  yield addTab("data:text/html;charset=utf-8," +
++add_task(async function() {
++  await addTab("data:text/html;charset=utf-8," +
+     "inplace editor CSS variable autocomplete");
+-  let [host, win, doc] = yield createHost();
++  let [host, win, doc] = await createHost();
+ 
+   let xulDocument = win.top.document;
+   let popup = new AutocompletePopup(xulDocument, { autoSelect: true });
+ 
+-  yield new Promise(resolve => {
++  await new Promise(resolve => {
+     createInplaceEditorAndClick({
+       start: runAutocompletionTest,
+       contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
+       property: {
+         name: "color"
+       },
+       done: resolve,
+       popup: popup
+     }, doc);
+   });
+ 
+   popup.destroy();
+   host.destroy();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-let runAutocompletionTest = Task.async(function* (editor) {
++let runAutocompletionTest = async function(editor) {
+   info("Starting to test for css variable completion");
+   editor._getCSSValuesForPropertyName = mockGetCSSValuesForPropertyName;
+   editor._getCSSVariableNames = mockGetCSSVariableNames;
+ 
+   for (let data of testData) {
+-    yield testCompletion(data, editor);
++    await testCompletion(data, editor);
+   }
+ 
+   EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
+-});
++};
+diff --git a/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js b/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js
+--- a/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js
++++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js
+@@ -54,60 +54,60 @@ const mockGetCSSPropertyList = function(
+ const mockGetCSSValuesForPropertyName = function(propertyName) {
+   let values = {
+     "color": ["blue", "red"],
+     "display": ["block", "flex", "none"]
+   };
+   return values[propertyName] || [];
+ };
+ 
+-add_task(function* () {
+-  yield addTab("data:text/html;charset=utf-8,inplace editor CSS value autocomplete");
+-  let [host,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  await addTab("data:text/html;charset=utf-8,inplace editor CSS value autocomplete");
++  let [host,, doc] = await createHost("bottom", TEST_URI);
+ 
+   let popup = new AutocompletePopup(doc, { autoSelect: true });
+ 
+   info("Create a CSS_MIXED type autocomplete");
+-  yield new Promise(resolve => {
++  await new Promise(resolve => {
+     createInplaceEditorAndClick({
+       initial: "style=",
+       start: runAutocompletionTest,
+       contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
+       done: resolve,
+       popup: popup
+     }, doc);
+   });
+ 
+   popup.destroy();
+   host.destroy();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-let runAutocompletionTest = Task.async(function* (editor) {
++let runAutocompletionTest = async function(editor) {
+   info("Starting autocomplete test for inplace-editor popup offset");
+   editor._getCSSPropertyList = mockGetCSSPropertyList;
+   editor._getCSSValuesForPropertyName = mockGetCSSValuesForPropertyName;
+ 
+   let previousOffset = -1;
+   for (let data of testData) {
+     if (data[0] === "checkPopupOffset") {
+       info("Check the popup offset has been modified");
+       // We are not testing hard coded offset values here, which could be fragile. We only
+       // want to ensure the popup tries to match the position of the query in the editor
+       // input.
+       let offset = getPopupOffset(editor);
+       ok(offset > previousOffset, "New popup offset is greater than the previous one");
+       previousOffset = offset;
+     } else {
+-      yield testCompletion(data, editor);
++      await testCompletion(data, editor);
+     }
+   }
+ 
+   EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
+-});
++};
+ 
+ /**
+  * Get the autocomplete panel left offset, relative to the provided input's left offset.
+  */
+ function getPopupOffset({popup, input}) {
+   let popupQuads = popup._panel.getBoxQuads({relativeTo: input});
+   return popupQuads[0].bounds.left;
+ }
+diff --git a/devtools/client/shared/test/browser_inplace-editor_maxwidth.js b/devtools/client/shared/test/browser_inplace-editor_maxwidth.js
+--- a/devtools/client/shared/test/browser_inplace-editor_maxwidth.js
++++ b/devtools/client/shared/test/browser_inplace-editor_maxwidth.js
+@@ -10,35 +10,35 @@ loadHelperScript("helper_inplace_editor.
+ const MAX_WIDTH = 300;
+ const START_TEXT = "Start text";
+ const LONG_TEXT = "I am a long text and I will not fit in a 300px container. " +
+   "I expect the inplace editor to wrap.";
+ 
+ // Test the inplace-editor behavior with a maxWidth configuration option
+ // defined.
+ 
+-add_task(function* () {
+-  yield addTab("data:text/html;charset=utf-8,inplace editor max width tests");
+-  let [host, , doc] = yield createHost();
++add_task(async function() {
++  await addTab("data:text/html;charset=utf-8,inplace editor max width tests");
++  let [host, , doc] = await createHost();
+ 
+   info("Testing the maxWidth option in pixels, to precisely check the size");
+-  yield new Promise(resolve => {
++  await new Promise(resolve => {
+     createInplaceEditorAndClick({
+       multiline: true,
+       maxWidth: MAX_WIDTH,
+       start: testMaxWidth,
+       done: resolve
+     }, doc, START_TEXT);
+   });
+ 
+   host.destroy();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-let testMaxWidth = Task.async(function* (editor) {
++let testMaxWidth = async function(editor) {
+   is(editor.input.value, START_TEXT, "Span text content should be used");
+   ok(editor.input.offsetWidth < MAX_WIDTH,
+     "Input width should be strictly smaller than MAX_WIDTH");
+   is(getLines(editor.input), 1, "Input should display 1 line of text");
+ 
+   info("Check a text is on several lines if it does not fit MAX_WIDTH");
+   for (let key of LONG_TEXT) {
+     EventUtils.sendChar(key);
+@@ -81,17 +81,17 @@ let testMaxWidth = Task.async(function* 
+     "Input width should again be strictly smaller than MAX_WIDTH");
+   ok(editor.input.offsetWidth > 0,
+     "Even with no content, the input has a non-zero width");
+   is(getLines(editor.input), 1, "Input should display 1 line of text");
+   checkScrollbars(editor.input);
+ 
+   info("Leave the inplace-editor");
+   EventUtils.sendKey("RETURN");
+-});
++};
+ 
+ /**
+  * Retrieve the current number of lines displayed in the provided textarea.
+  *
+  * @param {DOMNode} textarea
+  * @return {Number} the number of lines
+  */
+ function getLines(textarea) {
+diff --git a/devtools/client/shared/test/browser_key_shortcuts.js b/devtools/client/shared/test/browser_key_shortcuts.js
+--- a/devtools/client/shared/test/browser_key_shortcuts.js
++++ b/devtools/client/shared/test/browser_key_shortcuts.js
+@@ -1,37 +1,37 @@
+ /* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ var isOSX = Services.appinfo.OS === "Darwin";
+ 
+-add_task(function* () {
++add_task(async function() {
+   let shortcuts = new KeyShortcuts({
+     window
+   });
+ 
+-  yield testSimple(shortcuts);
+-  yield testNonLetterCharacter(shortcuts);
+-  yield testPlusCharacter(shortcuts);
+-  yield testFunctionKey(shortcuts);
+-  yield testMixup(shortcuts);
+-  yield testLooseDigits(shortcuts);
+-  yield testExactModifiers(shortcuts);
+-  yield testLooseShiftModifier(shortcuts);
+-  yield testStrictLetterShiftModifier(shortcuts);
+-  yield testAltModifier(shortcuts);
+-  yield testCommandOrControlModifier(shortcuts);
+-  yield testCtrlModifier(shortcuts);
+-  yield testInvalidShortcutString(shortcuts);
+-  yield testCmdShiftShortcut(shortcuts);
++  await testSimple(shortcuts);
++  await testNonLetterCharacter(shortcuts);
++  await testPlusCharacter(shortcuts);
++  await testFunctionKey(shortcuts);
++  await testMixup(shortcuts);
++  await testLooseDigits(shortcuts);
++  await testExactModifiers(shortcuts);
++  await testLooseShiftModifier(shortcuts);
++  await testStrictLetterShiftModifier(shortcuts);
++  await testAltModifier(shortcuts);
++  await testCommandOrControlModifier(shortcuts);
++  await testCtrlModifier(shortcuts);
++  await testInvalidShortcutString(shortcuts);
++  await testCmdShiftShortcut(shortcuts);
+   shortcuts.destroy();
+ 
+-  yield testTarget();
++  await testTarget();
+ });
+ 
+ // Test helper to listen to the next key press for a given key,
+ // returning a promise to help using Tasks.
+ function once(shortcuts, key, listener) {
+   let called = false;
+   return new Promise(done => {
+     let onShortcut = event => {
+@@ -40,136 +40,136 @@ function once(shortcuts, key, listener) 
+       called = true;
+       listener(event);
+       done();
+     };
+     shortcuts.on(key, onShortcut);
+   });
+ }
+ 
+-function* testSimple(shortcuts) {
++async function testSimple(shortcuts) {
+   info("Test simple key shortcuts");
+ 
+   let onKey = once(shortcuts, "0", event => {
+     is(event.key, "0");
+ 
+     // Display another key press to ensure that once() correctly stop listening
+     EventUtils.synthesizeKey("0", {}, window);
+   });
+ 
+   EventUtils.synthesizeKey("0", {}, window);
+-  yield onKey;
++  await onKey;
+ }
+ 
+-function* testNonLetterCharacter(shortcuts) {
++async function testNonLetterCharacter(shortcuts) {
+   info("Test non-naive character key shortcuts");
+ 
+   let onKey = once(shortcuts, "[", event => {
+     is(event.key, "[");
+   });
+ 
+   EventUtils.synthesizeKey("[", {}, window);
+-  yield onKey;
++  await onKey;
+ }
+ 
+-function* testFunctionKey(shortcuts) {
++async function testFunctionKey(shortcuts) {
+   info("Test function key shortcuts");
+ 
+   let onKey = once(shortcuts, "F12", event => {
+     is(event.key, "F12");
+   });
+ 
+   EventUtils.synthesizeKey("F12", { keyCode: 123 }, window);
+-  yield onKey;
++  await onKey;
+ }
+ 
+ // Plus is special. It's keycode is the one for "=". That's because it requires
+ // shift to be pressed and is behind "=" key. So it should be considered as a
+ // character key
+-function* testPlusCharacter(shortcuts) {
++async function testPlusCharacter(shortcuts) {
+   info("Test 'Plus' key shortcuts");
+ 
+   let onKey = once(shortcuts, "Plus", event => {
+     is(event.key, "+");
+   });
+ 
+   EventUtils.synthesizeKey("+", { keyCode: 61, shiftKey: true }, window);
+-  yield onKey;
++  await onKey;
+ }
+ 
+ // Test they listeners are not mixed up between shortcuts
+-function* testMixup(shortcuts) {
++async function testMixup(shortcuts) {
+   info("Test possible listener mixup");
+ 
+   let hitFirst = false, hitSecond = false;
+   let onFirstKey = once(shortcuts, "0", event => {
+     is(event.key, "0");
+     hitFirst = true;
+   });
+   let onSecondKey = once(shortcuts, "Alt+A", event => {
+     is(event.key, "a");
+     ok(event.altKey);
+     hitSecond = true;
+   });
+ 
+   // Dispatch the first shortcut and expect only this one to be notified
+   ok(!hitFirst, "First shortcut isn't notified before firing the key event");
+   EventUtils.synthesizeKey("0", {}, window);
+-  yield onFirstKey;
++  await onFirstKey;
+   ok(hitFirst, "Got the first shortcut notified");
+   ok(!hitSecond, "No mixup, second shortcut is still not notified (1/2)");
+ 
+   // Wait an extra time, just to be sure this isn't racy
+-  yield new Promise(done => {
++  await new Promise(done => {
+     window.setTimeout(done, 0);
+   });
+   ok(!hitSecond, "No mixup, second shortcut is still not notified (2/2)");
+ 
+   // Finally dispatch the second shortcut
+   EventUtils.synthesizeKey("a", { altKey: true }, window);
+-  yield onSecondKey;
++  await onSecondKey;
+   ok(hitSecond, "Got the second shortcut notified once it is actually fired");
+ }
+ 
+ // On azerty keyboard, digits are only available by pressing Shift/Capslock,
+ // but we accept them even if we omit doing that.
+-function* testLooseDigits(shortcuts) {
++async function testLooseDigits(shortcuts) {
+   info("Test Loose digits");
+   let onKey = once(shortcuts, "0", event => {
+     is(event.key, "à");
+     ok(!event.altKey);
+     ok(!event.ctrlKey);
+     ok(!event.metaKey);
+     ok(!event.shiftKey);
+   });
+   // Simulate a press on the "0" key, without shift pressed on a french
+   // keyboard
+   EventUtils.synthesizeKey(
+     "à",
+     { keyCode: 48 },
+     window);
+-  yield onKey;
++  await onKey;
+ 
+   onKey = once(shortcuts, "0", event => {
+     is(event.key, "0");
+     ok(!event.altKey);
+     ok(!event.ctrlKey);
+     ok(!event.metaKey);
+     ok(event.shiftKey);
+   });
+   // Simulate the same press with shift pressed
+   EventUtils.synthesizeKey(
+     "0",
+     { keyCode: 48, shiftKey: true },
+     window);
+-  yield onKey;
++  await onKey;
+ }
+ 
+ // Test that shortcuts is notified only when the modifiers match exactly
+-function* testExactModifiers(shortcuts) {
++async function testExactModifiers(shortcuts) {
+   info("Test exact modifiers match");
+ 
+   let hit = false;
+   let onKey = once(shortcuts, "Alt+A", event => {
+     is(event.key, "a");
+     ok(event.altKey);
+     ok(!event.ctrlKey);
+     ok(!event.metaKey);
+@@ -191,63 +191,63 @@ function* testExactModifiers(shortcuts) 
+     { accelKey: false, altKey: false, shiftKey: true },
+     window);
+   EventUtils.synthesizeKey(
+     "a",
+     { accelKey: false, altKey: false, shiftKey: false },
+     window);
+ 
+   // Wait an extra time to let a chance to call the listener
+-  yield new Promise(done => {
++  await new Promise(done => {
+     window.setTimeout(done, 0);
+   });
+   ok(!hit, "Listener isn't called when modifiers aren't exactly matching");
+ 
+   // Dispatch the expected modifiers
+   EventUtils.synthesizeKey("a", { accelKey: false, altKey: true, shiftKey: false},
+                            window);
+-  yield onKey;
++  await onKey;
+   ok(hit, "Got shortcut notified once it is actually fired");
+ }
+ 
+ // Some keys are only accessible via shift and listener should also be called
+ // even if the key didn't explicitely requested Shift modifier.
+ // For example, `%` on french keyboards is only accessible via Shift.
+ // Same thing for `@` on US keybords.
+-function* testLooseShiftModifier(shortcuts) {
++async function testLooseShiftModifier(shortcuts) {
+   info("Test Loose shift modifier");
+   let onKey = once(shortcuts, "%", event => {
+     is(event.key, "%");
+     ok(!event.altKey);
+     ok(!event.ctrlKey);
+     ok(!event.metaKey);
+     ok(event.shiftKey);
+   });
+   EventUtils.synthesizeKey(
+     "%",
+     { accelKey: false, altKey: false, ctrlKey: false, shiftKey: true},
+     window);
+-  yield onKey;
++  await onKey;
+ 
+   onKey = once(shortcuts, "@", event => {
+     is(event.key, "@");
+     ok(!event.altKey);
+     ok(!event.ctrlKey);
+     ok(!event.metaKey);
+     ok(event.shiftKey);
+   });
+   EventUtils.synthesizeKey(
+     "@",
+     { accelKey: false, altKey: false, ctrlKey: false, shiftKey: true},
+     window);
+-  yield onKey;
++  await onKey;
+ }
+ 
+ // But Shift modifier is strict on all letter characters (a to Z)
+-function* testStrictLetterShiftModifier(shortcuts) {
++async function testStrictLetterShiftModifier(shortcuts) {
+   info("Test strict shift modifier on letters");
+   let hitFirst = false;
+   let onKey = once(shortcuts, "a", event => {
+     is(event.key, "a");
+     ok(!event.altKey);
+     ok(!event.ctrlKey);
+     ok(!event.metaKey);
+     ok(!event.shiftKey);
+@@ -259,43 +259,43 @@ function* testStrictLetterShiftModifier(
+     ok(!event.ctrlKey);
+     ok(!event.metaKey);
+     ok(event.shiftKey);
+   });
+   EventUtils.synthesizeKey(
+     "a",
+     { shiftKey: true},
+     window);
+-  yield onShiftKey;
++  await onShiftKey;
+   ok(!hitFirst, "Didn't fire the explicit shift+a");
+ 
+   EventUtils.synthesizeKey(
+     "a",
+     { shiftKey: false},
+     window);
+-  yield onKey;
++  await onKey;
+ }
+ 
+-function* testAltModifier(shortcuts) {
++async function testAltModifier(shortcuts) {
+   info("Test Alt modifier");
+   let onKey = once(shortcuts, "Alt+F1", event => {
+     is(event.keyCode, window.KeyboardEvent.DOM_VK_F1);
+     ok(event.altKey);
+     ok(!event.ctrlKey);
+     ok(!event.metaKey);
+     ok(!event.shiftKey);
+   });
+   EventUtils.synthesizeKey(
+     "VK_F1",
+     { altKey: true },
+     window);
+-  yield onKey;
++  await onKey;
+ }
+ 
+-function* testCommandOrControlModifier(shortcuts) {
++async function testCommandOrControlModifier(shortcuts) {
+   info("Test CommandOrControl modifier");
+   let onKey = once(shortcuts, "CommandOrControl+F1", event => {
+     is(event.keyCode, window.KeyboardEvent.DOM_VK_F1);
+     ok(!event.altKey);
+     if (isOSX) {
+       ok(!event.ctrlKey);
+       ok(event.metaKey);
+     } else {
+@@ -322,21 +322,21 @@ function* testCommandOrControlModifier(s
+       { metaKey: true },
+       window);
+   } else {
+     EventUtils.synthesizeKey(
+       "VK_F1",
+       { ctrlKey: true },
+       window);
+   }
+-  yield onKey;
+-  yield onKeyAlias;
++  await onKey;
++  await onKeyAlias;
+ }
+ 
+-function* testCtrlModifier(shortcuts) {
++async function testCtrlModifier(shortcuts) {
+   info("Test Ctrl modifier");
+   let onKey = once(shortcuts, "Ctrl+F1", event => {
+     is(event.keyCode, window.KeyboardEvent.DOM_VK_F1);
+     ok(!event.altKey);
+     ok(event.ctrlKey);
+     ok(!event.metaKey);
+     ok(!event.shiftKey);
+   });
+@@ -346,21 +346,21 @@ function* testCtrlModifier(shortcuts) {
+     ok(event.ctrlKey);
+     ok(!event.metaKey);
+     ok(!event.shiftKey);
+   });
+   EventUtils.synthesizeKey(
+     "VK_F1",
+     { ctrlKey: true },
+     window);
+-  yield onKey;
+-  yield onKeyAlias;
++  await onKey;
++  await onKeyAlias;
+ }
+ 
+-function* testCmdShiftShortcut(shortcuts) {
++async function testCmdShiftShortcut(shortcuts) {
+   if (!isOSX) {
+     // This test is OSX only (Bug 1300458).
+     return;
+   }
+ 
+   let onCmdKey = once(shortcuts, "CmdOrCtrl+[", event => {
+     is(event.key, "[");
+     ok(!event.altKey);
+@@ -380,38 +380,38 @@ function* testCmdShiftShortcut(shortcuts
+     "[",
+     { metaKey: true, shiftKey: true },
+     window);
+   EventUtils.synthesizeKey(
+     "[",
+     { metaKey: true },
+     window);
+ 
+-  yield onCmdKey;
+-  yield onCmdShiftKey;
++  await onCmdKey;
++  await onCmdShiftKey;
+ }
+ 
+-function* testTarget() {
++async function testTarget() {
+   info("Test KeyShortcuts with target argument");
+ 
+   let target = document.createElementNS("http://www.w3.org/1999/xhtml",
+     "input");
+   document.documentElement.appendChild(target);
+   target.focus();
+ 
+   let shortcuts = new KeyShortcuts({
+     window,
+     target
+   });
+   let onKey = once(shortcuts, "0", event => {
+     is(event.key, "0");
+     is(event.target, target);
+   });
+   EventUtils.synthesizeKey("0", {}, window);
+-  yield onKey;
++  await onKey;
+ 
+   target.remove();
+ 
+   shortcuts.destroy();
+ }
+ 
+ function testInvalidShortcutString(shortcuts) {
+   info("Test wrong shortcut string");
+diff --git a/devtools/client/shared/test/browser_layoutHelpers.js b/devtools/client/shared/test/browser_layoutHelpers.js
+--- a/devtools/client/shared/test/browser_layoutHelpers.js
++++ b/devtools/client/shared/test/browser_layoutHelpers.js
+@@ -3,18 +3,18 @@
+ 
+ "use strict";
+ 
+ // Tests that scrollIntoViewIfNeeded works properly.
+ const {scrollIntoViewIfNeeded} = require("devtools/client/shared/scroll");
+ 
+ const TEST_URI = TEST_URI_ROOT + "doc_layoutHelpers.html";
+ 
+-add_task(function* () {
+-  let [host, win] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [host, win] = await createHost("bottom", TEST_URI);
+   runTest(win);
+   host.destroy();
+ });
+ 
+ function runTest(win) {
+   let some = win.document.getElementById("some");
+ 
+   some.style.top = win.innerHeight + "px";
+diff --git a/devtools/client/shared/test/browser_options-view-01.js b/devtools/client/shared/test/browser_options-view-01.js
+--- a/devtools/client/shared/test/browser_options-view-01.js
++++ b/devtools/client/shared/test/browser_options-view-01.js
+@@ -9,40 +9,40 @@ const {OptionsView} = require("devtools/
+ 
+ const BRANCH = "devtools.debugger.";
+ const BLACK_BOX_PREF = "auto-black-box";
+ const PRETTY_PRINT_PREF = "auto-pretty-print";
+ 
+ const originalBlackBox = Services.prefs.getBoolPref(BRANCH + BLACK_BOX_PREF);
+ const originalPrettyPrint = Services.prefs.getBoolPref(BRANCH + PRETTY_PRINT_PREF);
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Setting a couple of preferences");
+   Services.prefs.setBoolPref(BRANCH + BLACK_BOX_PREF, false);
+   Services.prefs.setBoolPref(BRANCH + PRETTY_PRINT_PREF, true);
+ 
+   info("Opening a test tab and a toolbox host to create the options view in");
+-  yield addTab("about:blank");
+-  let [host, win] = yield createHost("bottom", OPTIONS_VIEW_URL);
++  await addTab("about:blank");
++  let [host, win] = await createHost("bottom", OPTIONS_VIEW_URL);
+ 
+-  yield testOptionsView(win);
++  await testOptionsView(win);
+ 
+   info("Closing the host and current tab");
+   host.destroy();
+   gBrowser.removeCurrentTab();
+ 
+   info("Resetting the preferences");
+   Services.prefs.setBoolPref(BRANCH + BLACK_BOX_PREF, originalBlackBox);
+   Services.prefs.setBoolPref(BRANCH + PRETTY_PRINT_PREF, originalPrettyPrint);
+ });
+ 
+-function* testOptionsView(win) {
++async function testOptionsView(win) {
+   let events = [];
+   let options = createOptionsView(win);
+-  yield options.initialize();
++  await options.initialize();
+ 
+   let $ = win.document.querySelector.bind(win.document);
+ 
+   options.on("pref-changed", pref => events.push(pref));
+ 
+   let ppEl = $("menuitem[data-pref='auto-pretty-print']");
+   let bbEl = $("menuitem[data-pref='auto-black-box']");
+ 
+@@ -63,48 +63,48 @@ function* testOptionsView(win) {
+   // Tests events are fired when preferences update outside of the menu
+   is(events.length, 2, "two 'pref-changed' events fired");
+   is(events[0], "auto-pretty-print",
+      "correct pref passed in 'pref-changed' event (auto-pretty-print)");
+   is(events[1], "auto-black-box",
+      "correct pref passed in 'pref-changed' event (auto-black-box)");
+ 
+   // Test buttons update when clicked and preferences are updated
+-  yield click(options, win, ppEl);
++  await click(options, win, ppEl);
+   is(ppEl.getAttribute("checked"), "true", "menuitems update when clicked");
+   is(Services.prefs.getBoolPref(BRANCH + PRETTY_PRINT_PREF),
+      true, "preference updated via click");
+ 
+-  yield click(options, win, bbEl);
++  await click(options, win, bbEl);
+   is(bbEl.getAttribute("checked"), "", "menuitems update when clicked");
+   is(Services.prefs.getBoolPref(BRANCH + BLACK_BOX_PREF),
+      false, "preference updated via click");
+ 
+   // Tests events are fired when preferences updated via click
+   is(events.length, 4, "two 'pref-changed' events fired");
+   is(events[2], "auto-pretty-print",
+      "correct pref passed in 'pref-changed' event (auto-pretty-print)");
+   is(events[3], "auto-black-box",
+      "correct pref passed in 'pref-changed' event (auto-black-box)");
+ 
+-  yield options.destroy();
++  await options.destroy();
+ }
+ 
+ function createOptionsView(win) {
+   return new OptionsView({
+     branchName: BRANCH,
+     menupopup: win.document.querySelector("#options-menupopup")
+   });
+ }
+ 
+-function* click(view, win, menuitem) {
++async function click(view, win, menuitem) {
+   let opened = view.once("options-shown");
+   let closed = view.once("options-hidden");
+ 
+   let button = win.document.querySelector("#options-button");
+   EventUtils.synthesizeMouseAtCenter(button, {}, win);
+-  yield opened;
++  await opened;
+   is(button.getAttribute("open"), "true", "button has `open` attribute");
+ 
+   EventUtils.synthesizeMouseAtCenter(menuitem, {}, win);
+-  yield closed;
++  await closed;
+   ok(!button.hasAttribute("open"), "button does not have `open` attribute");
+ }
+diff --git a/devtools/client/shared/test/browser_outputparser.js b/devtools/client/shared/test/browser_outputparser.js
+--- a/devtools/client/shared/test/browser_outputparser.js
++++ b/devtools/client/shared/test/browser_outputparser.js
+@@ -2,29 +2,29 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const OutputParser = require("devtools/client/shared/output-parser");
+ const {initCssProperties, getCssProperties} = require("devtools/shared/fronts/css-properties");
+ const CSS_SHAPES_ENABLED_PREF = "devtools.inspector.shapesHighlighter.enabled";
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  yield performTest();
++add_task(async function() {
++  await addTab("about:blank");
++  await performTest();
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* performTest() {
+-  let [host, , doc] = yield createHost("bottom", "data:text/html," +
++async function performTest() {
++  let [host, , doc] = await createHost("bottom", "data:text/html," +
+     "<h1>browser_outputParser.js</h1><div></div>");
+ 
+   // Mock the toolbox that initCssProperties expect so we get the fallback css properties.
+   let toolbox = {target: {client: {}, hasActor: () => false}};
+-  yield initCssProperties(toolbox);
++  await initCssProperties(toolbox);
+   let cssProperties = getCssProperties(toolbox);
+ 
+   let parser = new OutputParser(doc, cssProperties);
+   testParseCssProperty(doc, parser);
+   testParseCssVar(doc, parser);
+   testParseURL(doc, parser);
+   testParseFilter(doc, parser);
+   testParseAngle(doc, parser);
+diff --git a/devtools/client/shared/test/browser_poller.js b/devtools/client/shared/test/browser_poller.js
+--- a/devtools/client/shared/test/browser_poller.js
++++ b/devtools/client/shared/test/browser_poller.js
+@@ -3,17 +3,17 @@
+ /* eslint-disable mozilla/no-arbitrary-setTimeout */
+ 
+ "use strict";
+ 
+ // Tests the Poller class.
+ 
+ const { Poller } = require("devtools/client/shared/poller");
+ 
+-add_task(function* () {
++add_task(async function() {
+   let count1 = 0, count2 = 0, count3 = 0;
+ 
+   let poller1 = new Poller(function() {
+     count1++;
+   }, 1000000000, true);
+   let poller2 = new Poller(function() {
+     count2++;
+   }, 10);
+@@ -21,46 +21,46 @@ add_task(function* () {
+     count3++;
+   }, 1000000000);
+ 
+   poller2.on();
+ 
+   ok(!poller1.isPolling(), "isPolling() returns false for an off poller");
+   ok(poller2.isPolling(), "isPolling() returns true for an on poller");
+ 
+-  yield waitUntil(() => count2 > 10);
++  await waitUntil(() => count2 > 10);
+ 
+   ok(count2 > 10, "poller that was turned on polled several times");
+   ok(count1 === 0, "poller that was never turned on never polled");
+ 
+-  yield poller2.off();
++  await poller2.off();
+   let currentCount2 = count2;
+ 
+   // Really high poll time!
+   poller1.on();
+   poller3.on();
+ 
+-  yield waitUntil(() => count1 === 1);
++  await waitUntil(() => count1 === 1);
+   ok(true, "Poller calls fn immediately when `immediate` is true");
+   ok(count3 === 0, "Poller does not call fn immediately when `immediate` is not set");
+ 
+   ok(count2 === currentCount2, "a turned off poller does not continue to poll");
+-  yield poller2.off();
+-  yield poller2.off();
+-  yield poller2.off();
++  await poller2.off();
++  await poller2.off();
++  await poller2.off();
+   ok(true, "Poller.prototype.off() is idempotent");
+ 
+   // This should still have not polled a second time
+   is(count1, 1, "wait time works");
+ 
+   ok(poller1.isPolling(), "isPolling() returns true for an on poller");
+   ok(!poller2.isPolling(), "isPolling() returns false for an off poller");
+ });
+ 
+-add_task(function* () {
++add_task(async function() {
+   let count = -1;
+   // Create a poller that returns a promise.
+   // The promise is resolved asynchronously after adding 9 to the count, ensuring
+   // that on every poll, we have a multiple of 10.
+   let asyncPoller = new Poller(function() {
+     count++;
+     ok(!(count % 10), `Async poller called with a multiple of 10: ${count}`);
+     return new Promise(function(resolve, reject) {
+@@ -72,21 +72,21 @@ add_task(function* () {
+           clearInterval(interval);
+           resolve();
+         }
+       }, 10);
+     });
+   });
+ 
+   asyncPoller.on(1);
+-  yield waitUntil(() => count > 50);
+-  yield asyncPoller.off();
++  await waitUntil(() => count > 50);
++  await asyncPoller.off();
+ });
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Create a poller that returns a promise. This poll call
+   // is called immediately, and then subsequently turned off.
+   // The call to `off` should not resolve until the inflight call
+   // finishes.
+   let inflightFinished = null;
+   let pollCalls = 0;
+   let asyncPoller = new Poller(function() {
+     pollCalls++;
+@@ -94,23 +94,23 @@ add_task(function* () {
+       setTimeout(() => {
+         inflightFinished = true;
+         resolve();
+       }, 1000);
+     });
+   }, 1, true);
+   asyncPoller.on();
+ 
+-  yield asyncPoller.off();
++  await asyncPoller.off();
+   ok(inflightFinished,
+      "off() method does not resolve until remaining inflight poll calls finish");
+   is(pollCalls, 1, "should only be one poll call to occur before turning off polling");
+ });
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Create a poller that returns a promise. This poll call
+   // is called immediately, and then subsequently turned off.
+   // The call to `off` should not resolve until the inflight call
+   // finishes.
+   let inflightFinished = null;
+   let pollCalls = 0;
+   let asyncPoller = new Poller(function() {
+     pollCalls++;
+@@ -118,17 +118,17 @@ add_task(function* () {
+       setTimeout(() => {
+         inflightFinished = true;
+         resolve();
+       }, 1000);
+     });
+   }, 1, true);
+   asyncPoller.on();
+ 
+-  yield asyncPoller.destroy();
++  await asyncPoller.destroy();
+   ok(inflightFinished,
+      "destroy() method does not resolve until remaining inflight poll calls finish");
+   is(pollCalls, 1, "should only be one poll call to occur before destroying polling");
+ 
+   try {
+     asyncPoller.on();
+     ok(false, "Calling on() after destruction should throw");
+   } catch (e) {
+diff --git a/devtools/client/shared/test/browser_spectrum.js b/devtools/client/shared/test/browser_spectrum.js
+--- a/devtools/client/shared/test/browser_spectrum.js
++++ b/devtools/client/shared/test/browser_spectrum.js
+@@ -5,26 +5,26 @@
+ "use strict";
+ 
+ // Tests that the spectrum color picker works correctly
+ 
+ const {Spectrum} = require("devtools/client/shared/widgets/Spectrum");
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_spectrum.html";
+ 
+-add_task(function* () {
+-  let [host,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  let [host,, doc] = await createHost("bottom", TEST_URI);
+ 
+   let container = doc.getElementById("spectrum-container");
+ 
+-  yield testCreateAndDestroyShouldAppendAndRemoveElements(container);
+-  yield testPassingAColorAtInitShouldSetThatColor(container);
+-  yield testSettingAndGettingANewColor(container);
+-  yield testChangingColorShouldEmitEvents(container);
+-  yield testSettingColorShoudUpdateTheUI(container);
++  await testCreateAndDestroyShouldAppendAndRemoveElements(container);
++  await testPassingAColorAtInitShouldSetThatColor(container);
++  await testSettingAndGettingANewColor(container);
++  await testChangingColorShouldEmitEvents(container);
++  await testSettingColorShoudUpdateTheUI(container);
+ 
+   host.destroy();
+ });
+ 
+ function testCreateAndDestroyShouldAppendAndRemoveElements(container) {
+   ok(container, "We have the root node to append spectrum to");
+   is(container.childElementCount, 0, "Root node is empty");
+ 
+diff --git a/devtools/client/shared/test/browser_tableWidget_basic.js b/devtools/client/shared/test/browser_tableWidget_basic.js
+--- a/devtools/client/shared/test/browser_tableWidget_basic.js
++++ b/devtools/client/shared/test/browser_tableWidget_basic.js
+@@ -5,19 +5,19 @@
+ // Tests that the table widget api works fine
+ 
+ "use strict";
+ 
+ const TEST_URI = CHROME_URL_ROOT + "doc_tableWidget_basic.html";
+ 
+ const {TableWidget} = require("devtools/client/shared/widgets/TableWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  let [host, , doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  await addTab("about:blank");
++  let [host, , doc] = await createHost("bottom", TEST_URI);
+ 
+   let table = new TableWidget(doc.querySelector("box"), {
+     initialColumns: {
+       col1: "Column 1",
+       col2: "Column 2",
+       col3: "Column 3",
+       col4: "Column 4"
+     },
+diff --git a/devtools/client/shared/test/browser_tableWidget_keyboard_interaction.js b/devtools/client/shared/test/browser_tableWidget_keyboard_interaction.js
+--- a/devtools/client/shared/test/browser_tableWidget_keyboard_interaction.js
++++ b/devtools/client/shared/test/browser_tableWidget_keyboard_interaction.js
+@@ -39,21 +39,21 @@ function test() {
+ 
+ function endTests() {
+   table.destroy();
+   doc.defaultView.close();
+   doc = table = null;
+   finish();
+ }
+ 
+-var startTests = Task.async(function* () {
++var startTests = async function() {
+   populateTable();
+-  yield testKeyboardInteraction();
++  await testKeyboardInteraction();
+   endTests();
+-});
++};
+ 
+ function populateTable() {
+   table.push({
+     col1: "id1",
+     col2: "value10",
+     col3: "value20",
+     col4: "value30"
+   });
+@@ -126,55 +126,55 @@ function click(node, button = 0) {
+ function getNodeByValue(value) {
+   return table.tbody.querySelector("[value=" + value + "]");
+ }
+ 
+ /**
+  * Tests if pressing navigation keys on the table items does the expected
+  * behavior.
+  */
+-var testKeyboardInteraction = Task.async(function* () {
++var testKeyboardInteraction = async function() {
+   info("Testing keyboard interaction with the table");
+   info("clicking on the row containing id2");
+   let node = getNodeByValue("id2");
+   let event = table.once(TableWidget.EVENTS.ROW_SELECTED);
+   click(node);
+-  yield event;
++  await event;
+ 
+-  yield testRow("id3", "DOWN", "next row");
+-  yield testRow("id4", "DOWN", "next row");
+-  yield testRow("id3", "UP", "previous row");
+-  yield testRow("id4", "DOWN", "next row");
+-  yield testRow("id5", "DOWN", "next row");
+-  yield testRow("id6", "DOWN", "next row");
+-  yield testRow("id5", "UP", "previous row");
+-  yield testRow("id4", "UP", "previous row");
+-  yield testRow("id3", "UP", "previous row");
++  await testRow("id3", "DOWN", "next row");
++  await testRow("id4", "DOWN", "next row");
++  await testRow("id3", "UP", "previous row");
++  await testRow("id4", "DOWN", "next row");
++  await testRow("id5", "DOWN", "next row");
++  await testRow("id6", "DOWN", "next row");
++  await testRow("id5", "UP", "previous row");
++  await testRow("id4", "UP", "previous row");
++  await testRow("id3", "UP", "previous row");
+ 
+   // selecting last item node to test edge navigation cycling case
+   table.selectedRow = "id9";
+ 
+   // pressing down on last row should move to first row.
+-  yield testRow("id1", "DOWN", "first row");
++  await testRow("id1", "DOWN", "first row");
+ 
+   // pressing up now should move to last row.
+-  yield testRow("id9", "UP", "last row");
+-});
++  await testRow("id9", "UP", "last row");
++};
+ 
+-function* testRow(id, key, destination) {
++async function testRow(id, key, destination) {
+   let node = getNodeByValue(id);
+   // node should not have selected class
+   ok(!node.classList.contains("theme-selected"),
+      "Row should not have selected class");
+   info(`Pressing ${key} to select ${destination}`);
+ 
+   let event = table.once(TableWidget.EVENTS.ROW_SELECTED);
+   EventUtils.sendKey(key, doc.defaultView);
+ 
+-  let uniqueId = yield event;
++  let uniqueId = await event;
+   is(id, uniqueId, `Correct row was selected after pressing ${key}`);
+ 
+   ok(node.classList.contains("theme-selected"), "row has selected class");
+ 
+   let nodes = doc.querySelectorAll(".theme-selected");
+   for (let i = 0; i < nodes.length; i++) {
+     is(nodes[i].getAttribute("data-id"), id,
+        "Correct cell selected in all columns");
+diff --git a/devtools/client/shared/test/browser_tableWidget_mouse_interaction.js b/devtools/client/shared/test/browser_tableWidget_mouse_interaction.js
+--- a/devtools/client/shared/test/browser_tableWidget_mouse_interaction.js
++++ b/devtools/client/shared/test/browser_tableWidget_mouse_interaction.js
+@@ -40,21 +40,21 @@ function test() {
+ 
+ function endTests() {
+   table.destroy();
+   doc.defaultView.close();
+   doc = table = null;
+   finish();
+ }
+ 
+-var startTests = Task.async(function* () {
++var startTests = async function() {
+   populateTable();
+-  yield testMouseInteraction();
++  await testMouseInteraction();
+   endTests();
+-});
++};
+ 
+ function populateTable() {
+   table.push({
+     col1: "id1",
+     col2: "value10",
+     col3: "value20",
+     col4: "value30"
+   });
+@@ -122,139 +122,139 @@ function click(node, button = 0) {
+       type: "contextmenu"
+     }, doc.defaultView));
+   }
+ }
+ 
+ /**
+  * Tests if clicking the table items does the expected behavior
+  */
+-var testMouseInteraction = Task.async(function* () {
++var testMouseInteraction = async function() {
+   info("Testing mouse interaction with the table");
+   ok(!table.selectedRow, "Nothing should be selected beforehand");
+ 
+   let event = table.once(TableWidget.EVENTS.ROW_SELECTED);
+   let firstColumnFirstRowCell = table.tbody.firstChild.firstChild.children[1];
+   info("clicking on the first row");
+   ok(!firstColumnFirstRowCell.classList.contains("theme-selected"),
+      "Node should not have selected class before clicking");
+   click(firstColumnFirstRowCell);
+-  let id = yield event;
++  let id = await event;
+   ok(firstColumnFirstRowCell.classList.contains("theme-selected"),
+      "Node has selected class after click");
+   is(id, "id1", "Correct row was selected");
+ 
+   info("clicking on second row to select it");
+   event = table.once(TableWidget.EVENTS.ROW_SELECTED);
+   let firstColumnSecondRowCell = table.tbody.firstChild.firstChild.children[2];
+   // node should not have selected class
+   ok(!firstColumnSecondRowCell.classList.contains("theme-selected"),
+      "New node should not have selected class before clicking");
+   click(firstColumnSecondRowCell);
+-  id = yield event;
++  id = await event;
+   ok(firstColumnSecondRowCell.classList.contains("theme-selected"),
+      "New node has selected class after clicking");
+   is(id, "id2", "Correct table path is emitted for new node");
+   isnot(firstColumnFirstRowCell, firstColumnSecondRowCell,
+     "Old and new node are different");
+   ok(!firstColumnFirstRowCell.classList.contains("theme-selected"),
+      "Old node should not have selected class after the click on new node");
+ 
+   info("clicking on the third row cell content to select third row");
+   event = table.once(TableWidget.EVENTS.ROW_SELECTED);
+   let firstColumnThirdRowCell = table.tbody.firstChild.firstChild.children[3];
+   let firstColumnThirdRowCellInnerNode = firstColumnThirdRowCell.querySelector("span");
+   // node should not have selected class
+   ok(!firstColumnThirdRowCell.classList.contains("theme-selected"),
+      "New node should not have selected class before clicking");
+   click(firstColumnThirdRowCellInnerNode);
+-  id = yield event;
++  id = await event;
+   ok(firstColumnThirdRowCell.classList.contains("theme-selected"),
+      "New node has selected class after clicking the cell content");
+   is(id, "id3", "Correct table path is emitted for new node");
+ 
+   // clicking on table header to sort by it
+   event = table.once(TableWidget.EVENTS.COLUMN_SORTED);
+   let node = table.tbody.children[6].firstChild.children[0];
+   info("clicking on the 4th coulmn header to sort the table by it");
+   ok(!node.hasAttribute("sorted"),
+      "Node should not have sorted attribute before clicking");
+   ok(doc.querySelector("[sorted]"),
+      "Although, something else should be sorted on");
+   isnot(doc.querySelector("[sorted]"), node, "Which is not equal to this node");
+   click(node);
+-  id = yield event;
++  id = await event;
+   is(id, "col4", "Correct column was sorted on");
+   ok(node.hasAttribute("sorted"),
+      "Node should now have sorted attribute after clicking");
+   is(doc.querySelectorAll("[sorted]").length, 1,
+      "Now only one column should be sorted on");
+   is(doc.querySelector("[sorted]"), node, "Which should be this column");
+ 
+   // test context menu opening.
+   // hiding second column
+   // event listener for popupshown
+   info("right click on the first column header");
+   node = table.tbody.firstChild.firstChild.firstChild;
+   let onPopupShown = once(table.menupopup, "popupshown");
+   click(node, 2);
+-  yield onPopupShown;
++  await onPopupShown;
+ 
+   is(table.menupopup.querySelectorAll("[disabled]").length, 1,
+      "Only 1 menuitem is disabled");
+   is(table.menupopup.querySelector("[disabled]"),
+      table.menupopup.querySelector("[data-id='col1']"),
+      "Which is the unique column");
+   // popup should be open now
+   // clicking on second column label
+   let onPopupHidden = once(table.menupopup, "popuphidden");
+   event = table.once(TableWidget.EVENTS.HEADER_CONTEXT_MENU);
+   node = table.menupopup.querySelector("[data-id='col2']");
+   info("selecting to hide the second column");
+   ok(!table.tbody.children[2].hasAttribute("hidden"),
+      "Column is not hidden before hiding it");
+   click(node);
+-  id = yield event;
+-  yield onPopupHidden;
++  id = await event;
++  await onPopupHidden;
+   is(id, "col2", "Correct column was triggered to be hidden");
+   is(table.tbody.children[2].getAttribute("hidden"), "true",
+      "Column is hidden after hiding it");
+ 
+   // hiding third column
+   // event listener for popupshown
+   info("right clicking on the first column header");
+   node = table.tbody.firstChild.firstChild.firstChild;
+   onPopupShown = once(table.menupopup, "popupshown");
+   click(node, 2);
+-  yield onPopupShown;
++  await onPopupShown;
+ 
+   is(table.menupopup.querySelectorAll("[disabled]").length, 1,
+      "Only 1 menuitem is disabled");
+   // popup should be open now
+   // clicking on second column label
+   onPopupHidden = once(table.menupopup, "popuphidden");
+   event = table.once(TableWidget.EVENTS.HEADER_CONTEXT_MENU);
+   node = table.menupopup.querySelector("[data-id='col3']");
+   info("selecting to hide the second column");
+   ok(!table.tbody.children[4].hasAttribute("hidden"),
+      "Column is not hidden before hiding it");
+   click(node);
+-  id = yield event;
+-  yield onPopupHidden;
++  id = await event;
++  await onPopupHidden;
+   is(id, "col3", "Correct column was triggered to be hidden");
+   is(table.tbody.children[4].getAttribute("hidden"), "true",
+      "Column is hidden after hiding it");
+ 
+   // opening again to see if 2 items are disabled now
+   // event listener for popupshown
+   info("right clicking on the first column header");
+   node = table.tbody.firstChild.firstChild.firstChild;
+   onPopupShown = once(table.menupopup, "popupshown");
+   click(node, 2);
+-  yield onPopupShown;
++  await onPopupShown;
+ 
+   is(table.menupopup.querySelectorAll("[disabled]").length, 2,
+      "2 menuitems are disabled now as only 2 columns remain visible");
+   is(table.menupopup.querySelectorAll("[disabled]")[0],
+      table.menupopup.querySelector("[data-id='col1']"),
+      "First is the unique column");
+   is(table.menupopup.querySelectorAll("[disabled]")[1],
+      table.menupopup.querySelector("[data-id='col4']"),
+@@ -265,41 +265,41 @@ var testMouseInteraction = Task.async(fu
+   // clicking on second column label
+   onPopupHidden = once(table.menupopup, "popuphidden");
+   event = table.once(TableWidget.EVENTS.HEADER_CONTEXT_MENU);
+   node = table.menupopup.querySelector("[data-id='col2']");
+   info("selecting to hide the second column");
+   is(table.tbody.children[2].getAttribute("hidden"), "true",
+      "Column is hidden before unhiding it");
+   click(node);
+-  id = yield event;
+-  yield onPopupHidden;
++  id = await event;
++  await onPopupHidden;
+   is(id, "col2", "Correct column was triggered to be hidden");
+   ok(!table.tbody.children[2].hasAttribute("hidden"),
+      "Column is not hidden after unhiding it");
+ 
+   // showing back 3rd column
+   // event listener for popupshown
+   info("right clicking on the first column header");
+   node = table.tbody.firstChild.firstChild.firstChild;
+   onPopupShown = once(table.menupopup, "popupshown");
+   click(node, 2);
+-  yield onPopupShown;
++  await onPopupShown;
+ 
+   // popup should be open now
+   // clicking on second column label
+   onPopupHidden = once(table.menupopup, "popuphidden");
+   event = table.once(TableWidget.EVENTS.HEADER_CONTEXT_MENU);
+   node = table.menupopup.querySelector("[data-id='col3']");
+   info("selecting to hide the second column");
+   is(table.tbody.children[4].getAttribute("hidden"), "true",
+      "Column is hidden before unhiding it");
+   click(node);
+-  id = yield event;
+-  yield onPopupHidden;
++  id = await event;
++  await onPopupHidden;
+   is(id, "col3", "Correct column was triggered to be hidden");
+   ok(!table.tbody.children[4].hasAttribute("hidden"),
+      "Column is not hidden after unhiding it");
+ 
+   // reset table state
+   table.clearSelection();
+   table.sortBy("col1");
+-});
++};
+diff --git a/devtools/client/shared/test/browser_telemetry_button_eyedropper.js b/devtools/client/shared/test/browser_telemetry_button_eyedropper.js
+--- a/devtools/client/shared/test/browser_telemetry_button_eyedropper.js
++++ b/devtools/client/shared/test/browser_telemetry_button_eyedropper.js
+@@ -1,37 +1,37 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_button_eyedropper.js</p><div>test</div>";
+ const EYEDROPPER_OPENED = "devtools.toolbar.eyedropper.opened";
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  let toolbox = yield gDevTools.showToolbox(target, "inspector");
++  let toolbox = await gDevTools.showToolbox(target, "inspector");
+   info("inspector opened");
+ 
+   info("testing the eyedropper button");
+-  yield testButton(toolbox, Telemetry);
++  await testButton(toolbox, Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+-  yield gDevTools.closeToolbox(target);
++  await gDevTools.closeToolbox(target);
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* testButton(toolbox, Telemetry) {
++async function testButton(toolbox, Telemetry) {
+   info("Calling the eyedropper button's callback");
+   // We call the button callback directly because we don't need to test the UI here, we're
+   // only concerned about testing the telemetry probe.
+-  yield toolbox.getPanel("inspector").showEyeDropper();
++  await toolbox.getPanel("inspector").showEyeDropper();
+ 
+   checkTelemetryResults(Telemetry);
+ }
+ 
+ function checkTelemetryResults(Telemetry) {
+   let data = Telemetry.prototype.telemetryInfo;
+   let results = new Map();
+ 
+diff --git a/devtools/client/shared/test/browser_telemetry_button_paintflashing.js b/devtools/client/shared/test/browser_telemetry_button_paintflashing.js
+--- a/devtools/client/shared/test/browser_telemetry_button_paintflashing.js
++++ b/devtools/client/shared/test/browser_telemetry_button_paintflashing.js
+@@ -6,46 +6,46 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_button_paintflashing.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+-  yield pushPref("devtools.command-button-paintflashing.enabled", true);
++  await pushPref("devtools.command-button-paintflashing.enabled", true);
+ 
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  let toolbox = yield gDevTools.showToolbox(target, "inspector");
++  let toolbox = await gDevTools.showToolbox(target, "inspector");
+   info("inspector opened");
+ 
+   info("testing the paintflashing button");
+-  yield testButton(toolbox, Telemetry);
++  await testButton(toolbox, Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+-  yield gDevTools.closeToolbox(target);
++  await gDevTools.closeToolbox(target);
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* testButton(toolbox, Telemetry) {
++async function testButton(toolbox, Telemetry) {
+   info("Testing command-button-paintflashing");
+ 
+   let button = toolbox.doc.querySelector("#command-button-paintflashing");
+   ok(button, "Captain, we have the button");
+ 
+-  yield* delayedClicks(toolbox, button, 4);
++  await delayedClicks(toolbox, button, 4);
+   checkResults("_PAINTFLASHING_", Telemetry);
+ }
+ 
+-function* delayedClicks(toolbox, node, clicks) {
++async function delayedClicks(toolbox, node, clicks) {
+   for (let i = 0; i < clicks; i++) {
+-    yield new Promise(resolve => {
++    await new Promise(resolve => {
+       // See TOOL_DELAY for why we need setTimeout here
+       setTimeout(() => resolve(), TOOL_DELAY);
+     });
+ 
+     let { CommandState } = require("devtools/shared/gcli/command-state");
+     let clicked = new Promise(resolve => {
+       CommandState.on("changed", function changed(type, { command }) {
+         if (command === "paintflashing") {
+@@ -53,17 +53,17 @@ function* delayedClicks(toolbox, node, c
+           resolve();
+         }
+       });
+     });
+ 
+     info("Clicking button " + node.id);
+     node.click();
+ 
+-    yield clicked;
++    await clicked;
+   }
+ }
+ 
+ function checkResults(histIdFocus, Telemetry) {
+   let result = Telemetry.prototype.telemetryInfo;
+ 
+   for (let [histId, value] of Object.entries(result)) {
+     if (histId.startsWith("DEVTOOLS_INSPECTOR_") ||
+diff --git a/devtools/client/shared/test/browser_telemetry_button_responsive.js b/devtools/client/shared/test/browser_telemetry_button_responsive.js
+--- a/devtools/client/shared/test/browser_telemetry_button_responsive.js
++++ b/devtools/client/shared/test/browser_telemetry_button_responsive.js
+@@ -27,65 +27,65 @@ registerCleanupFunction(() => {
+   Services.prefs.clearUserPref("devtools.devices.url");
+   Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
+   asyncStorage.removeItem("devtools.devices.url_cache");
+   asyncStorage.removeItem("devtools.devices.local");
+ });
+ 
+ loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  let toolbox = yield gDevTools.showToolbox(target, "inspector");
++  let toolbox = await gDevTools.showToolbox(target, "inspector");
+   info("inspector opened");
+ 
+   info("testing the responsivedesign button");
+-  yield testButton(toolbox, Telemetry);
++  await testButton(toolbox, Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+-  yield gDevTools.closeToolbox(target);
++  await gDevTools.closeToolbox(target);
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* testButton(toolbox, Telemetry) {
++async function testButton(toolbox, Telemetry) {
+   info("Testing command-button-responsive");
+ 
+   let button = toolbox.doc.querySelector("#command-button-responsive");
+   ok(button, "Captain, we have the button");
+ 
+-  yield delayedClicks(button, 4);
++  await delayedClicks(button, 4);
+ 
+   checkResults("_RESPONSIVE_", Telemetry);
+ }
+ 
+ function waitForToggle() {
+   return new Promise(resolve => {
+     let handler = () => {
+       ResponsiveUIManager.off("on", handler);
+       ResponsiveUIManager.off("off", handler);
+       resolve();
+     };
+     ResponsiveUIManager.on("on", handler);
+     ResponsiveUIManager.on("off", handler);
+   });
+ }
+ 
+-var delayedClicks = Task.async(function* (node, clicks) {
++var delayedClicks = async function(node, clicks) {
+   for (let i = 0; i < clicks; i++) {
+     info("Clicking button " + node.id);
+     let toggled = waitForToggle();
+     node.click();
+-    yield toggled;
++    await toggled;
+     // See TOOL_DELAY for why we need setTimeout here
+-    yield DevToolsUtils.waitForTime(TOOL_DELAY);
++    await DevToolsUtils.waitForTime(TOOL_DELAY);
+   }
+-});
++};
+ 
+ function checkResults(histIdFocus, Telemetry) {
+   let result = Telemetry.prototype.telemetryInfo;
+ 
+   for (let [histId, value] of Object.entries(result)) {
+     if (histId.startsWith("DEVTOOLS_INSPECTOR_") ||
+         !histId.includes(histIdFocus)) {
+       // Inspector stats are tested in
+diff --git a/devtools/client/shared/test/browser_telemetry_button_scratchpad.js b/devtools/client/shared/test/browser_telemetry_button_scratchpad.js
+--- a/devtools/client/shared/test/browser_telemetry_button_scratchpad.js
++++ b/devtools/client/shared/test/browser_telemetry_button_scratchpad.js
+@@ -6,36 +6,36 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_button_scratchpad.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield pushPref("devtools.command-button-scratchpad.enabled", true);
++  await pushPref("devtools.command-button-scratchpad.enabled", true);
+ 
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  let toolbox = yield gDevTools.showToolbox(target, "inspector");
++  let toolbox = await gDevTools.showToolbox(target, "inspector");
+   info("inspector opened");
+ 
+   let onAllWindowsOpened = trackScratchpadWindows();
+ 
+   info("testing the scratchpad button");
+-  yield testButton(toolbox, Telemetry);
+-  yield onAllWindowsOpened;
++  await testButton(toolbox, Telemetry);
++  await onAllWindowsOpened;
+ 
+   checkResults("_SCRATCHPAD_", Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+-  yield gDevTools.closeToolbox(target);
++  await gDevTools.closeToolbox(target);
+   gBrowser.removeCurrentTab();
+ });
+ 
+ function trackScratchpadWindows() {
+   info("register the window observer to track when scratchpad windows open");
+ 
+   let numScratchpads = 0;
+ 
+@@ -63,22 +63,22 @@ function trackScratchpadWindows() {
+             });
+           }
+         }, {once: true});
+       }
+     });
+   });
+ }
+ 
+-function* testButton(toolbox, Telemetry) {
++async function testButton(toolbox, Telemetry) {
+   info("Testing command-button-scratchpad");
+   let button = toolbox.doc.querySelector("#command-button-scratchpad");
+   ok(button, "Captain, we have the button");
+ 
+-  yield delayedClicks(button, 4);
++  await delayedClicks(button, 4);
+ }
+ 
+ function delayedClicks(node, clicks) {
+   return new Promise(resolve => {
+     let clicked = 0;
+ 
+     // See TOOL_DELAY for why we need setTimeout here
+     setTimeout(function delayedClick() {
+diff --git a/devtools/client/shared/test/browser_telemetry_sidebar.js b/devtools/client/shared/test/browser_telemetry_sidebar.js
+--- a/devtools/client/shared/test/browser_telemetry_sidebar.js
++++ b/devtools/client/shared/test/browser_telemetry_sidebar.js
+@@ -5,33 +5,33 @@
+ "use strict";
+ 
+ const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_sidebar.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  let toolbox = yield gDevTools.showToolbox(target, "inspector");
++  let toolbox = await gDevTools.showToolbox(target, "inspector");
+   info("inspector opened");
+ 
+-  yield testSidebar(toolbox);
++  await testSidebar(toolbox);
+   checkResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+-  yield gDevTools.closeToolbox(target);
++  await gDevTools.closeToolbox(target);
+   gBrowser.removeCurrentTab();
+ });
+ 
+-function* testSidebar(toolbox) {
++function testSidebar(toolbox) {
+   info("Testing sidebar");
+ 
+   let inspector = toolbox.getCurrentPanel();
+   let sidebarTools = ["ruleview", "computedview", "layoutview", "fontinspector",
+                       "animationinspector"];
+ 
+   // Concatenate the array with itself so that we can open each tool twice.
+   sidebarTools.push.apply(sidebarTools, sidebarTools);
+diff --git a/devtools/client/shared/test/browser_telemetry_toolbox.js b/devtools/client/shared/test/browser_telemetry_toolbox.js
+--- a/devtools/client/shared/test/browser_telemetry_toolbox.js
++++ b/devtools/client/shared/test/browser_telemetry_toolbox.js
+@@ -5,18 +5,18 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolbox.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(3, TOOL_DELAY, "inspector");
++  await openAndCloseToolbox(3, TOOL_DELAY, "inspector");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_canvasdebugger.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_canvasdebugger.js
+--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_canvasdebugger.js
++++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_canvasdebugger.js
+@@ -5,25 +5,25 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolboxtabs_canvasdebugger.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Activate the canvasdebugger");
+   let originalPref = Services.prefs.getBoolPref("devtools.canvasdebugger.enabled");
+   Services.prefs.setBoolPref("devtools.canvasdebugger.enabled", true);
+ 
+-  yield addTab(TEST_URI);
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(2, TOOL_DELAY, "canvasdebugger");
++  await openAndCloseToolbox(2, TOOL_DELAY, "canvasdebugger");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ 
+   info("De-activate the canvasdebugger");
+   Services.prefs.setBoolPref("devtools.canvasdebugger.enabled", originalPref);
+ });
+diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_inspector.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_inspector.js
+--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_inspector.js
++++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_inspector.js
+@@ -5,18 +5,18 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolboxtabs_inspector.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(2, TOOL_DELAY, "inspector");
++  await openAndCloseToolbox(2, TOOL_DELAY, "inspector");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsdebugger.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsdebugger.js
+--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsdebugger.js
++++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsdebugger.js
+@@ -5,18 +5,18 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolboxtabs_jsdebugger.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(2, TOOL_DELAY, "jsdebugger");
++  await openAndCloseToolbox(2, TOOL_DELAY, "jsdebugger");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsprofiler.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsprofiler.js
+--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsprofiler.js
++++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsprofiler.js
+@@ -5,18 +5,18 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolboxtabs_jsprofiler.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(2, TOOL_DELAY, "performance");
++  await openAndCloseToolbox(2, TOOL_DELAY, "performance");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_netmonitor.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_netmonitor.js
+--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_netmonitor.js
++++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_netmonitor.js
+@@ -5,19 +5,19 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolboxtabs_netmonitor.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(2, TOOL_DELAY, "netmonitor");
++  await openAndCloseToolbox(2, TOOL_DELAY, "netmonitor");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ });
+ 
+diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_options.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_options.js
+--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_options.js
++++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_options.js
+@@ -5,18 +5,18 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolboxtabs_options.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(2, TOOL_DELAY, "options");
++  await openAndCloseToolbox(2, TOOL_DELAY, "options");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_shadereditor.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_shadereditor.js
+--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_shadereditor.js
++++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_shadereditor.js
+@@ -6,25 +6,25 @@
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolboxtabs_shadereditor.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ const TOOL_PREF = "devtools.shadereditor.enabled";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Active the sharer editor");
+   let originalPref = Services.prefs.getBoolPref(TOOL_PREF);
+   Services.prefs.setBoolPref(TOOL_PREF, true);
+ 
+-  yield addTab(TEST_URI);
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(2, TOOL_DELAY, "shadereditor");
++  await openAndCloseToolbox(2, TOOL_DELAY, "shadereditor");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ 
+   info("De-activate the sharer editor");
+   Services.prefs.setBoolPref(TOOL_PREF, originalPref);
+ });
+diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_storage.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_storage.js
+--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_storage.js
++++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_storage.js
+@@ -5,18 +5,18 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolboxtabs_storage.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 1000;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(2, TOOL_DELAY, "storage");
++  await openAndCloseToolbox(2, TOOL_DELAY, "storage");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_styleeditor.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_styleeditor.js
+--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_styleeditor.js
++++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_styleeditor.js
+@@ -5,19 +5,19 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolboxtabs_styleeditor.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(2, TOOL_DELAY, "styleeditor");
++  await openAndCloseToolbox(2, TOOL_DELAY, "styleeditor");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ });
+ 
+diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_webaudioeditor.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_webaudioeditor.js
+--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_webaudioeditor.js
++++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_webaudioeditor.js
+@@ -5,25 +5,25 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolboxtabs_webaudioeditor.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Activating the webaudioeditor");
+   let originalPref = Services.prefs.getBoolPref("devtools.webaudioeditor.enabled");
+   Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true);
+ 
+-  yield addTab(TEST_URI);
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(2, TOOL_DELAY, "webaudioeditor");
++  await openAndCloseToolbox(2, TOOL_DELAY, "webaudioeditor");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ 
+   info("De-activating the webaudioeditor");
+   Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", originalPref);
+ });
+diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_webconsole.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_webconsole.js
+--- a/devtools/client/shared/test/browser_telemetry_toolboxtabs_webconsole.js
++++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_webconsole.js
+@@ -5,18 +5,18 @@
+ 
+ const TEST_URI = "data:text/html;charset=utf-8," +
+   "<p>browser_telemetry_toolboxtabs_styleeditor_webconsole.js</p>";
+ 
+ // Because we need to gather stats for the period of time that a tool has been
+ // opened we make use of setTimeout() to create tool active times.
+ const TOOL_DELAY = 200;
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
++add_task(async function() {
++  await addTab(TEST_URI);
+   let Telemetry = loadTelemetryAndRecordLogs();
+ 
+-  yield openAndCloseToolbox(2, TOOL_DELAY, "webconsole");
++  await openAndCloseToolbox(2, TOOL_DELAY, "webconsole");
+   checkTelemetryResults(Telemetry);
+ 
+   stopRecordingTelemetryLogs(Telemetry);
+   gBrowser.removeCurrentTab();
+ });
+diff --git a/devtools/client/shared/test/browser_templater_basic.js b/devtools/client/shared/test/browser_templater_basic.js
+--- a/devtools/client/shared/test/browser_templater_basic.js
++++ b/devtools/client/shared/test/browser_templater_basic.js
+@@ -10,23 +10,23 @@
+  * domtemplate)
+  * We should endevour to keep the source in sync.
+  */
+ 
+ const {template} = require("devtools/shared/gcli/templater");
+ 
+ const TEST_URI = TEST_URI_ROOT + "doc_templater_basic.html";
+ 
+-var test = Task.async(function* () {
+-  yield addTab("about:blank");
+-  let [host,, doc] = yield createHost("bottom", TEST_URI);
++var test = async function() {
++  await addTab("about:blank");
++  let [host,, doc] = await createHost("bottom", TEST_URI);
+ 
+   info("Starting DOM Templater Tests");
+   runTest(0, host, doc);
+-});
++};
+ 
+ function runTest(index, host, doc) {
+   let options = tests[index] = tests[index]();
+   let holder = doc.createElement("div");
+   holder.id = options.name;
+   let body = doc.body;
+   body.appendChild(holder);
+   // eslint-disable-next-line no-unsanitized/property
+diff --git a/devtools/client/shared/test/browser_theme_switching.js b/devtools/client/shared/test/browser_theme_switching.js
+--- a/devtools/client/shared/test/browser_theme_switching.js
++++ b/devtools/client/shared/test/browser_theme_switching.js
+@@ -1,17 +1,17 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+-add_task(function* () {
++add_task(async function() {
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  let toolbox = yield gDevTools.showToolbox(target);
++  let toolbox = await gDevTools.showToolbox(target);
+   let doc = toolbox.doc;
+   let root = doc.documentElement;
+ 
+   let platform = root.getAttribute("platform");
+   let expectedPlatform = getPlatform();
+   is(platform, expectedPlatform, ":root[platform] is correct");
+ 
+   let theme = Services.prefs.getCharPref("devtools.theme");
+@@ -34,17 +34,17 @@ add_task(function* () {
+ 
+   let sheetsFromTheme = gDevTools.getThemeDefinition(theme).stylesheets;
+   info("Checking for existence of " + sheetsInDOM.length + " sheets");
+   for (let themeSheet of sheetsFromTheme) {
+     ok(sheetsInDOM.some(s => s.includes(themeSheet)),
+        "There is a stylesheet for " + themeSheet);
+   }
+ 
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+ });
+ 
+ function getPlatform() {
+   let {OS} = Services.appinfo;
+   if (OS == "WINNT") {
+     return "win";
+   } else if (OS == "Darwin") {
+     return "mac";
+diff --git a/devtools/client/shared/test/browser_toolbar_basic.js b/devtools/client/shared/test/browser_toolbar_basic.js
+--- a/devtools/client/shared/test/browser_toolbar_basic.js
++++ b/devtools/client/shared/test/browser_toolbar_basic.js
+@@ -9,60 +9,60 @@
+ // See bug 1018184 for resolving these issues.
+ const { PromiseTestUtils } = scopedCuImport("resource://testing-common/PromiseTestUtils.jsm");
+ PromiseTestUtils.whitelistRejectionsGlobally(/Connection closed/);
+ 
+ const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser");
+ 
+ const TEST_URI = TEST_URI_ROOT + "doc_toolbar_basic.html";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Starting browser_toolbar_basic.js");
+-  yield addTab(TEST_URI);
++  await addTab(TEST_URI);
+ 
+   let toolbar = gDevToolsBrowser.getDeveloperToolbar(window);
+   ok(!toolbar.visible, "DeveloperToolbar is not visible in to start");
+ 
+   let shown = oneTimeObserve(toolbar.NOTIFICATIONS.SHOW);
+   document.getElementById("menu_devToolbar").doCommand();
+-  yield shown;
++  await shown;
+   ok(toolbar.visible, "DeveloperToolbar is visible in checkOpen");
+ 
+   let close = document.getElementById("developer-toolbar-closebutton");
+   ok(close, "Close button exists");
+ 
+   let toggleToolbox =
+     document.getElementById("menu_devToolbox");
+   ok(!isChecked(toggleToolbox), "toggle toolbox button is not checked");
+ 
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  yield gDevTools.showToolbox(target, "inspector");
++  await gDevTools.showToolbox(target, "inspector");
+   ok(isChecked(toggleToolbox), "toggle toolbox button is checked");
+ 
+-  yield addTab("about:blank");
++  await addTab("about:blank");
+   info("Opened a new tab");
+ 
+   ok(!isChecked(toggleToolbox), "toggle toolbox button is not checked");
+ 
+   gBrowser.removeCurrentTab();
+ 
+   let hidden = oneTimeObserve(toolbar.NOTIFICATIONS.HIDE);
+   document.getElementById("menu_devToolbar").doCommand();
+-  yield hidden;
++  await hidden;
+   ok(!toolbar.visible, "DeveloperToolbar is not visible in hidden");
+ 
+   shown = oneTimeObserve(toolbar.NOTIFICATIONS.SHOW);
+   document.getElementById("menu_devToolbar").doCommand();
+-  yield shown;
++  await shown;
+   ok(toolbar.visible, "DeveloperToolbar is visible in after open");
+ 
+   ok(isChecked(toggleToolbox), "toggle toolbox button is checked");
+ 
+   hidden = oneTimeObserve(toolbar.NOTIFICATIONS.HIDE);
+   document.getElementById("developer-toolbar-closebutton").doCommand();
+-  yield hidden;
++  await hidden;
+ 
+   ok(!toolbar.visible, "DeveloperToolbar is not visible after re-close");
+ });
+ 
+ function isChecked(b) {
+   return b.getAttribute("checked") == "true";
+ }
+diff --git a/devtools/client/shared/test/browser_toolbar_tooltip.js b/devtools/client/shared/test/browser_toolbar_tooltip.js
+--- a/devtools/client/shared/test/browser_toolbar_tooltip.js
++++ b/devtools/client/shared/test/browser_toolbar_tooltip.js
+@@ -12,33 +12,33 @@ const PREF_DEVTOOLS_THEME = "devtools.th
+ 
+ registerCleanupFunction(() => {
+   // Set preferences back to their original values
+   Services.prefs.clearUserPref(PREF_DEVTOOLS_THEME);
+ });
+ 
+ let toolbar = gDevToolsBrowser.getDeveloperToolbar(window);
+ 
+-add_task(function* showToolbar() {
+-  yield addTab(TEST_URI);
++add_task(async function showToolbar() {
++  await addTab(TEST_URI);
+ 
+   info("Starting browser_toolbar_tooltip.js");
+ 
+   ok(!toolbar.visible, "DeveloperToolbar is not visible in runTest");
+ 
+   let showPromise = observeOnce(toolbar.NOTIFICATIONS.SHOW);
+   document.getElementById("menu_devToolbar").doCommand();
+-  yield showPromise;
++  await showPromise;
+ });
+ 
+-add_task(function* testDimensions() {
++add_task(async function testDimensions() {
+   let tooltipPanel = toolbar.tooltipPanel;
+ 
+   toolbar.focusManager.helpRequest();
+-  yield toolbar.inputter.setInput("help help");
++  await toolbar.inputter.setInput("help help");
+ 
+   toolbar.inputter.setCursor({ start: "help help".length });
+   is(tooltipPanel._dimensions.start, "help ".length,
+           "search param start, when cursor at end");
+   ok(getLeftMargin() > 30, "tooltip offset, when cursor at end");
+ 
+   toolbar.inputter.setCursor({ start: "help".length });
+   is(tooltipPanel._dimensions.start, 0,
+@@ -51,45 +51,45 @@ add_task(function* testDimensions() {
+   ok(getLeftMargin() > 30, "tooltip offset, when cursor at penultimate position");
+ 
+   toolbar.inputter.setCursor({ start: 0 });
+   is(tooltipPanel._dimensions.start, 0,
+           "search param start, when cursor at start");
+   ok(getLeftMargin() > 9, "tooltip offset, when cursor at start");
+ });
+ 
+-add_task(function* testThemes() {
++add_task(async function testThemes() {
+   let tooltipPanel = toolbar.tooltipPanel;
+   ok(tooltipPanel.document, "Tooltip panel is initialized");
+ 
+   Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "dark");
+ 
+-  yield toolbar.inputter.setInput("");
+-  yield toolbar.inputter.setInput("help help");
++  await toolbar.inputter.setInput("");
++  await toolbar.inputter.setInput("help help");
+   is(tooltipPanel.document.documentElement.getAttribute("devtoolstheme"),
+      "dark", "Tooltip panel has correct theme");
+ 
+   Services.prefs.setCharPref(PREF_DEVTOOLS_THEME, "light");
+ 
+-  yield toolbar.inputter.setInput("");
+-  yield toolbar.inputter.setInput("help help");
++  await toolbar.inputter.setInput("");
++  await toolbar.inputter.setInput("help help");
+   is(tooltipPanel.document.documentElement.getAttribute("devtoolstheme"),
+      "light", "Tooltip panel has correct theme");
+ });
+ 
+-add_task(function* hideToolbar() {
++add_task(async function hideToolbar() {
+   info("Ending browser_toolbar_tooltip.js");
+-  yield toolbar.inputter.setInput("");
++  await toolbar.inputter.setInput("");
+ 
+   ok(toolbar.visible, "DeveloperToolbar is visible in hideToolbar");
+ 
+   info("Hide toolbar");
+   let hidePromise = observeOnce(toolbar.NOTIFICATIONS.HIDE);
+   document.getElementById("menu_devToolbar").doCommand();
+-  yield hidePromise;
++  await hidePromise;
+ 
+   ok(!toolbar.visible, "DeveloperToolbar is not visible in hideToolbar");
+ 
+   info("Done test");
+ });
+ 
+ function getLeftMargin() {
+   let style = toolbar.tooltipPanel._panel.style.marginLeft;
+diff --git a/devtools/client/shared/test/browser_treeWidget_basic.js b/devtools/client/shared/test/browser_treeWidget_basic.js
+--- a/devtools/client/shared/test/browser_treeWidget_basic.js
++++ b/devtools/client/shared/test/browser_treeWidget_basic.js
+@@ -5,19 +5,19 @@
+ // Tests that the tree widget api works fine
+ "use strict";
+ 
+ const TEST_URI = "data:text/html;charset=utf-8,<head>" +
+   "<link rel='stylesheet' type='text/css' href='chrome://devtools/skin/widg" +
+   "ets.css'></head><body><div></div><span></span></body>";
+ const {TreeWidget} = require("devtools/client/shared/widgets/TreeWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  let [host,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  await addTab("about:blank");
++  let [host,, doc] = await createHost("bottom", TEST_URI);
+ 
+   let tree = new TreeWidget(doc.querySelector("div"), {
+     defaultType: "store"
+   });
+ 
+   populateTree(tree, doc);
+   testTreeItemInsertedCorrectly(tree, doc);
+   testAPI(tree, doc);
+diff --git a/devtools/client/shared/test/browser_treeWidget_mouse_interaction.js b/devtools/client/shared/test/browser_treeWidget_mouse_interaction.js
+--- a/devtools/client/shared/test/browser_treeWidget_mouse_interaction.js
++++ b/devtools/client/shared/test/browser_treeWidget_mouse_interaction.js
+@@ -6,26 +6,26 @@
+ 
+ // Tests that mouse interaction works fine with tree widget
+ 
+ const TEST_URI = "data:text/html;charset=utf-8,<head>" +
+   "<link rel='stylesheet' type='text/css' href='chrome://devtools/skin/widg" +
+   "ets.css'></head><body><div></div><span></span></body>";
+ const {TreeWidget} = require("devtools/client/shared/widgets/TreeWidget");
+ 
+-add_task(function* () {
+-  yield addTab("about:blank");
+-  let [host,, doc] = yield createHost("bottom", TEST_URI);
++add_task(async function() {
++  await addTab("about:blank");
++  let [host,, doc] = await createHost("bottom", TEST_URI);
+ 
+   let tree = new TreeWidget(doc.querySelector("div"), {
+     defaultType: "store"
+   });
+ 
+   populateTree(tree, doc);
+-  yield testMouseInteraction(tree);
++  await testMouseInteraction(tree);
+ 
+   tree.destroy();
+   host.destroy();
+   gBrowser.removeCurrentTab();
+ });
+ 
+ function populateTree(tree, doc) {
+   tree.add([{
+@@ -78,58 +78,58 @@ function populateTree(tree, doc) {
+ function click(node) {
+   let win = node.ownerDocument.defaultView;
+   executeSoon(() => EventUtils.synthesizeMouseAtCenter(node, {}, win));
+ }
+ 
+ /**
+  * Tests if clicking the tree items does the expected behavior
+  */
+-function* testMouseInteraction(tree) {
++async function testMouseInteraction(tree) {
+   info("Testing mouse interaction with the tree");
+   let event;
+   let pass = (d, a) => event.resolve([d, a]);
+ 
+   ok(!tree.selectedItem, "Nothing should be selected beforehand");
+ 
+   tree.once("select", pass);
+   let node = tree.root.children.firstChild.firstChild;
+   info("clicking on first top level item");
+   event = defer();
+   ok(!node.classList.contains("theme-selected"),
+      "Node should not have selected class before clicking");
+   click(node);
+-  let [data, attachment] = yield event.promise;
++  let [data, attachment] = await event.promise;
+   ok(node.classList.contains("theme-selected"),
+      "Node has selected class after click");
+   is(data[0], "level1.2", "Correct tree path is emitted");
+   ok(attachment && attachment.foo, "Correct attachment is emitted");
+   is(attachment.foo, "bar", "Correct attachment value is emitted");
+ 
+   info("clicking second top level item with children to check if it expands");
+   let node2 = tree.root.children.firstChild.nextSibling.firstChild;
+   event = defer();
+   // node should not have selected class
+   ok(!node2.classList.contains("theme-selected"),
+      "New node should not have selected class before clicking");
+   ok(!node2.hasAttribute("expanded"), "New node is not expanded before clicking");
+   tree.once("select", pass);
+   click(node2);
+-  [data, attachment] = yield event.promise;
++  [data, attachment] = await event.promise;
+   ok(node2.classList.contains("theme-selected"),
+      "New node has selected class after clicking");
+   is(data[0], "level1", "Correct tree path is emitted for new node");
+   ok(!attachment, "null attachment should be emitted for new node");
+   ok(node2.hasAttribute("expanded"), "New node expanded after click");
+ 
+   ok(!node.classList.contains("theme-selected"),
+      "Old node should not have selected class after the click on new node");
+ 
+   // clicking again should just collapse
+   // this will not emit "select" event
+   event = defer();
+   node2.addEventListener("click", () => {
+     executeSoon(() => event.resolve(null));
+   }, { once: true });
+   click(node2);
+-  yield event.promise;
++  await event.promise;
+   ok(!node2.hasAttribute("expanded"), "New node collapsed after click again");
+ }
+diff --git a/devtools/client/shared/test/frame-script-utils.js b/devtools/client/shared/test/frame-script-utils.js
+--- a/devtools/client/shared/test/frame-script-utils.js
++++ b/devtools/client/shared/test/frame-script-utils.js
+@@ -1,17 +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/. */
+ 
+ /* eslint-env browser */
+ /* global addMessageListener, sendAsyncMessage, content */
+ "use strict";
+ const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+-const { Task } = require("devtools/shared/task");
+ const Services = require("Services");
+ 
+ addMessageListener("devtools:test:history", function({ data }) {
+   content.history[data.direction]();
+ });
+ 
+ addMessageListener("devtools:test:navigate", function({ data }) {
+   content.location = data.location;
+@@ -96,27 +95,27 @@ function promiseXHR(data) {
+  * The handler will respond with devtools:test:xhr message after all requests
+  * have finished. Following data will be available for each requests
+  * (in the same order as requests):
+  * {
+  *   status: XMLHttpRequest.status
+  *   response: XMLHttpRequest.response
+  * }
+  */
+-addMessageListener("devtools:test:xhr", Task.async(function* ({ data }) {
++addMessageListener("devtools:test:xhr", async function({ data }) {
+   let requests = Array.isArray(data) ? data : [data];
+   let responses = [];
+ 
+   for (let request of requests) {
+-    let response = yield promiseXHR(request);
++    let response = await promiseXHR(request);
+     responses.push(response);
+   }
+ 
+   sendAsyncMessage("devtools:test:xhr", responses);
+-}));
++});
+ 
+ addMessageListener("devtools:test:profiler", function({ data }) {
+   let { method, args, id } = data;
+   let result = Services.profiler[method](...args);
+   sendAsyncMessage("devtools:test:profiler:response", {
+     data: result,
+     id: id
+   });
+diff --git a/devtools/client/shared/test/head.js b/devtools/client/shared/test/head.js
+--- a/devtools/client/shared/test/head.js
++++ b/devtools/client/shared/test/head.js
+@@ -100,28 +100,28 @@ function oneTimeObserve(name, callback) 
+       }
+       resolve();
+     };
+     Services.obs.addObserver(func, name);
+   });
+ }
+ 
+ let createHost =
+-Task.async(function* (type = "bottom", src = CHROME_URL_ROOT + "dummy.html") {
++async function(type = "bottom", src = CHROME_URL_ROOT + "dummy.html") {
+   let host = new Hosts[type](gBrowser.selectedTab);
+-  let iframe = yield host.create();
++  let iframe = await host.create();
+ 
+-  yield new Promise(resolve => {
++  await new Promise(resolve => {
+     let domHelper = new DOMHelpers(iframe.contentWindow);
+     iframe.setAttribute("src", src);
+     domHelper.onceDOMReady(resolve);
+   });
+ 
+   return [host, iframe.contentWindow, iframe.contentDocument];
+-});
++};
+ 
+ /**
+  * Check the correctness of the data recorded in Telemetry after
+  * loadTelemetryAndRecordLogs was called.
+  */
+ function checkTelemetryResults(Telemetry) {
+   let result = Telemetry.prototype.telemetryInfo;
+ 
+@@ -150,27 +150,27 @@ function checkTelemetryResults(Telemetry
+ 
+ /**
+  * Open and close the toolbox in the current browser tab, several times, waiting
+  * some amount of time in between.
+  * @param {Number} nbOfTimes
+  * @param {Number} usageTime in milliseconds
+  * @param {String} toolId
+  */
+-function* openAndCloseToolbox(nbOfTimes, usageTime, toolId) {
++async function openAndCloseToolbox(nbOfTimes, usageTime, toolId) {
+   for (let i = 0; i < nbOfTimes; i++) {
+     info("Opening toolbox " + (i + 1));
+     let target = TargetFactory.forTab(gBrowser.selectedTab);
+-    yield gDevTools.showToolbox(target, toolId);
++    await gDevTools.showToolbox(target, toolId);
+ 
+     // We use a timeout to check the toolbox's active time
+-    yield new Promise(resolve => setTimeout(resolve, usageTime));
++    await new Promise(resolve => setTimeout(resolve, usageTime));
+ 
+     info("Closing toolbox " + (i + 1));
+-    yield gDevTools.closeToolbox(target);
++    await gDevTools.closeToolbox(target);
+   }
+ }
+ 
+ /**
+  * Synthesize a profile for testing.
+  */
+ function synthesizeProfileForTest(samples) {
+   const RecordingUtils = require("devtools/shared/performance/recording-utils");
+@@ -220,25 +220,25 @@ function showFilterPopupPresets(widget) 
+ /**
+  * Show presets list and create a sample preset with the name and value provided
+  * @param  {CSSFilterWidget} widget
+  * @param  {string} name
+  * @param  {string} value
+  * @return {Promise}
+  */
+ let showFilterPopupPresetsAndCreatePreset =
+-Task.async(function* (widget, name, value) {
+-  yield showFilterPopupPresets(widget);
++async function(widget, name, value) {
++  await showFilterPopupPresets(widget);
+ 
+   let onRender = widget.once("render");
+   widget.setCssValue(value);
+-  yield onRender;
++  await onRender;
+ 
+   let footer = widget.el.querySelector(".presets-list .footer");
+   footer.querySelector("input").value = name;
+ 
+   onRender = widget.once("render");
+   widget._savePreset({
+     preventDefault: () => {}
+   });
+ 
+-  yield onRender;
+-});
++  await onRender;
++};
+diff --git a/devtools/client/shared/test/helper_html_tooltip.js b/devtools/client/shared/test/helper_html_tooltip.js
+--- a/devtools/client/shared/test/helper_html_tooltip.js
++++ b/devtools/client/shared/test/helper_html_tooltip.js
+@@ -15,36 +15,36 @@
+  * @param {HTMLTooltip} tooltip
+  *        The tooltip instance to display
+  * @param {Node} anchor
+  *        The anchor that should be used to display the tooltip
+  * @param {Object} see HTMLTooltip:show documentation
+  * @return {Promise} promise that resolves when "shown" has been fired, reflow
+  *         and repaint done.
+  */
+-function* showTooltip(tooltip, anchor, {position, x, y} = {}) {
++async function showTooltip(tooltip, anchor, {position, x, y} = {}) {
+   let onShown = tooltip.once("shown");
+   tooltip.show(anchor, {position, x, y});
+-  yield onShown;
++  await onShown;
+   return waitForReflow(tooltip);
+ }
+ 
+ /**
+  * Hide an existing HTMLTooltip. After the tooltip "hidden" event has been fired
+  * a reflow will be triggered.
+  *
+  * @param {HTMLTooltip} tooltip
+  *        The tooltip instance to hide
+  * @return {Promise} promise that resolves when "hidden" has been fired, reflow
+  *         and repaint done.
+  */
+-function* hideTooltip(tooltip) {
++async function hideTooltip(tooltip) {
+   let onPopupHidden = tooltip.once("hidden");
+   tooltip.hide();
+-  yield onPopupHidden;
++  await onPopupHidden;
+   return waitForReflow(tooltip);
+ }
+ 
+ /**
+  * Forces the reflow of an HTMLTooltip document and waits for the next repaint.
+  *
+  * @param {HTMLTooltip} the tooltip to reflow
+  * @return {Promise} a promise that will resolve after the reflow and repaint
+diff --git a/devtools/client/shared/test/helper_inplace_editor.js b/devtools/client/shared/test/helper_inplace_editor.js
+--- a/devtools/client/shared/test/helper_inplace_editor.js
++++ b/devtools/client/shared/test/helper_inplace_editor.js
+@@ -18,28 +18,28 @@ const { editableField } = require("devto
+  *
+  * @param {Object} options
+  *        Options passed to the InplaceEditor/editableField constructor.
+  * @param {Document} doc
+  *        Document where the span element will be created.
+  * @param {String} textContent
+  *        (optional) String that will be used as the text content of the span.
+  */
+-const createInplaceEditorAndClick = Task.async(function* (options, doc, textContent) {
++const createInplaceEditorAndClick = async function(options, doc, textContent) {
+   let span = options.element = createSpan(doc);
+   if (textContent) {
+     span.textContent = textContent;
+   }
+ 
+   info("Creating an inplace-editor field");
+   editableField(options);
+ 
+   info("Clicking on the inplace-editor field to turn to edit mode");
+   span.click();
+-});
++};
+ 
+ /**
+  * Helper to create a span in the provided document.
+  *
+  * @param {Document} doc
+  *        Document where the span element will be created.
+  * @return {Element} the created span element.
+  */
+@@ -70,17 +70,17 @@ function createSpan(doc) {
+  * @param {Array} testData
+  *        - {String} key, the key to send
+  *        - {String} completion, the expected value of the auto-completion
+  *        - {Number} index, the index of the selected suggestion in the popup
+  *        - {Number} total, the total number of suggestions in the popup
+  * @param {InplaceEditor} editor
+  *        The InplaceEditor instance being tested
+  */
+-function* testCompletion([key, completion, index, total], editor) {
++async function testCompletion([key, completion, index, total], editor) {
+   info("Pressing key " + key);
+   info("Expecting " + completion);
+ 
+   let onVisibilityChange = null;
+   let open = total > 0;
+   if (editor.popup.isOpen != open) {
+     onVisibilityChange = editor.popup.once(open ? "popup-opened" : "popup-closed");
+   }
+@@ -92,19 +92,19 @@ function* testCompletion([key, completio
+   } else {
+     info("Waiting for after-suggest event on the editor");
+     onSuggest = editor.once("after-suggest");
+   }
+ 
+   info("Synthesizing key " + key);
+   EventUtils.synthesizeKey(key, {}, editor.input.defaultView);
+ 
+-  yield onSuggest;
+-  yield onVisibilityChange;
+-  yield waitForTime(5);
++  await onSuggest;
++  await onVisibilityChange;
++  await waitForTime(5);
+ 
+   info("Checking the state");
+   if (completion !== null) {
+     is(editor.input.value, completion, "Correct value is autocompleted");
+   }
+   if (total === 0) {
+     ok(!(editor.popup && editor.popup.isOpen), "Popup is closed");
+   } else {
+diff --git a/devtools/client/shared/test/shared-head.js b/devtools/client/shared/test/shared-head.js
+--- a/devtools/client/shared/test/shared-head.js
++++ b/devtools/client/shared/test/shared-head.js
+@@ -24,17 +24,16 @@ const {loader, require} = scopedCuImport
+ 
+ const {gDevTools} = require("devtools/client/framework/devtools");
+ const {TargetFactory} = require("devtools/client/framework/target");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ const flags = require("devtools/shared/flags");
+ let promise = require("promise");
+ let defer = require("devtools/shared/defer");
+ const Services = require("Services");
+-const {Task} = require("devtools/shared/task");
+ const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
+ 
+ const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+ const CHROME_URL_ROOT = TEST_DIR + "/";
+ const URL_ROOT = CHROME_URL_ROOT.replace("chrome://mochitests/content/",
+                                          "http://example.com/");
+ const URL_ROOT_SSL = CHROME_URL_ROOT.replace("chrome://mochitests/content/",
+                                              "https://example.com/");
+@@ -112,66 +111,66 @@ registerCleanupFunction(() => {
+   flags.testing = false;
+   Services.prefs.clearUserPref("devtools.dump.emit");
+   Services.prefs.clearUserPref("devtools.toolbox.host");
+   Services.prefs.clearUserPref("devtools.toolbox.previousHost");
+   Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
+   Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight");
+ });
+ 
+-registerCleanupFunction(function* cleanup() {
++registerCleanupFunction(async function cleanup() {
+   while (gBrowser.tabs.length > 1) {
+-    yield closeTabAndToolbox(gBrowser.selectedTab);
++    await closeTabAndToolbox(gBrowser.selectedTab);
+   }
+ });
+ 
+ /**
+  * Add a new test tab in the browser and load the given url.
+  * @param {String} url The url to be loaded in the new tab
+  * @param {Object} options Object with various optional fields:
+  *   - {Boolean} background If true, open the tab in background
+  *   - {ChromeWindow} window Firefox top level window we should use to open the tab
+  *   - {Number} userContextId The userContextId of the tab.
+  *   - {String} preferredRemoteType
+  * @return a promise that resolves to the tab object when the url is loaded
+  */
+-var addTab = Task.async(function* (url, options = { background: false, window: window }) {
++var addTab = async function(url, options = { background: false, window: window }) {
+   info("Adding a new tab with URL: " + url);
+ 
+   let { background } = options;
+   let { gBrowser } = options.window ? options.window : window;
+   let { userContextId } = options;
+ 
+   let tab = BrowserTestUtils.addTab(gBrowser, url,
+     {userContextId, preferredRemoteType: options.preferredRemoteType});
+   if (!background) {
+     gBrowser.selectedTab = tab;
+   }
+-  yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
++  await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ 
+   info("Tab added and finished loading");
+ 
+   return tab;
+-});
++};
+ 
+ /**
+  * Remove the given tab.
+  * @param {Object} tab The tab to be removed.
+  * @return Promise<undefined> resolved when the tab is successfully removed.
+  */
+-var removeTab = Task.async(function* (tab) {
++var removeTab = async function(tab) {
+   info("Removing tab.");
+ 
+   let { gBrowser } = tab.ownerDocument.defaultView;
+   let onClose = once(gBrowser.tabContainer, "TabClose");
+   gBrowser.removeTab(tab);
+-  yield onClose;
++  await onClose;
+ 
+   info("Tab removed and finished closing");
+-});
++};
+ 
+ /**
+  * Refresh the provided tab.
+  * @param {Object} tab The tab to be refreshed. Defaults to the currently selected tab.
+  * @return Promise<undefined> resolved when the tab is successfully refreshed.
+  */
+ var refreshTab = async function(tab = gBrowser.selectedTab) {
+   info("Refreshing tab.");
+@@ -370,81 +369,81 @@ function wait(ms) {
+ 
+ /**
+  * Open the toolbox in a given tab.
+  * @param {XULNode} tab The tab the toolbox should be opened in.
+  * @param {String} toolId Optional. The ID of the tool to be selected.
+  * @param {String} hostType Optional. The type of toolbox host to be used.
+  * @return {Promise} Resolves with the toolbox, when it has been opened.
+  */
+-var openToolboxForTab = Task.async(function* (tab, toolId, hostType) {
++var openToolboxForTab = async function(tab, toolId, hostType) {
+   info("Opening the toolbox");
+ 
+   let toolbox;
+   let target = TargetFactory.forTab(tab);
+-  yield target.makeRemote();
++  await target.makeRemote();
+ 
+   // Check if the toolbox is already loaded.
+   toolbox = gDevTools.getToolbox(target);
+   if (toolbox) {
+     if (!toolId || (toolId && toolbox.getPanel(toolId))) {
+       info("Toolbox is already opened");
+       return toolbox;
+     }
+   }
+ 
+   // If not, load it now.
+-  toolbox = yield gDevTools.showToolbox(target, toolId, hostType);
++  toolbox = await gDevTools.showToolbox(target, toolId, hostType);
+ 
+   // Make sure that the toolbox frame is focused.
+-  yield new Promise(resolve => waitForFocus(resolve, toolbox.win));
++  await new Promise(resolve => waitForFocus(resolve, toolbox.win));
+ 
+   info("Toolbox opened and focused");
+ 
+   return toolbox;
+-});
++};
+ 
+ /**
+  * Add a new tab and open the toolbox in it.
+  * @param {String} url The URL for the tab to be opened.
+  * @param {String} toolId Optional. The ID of the tool to be selected.
+  * @param {String} hostType Optional. The type of toolbox host to be used.
+  * @return {Promise} Resolves when the tab has been added, loaded and the
+  * toolbox has been opened. Resolves to the toolbox.
+  */
+-var openNewTabAndToolbox = Task.async(function* (url, toolId, hostType) {
+-  let tab = yield addTab(url);
++var openNewTabAndToolbox = async function(url, toolId, hostType) {
++  let tab = await addTab(url);
+   return openToolboxForTab(tab, toolId, hostType);
+-});
++};
+ 
+ /**
+  * Close a tab and if necessary, the toolbox that belongs to it
+  * @param {Tab} tab The tab to close.
+  * @return {Promise} Resolves when the toolbox and tab have been destroyed and
+  * closed.
+  */
+-var closeTabAndToolbox = Task.async(function* (tab = gBrowser.selectedTab) {
++var closeTabAndToolbox = async function(tab = gBrowser.selectedTab) {
+   let target = TargetFactory.forTab(tab);
+   if (target) {
+-    yield gDevTools.closeToolbox(target);
++    await gDevTools.closeToolbox(target);
+   }
+ 
+-  yield removeTab(tab);
+-});
++  await removeTab(tab);
++};
+ 
+ /**
+  * Close a toolbox and the current tab.
+  * @param {Toolbox} toolbox The toolbox to close.
+  * @return {Promise} Resolves when the toolbox and tab have been destroyed and
+  * closed.
+  */
+-var closeToolboxAndTab = Task.async(function* (toolbox) {
+-  yield toolbox.destroy();
+-  yield removeTab(gBrowser.selectedTab);
+-});
++var closeToolboxAndTab = async function(toolbox) {
++  await toolbox.destroy();
++  await removeTab(gBrowser.selectedTab);
++};
+ 
+ /**
+  * Waits until a predicate returns true.
+  *
+  * @param function predicate
+  *        Invoked once in a while until it returns true.
+  * @param number interval [optional]
+  *        How often the predicate is invoked, in milliseconds.
+@@ -571,20 +570,20 @@ function pushPref(preferenceName, value)
+  *        Dotted path to use to expand the object.
+  * @return {?} anything that is found at the provided path in the object.
+  */
+ function lookupPath(obj, path) {
+   let segments = path.split(".");
+   return segments.reduce((prev, current) => prev[current], obj);
+ }
+ 
+-var closeToolbox = Task.async(function* () {
++var closeToolbox = async function() {
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  yield gDevTools.closeToolbox(target);
+-});
++  await gDevTools.closeToolbox(target);
++};
+ 
+ /**
+  * Load the Telemetry utils, then stub Telemetry.prototype.log and
+  * Telemetry.prototype.logKeyed in order to record everything that's logged in
+  * it.
+  * Store all recordings in Telemetry.telemetryInfo.
+  * @return {Telemetry}
+  */
+@@ -676,22 +675,22 @@ function waitForTitleChange(toolbox) {
+  *   let url = "http://localhost: " + server.identity.primaryPort + "/path";
+  *
+  * @returns {HttpServer}
+  */
+ function createTestHTTPServer() {
+   const {HttpServer} = ChromeUtils.import("resource://testing-common/httpd.js", {});
+   let server = new HttpServer();
+ 
+-  registerCleanupFunction(function* cleanup() {
++  registerCleanupFunction(async function cleanup() {
+     let destroyed = defer();
+     server.stop(() => {
+       destroyed.resolve();
+     });
+-    yield destroyed.promise;
++    await destroyed.promise;
+   });
+ 
+   server.start(-1);
+   return server;
+ }
+ 
+ /**
+  * Inject `EventUtils` helpers into ContentTask scope.
+@@ -699,17 +698,17 @@ function createTestHTTPServer() {
+  * This helper is automatically exposed to mochitest browser tests,
+  * but is missing from content task scope.
+  * You should call this method only once per <browser> tag
+  *
+  * @param {xul:browser} browser
+  *        Reference to the browser in which we load content task
+  */
+ async function injectEventUtilsInContentTask(browser) {
+-  await ContentTask.spawn(browser, {}, function* () {
++  await ContentTask.spawn(browser, {}, async function() {
+     if ("EventUtils" in this) {
+       return;
+     }
+ 
+     let EventUtils = this.EventUtils = {};
+ 
+     EventUtils.window = {};
+     EventUtils.parent = EventUtils.window;
+diff --git a/devtools/client/shared/test/test-actor-registry.js b/devtools/client/shared/test/test-actor-registry.js
+--- a/devtools/client/shared/test/test-actor-registry.js
++++ b/devtools/client/shared/test/test-actor-registry.js
+@@ -3,89 +3,88 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ (function(exports) {
+   const CC = Components.Constructor;
+ 
+   const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+   const { fetch } = require("devtools/shared/DevToolsUtils");
+-  const { Task } = require("devtools/shared/task");
+ 
+   const TEST_URL_ROOT = "http://example.com/browser/devtools/client/shared/test/";
+   const ACTOR_URL = TEST_URL_ROOT + "test-actor.js";
+ 
+   // Register a test actor that can operate on the remote document
+-  exports.registerTestActor = Task.async(function* (client) {
++  exports.registerTestActor = async function(client) {
+     // First, instanciate ActorRegistryFront to be able to dynamically register an actor
+-    let response = yield client.listTabs();
++    let response = await client.listTabs();
+     let { ActorRegistryFront } = require("devtools/shared/fronts/actor-registry");
+     let registryFront = ActorRegistryFront(client, response);
+ 
+     // Then ask to register our test-actor to retrieve its front
+     let options = {
+       type: { tab: true },
+       constructor: "TestActor",
+       prefix: "testActor"
+     };
+-    let testActorFront = yield registryFront.registerActor(ACTOR_URL, options);
++    let testActorFront = await registryFront.registerActor(ACTOR_URL, options);
+     return testActorFront;
+-  });
++  };
+ 
+   // Load the test actor in a custom sandbox as we can't use SDK module loader with URIs
+-  let loadFront = Task.async(function* () {
+-    let sourceText = yield request(ACTOR_URL);
++  let loadFront = async function() {
++    let sourceText = await request(ACTOR_URL);
+     const principal = CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")();
+     const sandbox = Cu.Sandbox(principal);
+     sandbox.exports = {};
+     sandbox.require = require;
+     Cu.evalInSandbox(sourceText, sandbox, "1.8", ACTOR_URL, 1);
+     return sandbox.exports;
+-  });
++  };
+ 
+   // Ensure fetching a live TabActor form for the targeted app
+   // (helps fetching the test actor registered dynamically)
+   let getUpdatedForm = function(client, tab) {
+     return client.getTab({tab: tab})
+                  .then(response => response.tab);
+   };
+ 
+   // Spawn an instance of the test actor for the given toolbox
+-  exports.getTestActor = Task.async(function* (toolbox) {
++  exports.getTestActor = async function(toolbox) {
+     let client = toolbox.target.client;
+     return getTestActor(client, toolbox.target.tab, toolbox);
+-  });
++  };
+ 
+   // Sometimes, we need the test actor before opening or without a toolbox then just
+   // create a front for the given `tab`
+-  exports.getTestActorWithoutToolbox = Task.async(function* (tab) {
++  exports.getTestActorWithoutToolbox = async function(tab) {
+     let { DebuggerServer } = require("devtools/server/main");
+     let { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ 
+     // We need to spawn a client instance,
+     // but for that we have to first ensure a server is running
+     DebuggerServer.init();
+     DebuggerServer.registerAllActors();
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+-    yield client.connect();
++    await client.connect();
+ 
+     // We also need to make sure the test actor is registered on the server.
+-    yield exports.registerTestActor(client);
++    await exports.registerTestActor(client);
+ 
+     return getTestActor(client, tab);
+-  });
++  };
+ 
+   // Fetch the content of a URI
+   let request = function(uri) {
+     return fetch(uri).then(({ content }) => content);
+   };
+ 
+-  let getTestActor = Task.async(function* (client, tab, toolbox) {
++  let getTestActor = async function(client, tab, toolbox) {
+     // We may have to update the form in order to get the dynamically registered
+     // test actor.
+-    let form = yield getUpdatedForm(client, tab);
++    let form = await getUpdatedForm(client, tab);
+ 
+-    let { TestActorFront } = yield loadFront();
++    let { TestActorFront } = await loadFront();
+ 
+     return new TestActorFront(client, form, toolbox);
+-  });
++  };
+ })(this);
+diff --git a/devtools/client/shared/test/test-actor.js b/devtools/client/shared/test/test-actor.js
+--- a/devtools/client/shared/test/test-actor.js
++++ b/devtools/client/shared/test/test-actor.js
+@@ -9,17 +9,16 @@
+ // A helper actor for inspector and markupview tests.
+ 
+ const { Cc, Ci, Cu } = require("chrome");
+ const Services = require("Services");
+ const {
+   getRect, getAdjustedQuads, getWindowDimensions
+ } = require("devtools/shared/layout/utils");
+ const defer = require("devtools/shared/defer");
+-const {Task} = require("devtools/shared/task");
+ const {
+   isContentStylesheet,
+   getCSSStyleRules
+ } = require("devtools/shared/inspector/css-logic");
+ const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
+ 
+ // Set up a dummy environment so that EventUtils works. We need to be careful to
+ // pass a window object into each EventUtils method we call rather than having
+@@ -695,26 +694,26 @@ var TestActor = exports.TestActor = prot
+   reflow: function() {
+     let deferred = defer();
+     this.content.document.documentElement.offsetWidth;
+     this.content.requestAnimationFrame(deferred.resolve);
+ 
+     return deferred.promise;
+   },
+ 
+-  getNodeRect: Task.async(function* (selector) {
++  async getNodeRect(selector) {
+     let node = this._querySelector(selector);
+     return getRect(this.content, node, this.content);
+-  }),
++  },
+ 
+-  getTextNodeRect: Task.async(function* (parentSelector, childNodeIndex) {
++  async getTextNodeRect(parentSelector, childNodeIndex) {
+     let parentNode = this._querySelector(parentSelector);
+     let node = parentNode.childNodes[childNodeIndex];
+     return getAdjustedQuads(this.content, node)[0].bounds;
+-  }),
++  },
+ 
+   /**
+    * Get information about a DOM element, identified by a selector.
+    * @param {String} selector The CSS selector to get the node (can be an array
+    * of selectors to get elements in an iframe).
+    * @return {Object} data Null if selector didn't match any node, otherwise:
+    * - {String} tagName.
+    * - {String} namespaceURI.
+@@ -843,104 +842,104 @@ var TestActorFront = exports.TestActorFr
+   /**
+    * Assert that the box-model highlighter's current position corresponds to the
+    * given node boxquads.
+    * @param {String} selector The node selector to get the boxQuads from
+    * @param {Function} is assertion function to call for equality checks
+    * @param {String} prefix An optional prefix for logging information to the
+    * console.
+    */
+-  isNodeCorrectlyHighlighted: Task.async(function* (selector, is, prefix = "") {
++  async isNodeCorrectlyHighlighted(selector, is, prefix = "") {
+     prefix += (prefix ? " " : "") + selector + " ";
+ 
+-    let boxModel = yield this._getBoxModelStatus();
+-    let regions = yield this.getAllAdjustedQuads(selector);
++    let boxModel = await this._getBoxModelStatus();
++    let regions = await this.getAllAdjustedQuads(selector);
+ 
+     for (let boxType of ["content", "padding", "border", "margin"]) {
+       let [quad] = regions[boxType];
+       for (let point in boxModel[boxType].points) {
+         is(boxModel[boxType].points[point].x, quad[point].x,
+           prefix + boxType + " point " + point + " x coordinate is correct");
+         is(boxModel[boxType].points[point].y, quad[point].y,
+           prefix + boxType + " point " + point + " y coordinate is correct");
+       }
+     }
+-  }),
++  },
+ 
+   /**
+    * Get the current rect of the border region of the box-model highlighter
+    */
+-  getSimpleBorderRect: Task.async(function* (toolbox) {
+-    let {border} = yield this._getBoxModelStatus(toolbox);
++  async getSimpleBorderRect(toolbox) {
++    let {border} = await this._getBoxModelStatus(toolbox);
+     let {p1, p2, p4} = border.points;
+ 
+     return {
+       top: p1.y,
+       left: p1.x,
+       width: p2.x - p1.x,
+       height: p4.y - p1.y
+     };
+-  }),
++  },
+ 
+   /**
+    * Get the current positions and visibility of the various box-model highlighter
+    * elements.
+    */
+-  _getBoxModelStatus: Task.async(function* () {
+-    let isVisible = yield this.isHighlighting();
++  async _getBoxModelStatus() {
++    let isVisible = await this.isHighlighting();
+ 
+     let ret = {
+       visible: isVisible
+     };
+ 
+     for (let region of ["margin", "border", "padding", "content"]) {
+-      let points = yield this._getPointsForRegion(region);
+-      let visible = yield this._isRegionHidden(region);
++      let points = await this._getPointsForRegion(region);
++      let visible = await this._isRegionHidden(region);
+       ret[region] = {points, visible};
+     }
+ 
+     ret.guides = {};
+     for (let guide of ["top", "right", "bottom", "left"]) {
+-      ret.guides[guide] = yield this._getGuideStatus(guide);
++      ret.guides[guide] = await this._getGuideStatus(guide);
+     }
+ 
+     return ret;
+-  }),
++  },
+ 
+   /**
+    * Check that the box-model highlighter is currently highlighting the node matching the
+    * given selector.
+    * @param {String} selector
+    * @return {Boolean}
+    */
+-  assertHighlightedNode: Task.async(function* (selector) {
+-    let rect = yield this.getNodeRect(selector);
+-    return yield this.isNodeRectHighlighted(rect);
+-  }),
++  async assertHighlightedNode(selector) {
++    let rect = await this.getNodeRect(selector);
++    return this.isNodeRectHighlighted(rect);
++  },
+ 
+   /**
+    * Check that the box-model highlighter is currently highlighting the text node that can
+    * be found at a given index within the list of childNodes of a parent element matching
+    * the given selector.
+    * @param {String} parentSelector
+    * @param {Number} childNodeIndex
+    * @return {Boolean}
+    */
+-  assertHighlightedTextNode: Task.async(function* (parentSelector, childNodeIndex) {
+-    let rect = yield this.getTextNodeRect(parentSelector, childNodeIndex);
+-    return yield this.isNodeRectHighlighted(rect);
+-  }),
++  async assertHighlightedTextNode(parentSelector, childNodeIndex) {
++    let rect = await this.getTextNodeRect(parentSelector, childNodeIndex);
++    return this.isNodeRectHighlighted(rect);
++  },
+ 
+   /**
+    * Check that the box-model highlighter is currently highlighting the given rect.
+    * @param {Object} rect
+    * @return {Boolean}
+    */
+-  isNodeRectHighlighted: Task.async(function* ({ left, top, width, height }) {
+-    let {visible, border} = yield this._getBoxModelStatus();
++  async isNodeRectHighlighted({ left, top, width, height }) {
++    let {visible, border} = await this._getBoxModelStatus();
+     let points = border.points;
+     if (!visible) {
+       return false;
+     }
+ 
+     // Check that the node is within the box model
+     let right = left + width;
+     let bottom = top + height;
+@@ -953,24 +952,24 @@ var TestActorFront = exports.TestActorFr
+     }
+     points = list;
+ 
+     // Check that each point of the node is within the box model
+     return isInside([left, top], points) &&
+            isInside([right, top], points) &&
+            isInside([right, bottom], points) &&
+            isInside([left, bottom], points);
+-  }),
++  },
+ 
+   /**
+    * Get the coordinate (points attribute) from one of the polygon elements in the
+    * box model highlighter.
+    */
+-  _getPointsForRegion: Task.async(function* (region) {
+-    let d = yield this.getHighlighterNodeAttribute("box-model-" + region, "d");
++  async _getPointsForRegion(region) {
++    let d = await this.getHighlighterNodeAttribute("box-model-" + region, "d");
+ 
+     let polygons = d.match(/M[^M]+/g);
+     if (!polygons) {
+       return null;
+     }
+ 
+     let points = polygons[0].trim().split(" ").map(i => {
+       return i.replace(/M|L/, "").split(",");
+@@ -989,68 +988,68 @@ var TestActorFront = exports.TestActorFr
+         x: parseFloat(points[2][0]),
+         y: parseFloat(points[2][1])
+       },
+       p4: {
+         x: parseFloat(points[3][0]),
+         y: parseFloat(points[3][1])
+       }
+     };
+-  }),
++  },
+ 
+   /**
+    * Is a given region polygon element of the box-model highlighter currently
+    * hidden?
+    */
+-  _isRegionHidden: Task.async(function* (region) {
+-    let value = yield this.getHighlighterNodeAttribute("box-model-" + region, "hidden");
++  async _isRegionHidden(region) {
++    let value = await this.getHighlighterNodeAttribute("box-model-" + region, "hidden");
+     return value !== null;
+-  }),
++  },
+ 
+-  _getGuideStatus: Task.async(function* (location) {
++  async _getGuideStatus(location) {
+     let id = "box-model-guide-" + location;
+ 
+-    let hidden = yield this.getHighlighterNodeAttribute(id, "hidden");
+-    let x1 = yield this.getHighlighterNodeAttribute(id, "x1");
+-    let y1 = yield this.getHighlighterNodeAttribute(id, "y1");
+-    let x2 = yield this.getHighlighterNodeAttribute(id, "x2");
+-    let y2 = yield this.getHighlighterNodeAttribute(id, "y2");
++    let hidden = await this.getHighlighterNodeAttribute(id, "hidden");
++    let x1 = await this.getHighlighterNodeAttribute(id, "x1");
++    let y1 = await this.getHighlighterNodeAttribute(id, "y1");
++    let x2 = await this.getHighlighterNodeAttribute(id, "x2");
++    let y2 = await this.getHighlighterNodeAttribute(id, "y2");
+ 
+     return {
+       visible: !hidden,
+       x1: x1,
+       y1: y1,
+       x2: x2,
+       y2: y2
+     };
+-  }),
++  },
+ 
+   /**
+    * Get the coordinates of the rectangle that is defined by the 4 guides displayed
+    * in the toolbox box-model highlighter.
+    * @return {Object} Null if at least one guide is hidden. Otherwise an object
+    * with p1, p2, p3, p4 properties being {x, y} objects.
+    */
+-  getGuidesRectangle: Task.async(function* () {
+-    let tGuide = yield this._getGuideStatus("top");
+-    let rGuide = yield this._getGuideStatus("right");
+-    let bGuide = yield this._getGuideStatus("bottom");
+-    let lGuide = yield this._getGuideStatus("left");
++  async getGuidesRectangle() {
++    let tGuide = await this._getGuideStatus("top");
++    let rGuide = await this._getGuideStatus("right");
++    let bGuide = await this._getGuideStatus("bottom");
++    let lGuide = await this._getGuideStatus("left");
+ 
+     if (!tGuide.visible || !rGuide.visible || !bGuide.visible || !lGuide.visible) {
+       return null;
+     }
+ 
+     return {
+       p1: {x: lGuide.x1, y: tGuide.y1},
+       p2: {x: +rGuide.x1 + 1, y: tGuide.y1},
+       p3: {x: +rGuide.x1 + 1, y: +bGuide.y1 + 1},
+       p4: {x: lGuide.x1, y: +bGuide.y1 + 1}
+     };
+-  }),
++  },
+ 
+   waitForHighlighterEvent: protocol.custom(function(event) {
+     return this._waitForHighlighterEvent(event, this.toolbox.highlighter.actorID);
+   }, {
+     impl: "_waitForHighlighterEvent"
+   }),
+ 
+   /**
+@@ -1058,18 +1057,18 @@ var TestActorFront = exports.TestActorFr
+    * <path> elements, and parse it to a list of points.
+    * @param {String} region The box model region name.
+    * @param {Front} highlighter The front of the highlighter.
+    * @return {Object} The object returned has the following form:
+    * - d {String} the d attribute value
+    * - points {Array} an array of all the polygons defined by the path. Each box
+    *   is itself an Array of points, themselves being [x,y] coordinates arrays.
+    */
+-  getHighlighterRegionPath: Task.async(function* (region, highlighter) {
+-    let d = yield this.getHighlighterNodeAttribute(
++  async getHighlighterRegionPath(region, highlighter) {
++    let d = await this.getHighlighterNodeAttribute(
+       `box-model-${region}`, "d", highlighter
+     );
+     if (!d) {
+       return {d: null};
+     }
+ 
+     let polygons = d.match(/M[^M]+/g);
+     if (!polygons) {
+@@ -1079,17 +1078,17 @@ var TestActorFront = exports.TestActorFr
+     let points = [];
+     for (let polygon of polygons) {
+       points.push(polygon.trim().split(" ").map(i => {
+         return i.replace(/M|L/, "").split(",");
+       }));
+     }
+ 
+     return {d, points};
+-  })
++  }
+ });
+ 
+ /**
+  * Check whether a point is included in a polygon.
+  * Taken and tweaked from:
+  * https://github.com/iominh/point-in-polygon-extended/blob/master/src/index.js#L30-L85
+  * @param {Array} point [x,y] coordinates
+  * @param {Array} polygon An array of [x,y] points
+diff --git a/devtools/client/shared/view-source.js b/devtools/client/shared/view-source.js
+--- a/devtools/client/shared/view-source.js
++++ b/devtools/client/shared/view-source.js
+@@ -1,16 +1,14 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+-var { Task } = require("devtools/shared/task");
+-
+ var Services = require("Services");
+ var { gDevTools } = require("devtools/client/framework/devtools");
+ var { getSourceText } = require("devtools/client/debugger/content/queries");
+ 
+ /**
+  * Tries to open a Stylesheet file in the Style Editor. If the file is not
+  * found, it is opened in source view instead.
+  * Returns a promise resolving to a boolean indicating whether or not
+@@ -18,76 +16,76 @@ var { getSourceText } = require("devtool
+  * Firefox View Source is the fallback.
+  *
+  * @param {Toolbox} toolbox
+  * @param {string} sourceURL
+  * @param {number} sourceLine
+  *
+  * @return {Promise<boolean>}
+  */
+-exports.viewSourceInStyleEditor = Task.async(function* (toolbox, sourceURL,
++exports.viewSourceInStyleEditor = async function(toolbox, sourceURL,
+                                                         sourceLine) {
+-  let panel = yield toolbox.loadTool("styleeditor");
++  let panel = await toolbox.loadTool("styleeditor");
+ 
+   try {
+-    yield panel.selectStyleSheet(sourceURL, sourceLine);
+-    yield toolbox.selectTool("styleeditor");
++    await panel.selectStyleSheet(sourceURL, sourceLine);
++    await toolbox.selectTool("styleeditor");
+     return true;
+   } catch (e) {
+     exports.viewSource(toolbox, sourceURL, sourceLine);
+     return false;
+   }
+-});
++};
+ 
+ /**
+  * Tries to open a JavaScript file in the Debugger. If the file is not found,
+  * it is opened in source view instead.
+  * Returns a promise resolving to a boolean indicating whether or not
+  * the source was able to be displayed in the Debugger, as the built-in Firefox
+  * View Source is the fallback.
+  *
+  * @param {Toolbox} toolbox
+  * @param {string} sourceURL
+  * @param {number} sourceLine
+  *
+  * @return {Promise<boolean>}
+  */
+-exports.viewSourceInDebugger = Task.async(function* (toolbox, sourceURL, sourceLine) {
++exports.viewSourceInDebugger = async function(toolbox, sourceURL, sourceLine) {
+   // If the Debugger was already open, switch to it and try to show the
+   // source immediately. Otherwise, initialize it and wait for the sources
+   // to be added first.
+   let debuggerAlreadyOpen = toolbox.getPanel("jsdebugger");
+-  let dbg = yield toolbox.loadTool("jsdebugger");
++  let dbg = await toolbox.loadTool("jsdebugger");
+ 
+   // New debugger frontend
+   if (Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend")) {
+     const source = dbg.getSource(sourceURL);
+     if (source) {
+-      yield toolbox.selectTool("jsdebugger");
++      await toolbox.selectTool("jsdebugger");
+       dbg.selectSource(sourceURL, sourceLine);
+       return true;
+     }
+ 
+     exports.viewSource(toolbox, sourceURL, sourceLine);
+     return false;
+   }
+ 
+   const win = dbg.panelWin;
+ 
+   // Old debugger frontend
+   if (!debuggerAlreadyOpen) {
+-    yield win.DebuggerController.waitForSourcesLoaded();
++    await win.DebuggerController.waitForSourcesLoaded();
+   }
+ 
+   let { DebuggerView } = win;
+   let { Sources } = DebuggerView;
+ 
+   let item = Sources.getItemForAttachment(a => a.source.url === sourceURL);
+   if (item) {
+-    yield toolbox.selectTool("jsdebugger");
++    await toolbox.selectTool("jsdebugger");
+ 
+     // Determine if the source has already finished loading. There's two cases
+     // in which we need to wait for the source to be shown:
+     // 1) The requested source is not yet selected and will be shown once it is
+     //    selected and loaded
+     // 2) The requested source is selected BUT the source text is still loading.
+     const { actor } = item.attachment.source;
+     const state = win.DebuggerController.getState();
+@@ -107,35 +105,35 @@ exports.viewSourceInDebugger = Task.asyn
+       isLoading = sourceTextInfo && sourceTextInfo.loading;
+     }
+ 
+     // Select the requested source
+     DebuggerView.setEditorLocation(actor, sourceLine, { noDebug: true });
+ 
+     // Wait for it to load
+     if (!isSelected || isLoading) {
+-      yield win.DebuggerController.waitForSourceShown(sourceURL);
++      await win.DebuggerController.waitForSourceShown(sourceURL);
+     }
+     return true;
+   }
+ 
+   // If not found, still attempt to open in View Source
+   exports.viewSource(toolbox, sourceURL, sourceLine);
+   return false;
+-});
++};
+ 
+ /**
+  * Tries to open a JavaScript file in the corresponding Scratchpad.
+  *
+  * @param {string} sourceURL
+  * @param {number} sourceLine
+  *
+  * @return {Promise}
+  */
+-exports.viewSourceInScratchpad = Task.async(function* (sourceURL, sourceLine) {
++exports.viewSourceInScratchpad = async function(sourceURL, sourceLine) {
+   // Check for matching top level scratchpad window.
+   let wins = Services.wm.getEnumerator("devtools:scratchpad");
+ 
+   while (wins.hasMoreElements()) {
+     let win = wins.getNext();
+ 
+     if (!win.closed && win.Scratchpad.uniqueName === sourceURL) {
+       win.focus();
+@@ -153,33 +151,33 @@ exports.viewSourceInScratchpad = Task.as
+         toolbox.selectTool("scratchpad");
+         toolbox.raise();
+         scratchpad.editor.focus();
+         scratchpad.editor.setCursor({ line: sourceLine, ch: 0 });
+         return;
+       }
+     }
+   }
+-});
++};
+ 
+ /**
+  * Open a link in Firefox's View Source.
+  *
+  * @param {Toolbox} toolbox
+  * @param {string} sourceURL
+  * @param {number} sourceLine
+  *
+  * @return {Promise}
+  */
+-exports.viewSource = Task.async(function* (toolbox, sourceURL, sourceLine) {
++exports.viewSource = async function(toolbox, sourceURL, sourceLine) {
+   // Attempt to access view source via a browser first, which may display it in
+   // a tab, if enabled.
+   let browserWin = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
+   if (browserWin && browserWin.BrowserViewSourceOfDocument) {
+     return browserWin.BrowserViewSourceOfDocument({
+       URL: sourceURL,
+       lineNumber: sourceLine
+     });
+   }
+   let utils = toolbox.gViewSourceUtils;
+   utils.viewSource(sourceURL, null, toolbox.doc, sourceLine || 0);
+   return null;
+-});
++};
+diff --git a/devtools/client/shared/widgets/ColorWidget.js b/devtools/client/shared/widgets/ColorWidget.js
+--- a/devtools/client/shared/widgets/ColorWidget.js
++++ b/devtools/client/shared/widgets/ColorWidget.js
+@@ -4,17 +4,16 @@
+ 
+ /**
+  * This file is a new working copy of Spectrum.js for the purposes of refreshing the color
+  * widget. It is hidden behind a pref("devtools.inspector.colorWidget.enabled").
+  */
+ 
+ "use strict";
+ 
+-const {Task} = require("devtools/shared/task");
+ const EventEmitter = require("devtools/shared/event-emitter");
+ const {colorUtils} = require("devtools/shared/css/color");
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");
+ 
+ const XHTML_NS = "http://www.w3.org/1999/xhtml";
+ const SAMPLE_TEXT = "Abc";
+ 
+@@ -381,36 +380,36 @@ ColorWidget.prototype = {
+       h: this.element.querySelector(".colorwidget-hsla-h"),
+       s: this.element.querySelector(".colorwidget-hsla-s"),
+       l: this.element.querySelector(".colorwidget-hsla-l"),
+       a: this.element.querySelector(".colorwidget-hsla-a"),
+     };
+     this.hslaValue.addEventListener("input", this.onHslaInputChange);
+   },
+ 
+-  show: Task.async(function* () {
++  async show() {
+     this.initializeColorWidget();
+     this.element.classList.add("colorwidget-show");
+ 
+     this.slideHeight = this.slider.offsetHeight;
+     this.dragWidth = this.dragger.offsetWidth;
+     this.dragHeight = this.dragger.offsetHeight;
+     this.dragHelperHeight = this.dragHelper.offsetHeight;
+     this.slideHelperHeight = this.slideHelper.offsetHeight;
+     this.alphaSliderWidth = this.alphaSliderInner.offsetWidth;
+     this.alphaSliderHelperWidth = this.alphaSliderHelper.offsetWidth;
+ 
+     if (this.inspector && this.inspector.selection.nodeFront && this.contrastEnabled) {
+       let node = this.inspector.selection.nodeFront;
+-      this.closestBackgroundColor = yield node.getClosestBackgroundColor();
++      this.closestBackgroundColor = await node.getClosestBackgroundColor();
+     }
+     this.updateContrast();
+ 
+     this.updateUI();
+-  }),
++  },
+ 
+   onElementClick: function(e) {
+     e.stopPropagation();
+   },
+ 
+   onSliderMove: function(dragX, dragY) {
+     this.hsv[0] = (dragY / this.slideHeight);
+     this.hsl[0] = (dragY / this.slideHeight) * 360;
+diff --git a/devtools/client/shared/widgets/FlameGraph.js b/devtools/client/shared/widgets/FlameGraph.js
+--- a/devtools/client/shared/widgets/FlameGraph.js
++++ b/devtools/client/shared/widgets/FlameGraph.js
+@@ -1,14 +1,13 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+-const { Task } = require("devtools/shared/task");
+ const { ViewHelpers, setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
+ const { ELLIPSIS } = require("devtools/shared/l10n");
+ 
+ loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
+ loader.lazyRequireGetter(this, "EventEmitter",
+   "devtools/shared/event-emitter");
+ 
+ loader.lazyRequireGetter(this, "getColor",
+@@ -227,18 +226,18 @@ FlameGraph.prototype = {
+    */
+   ready: function() {
+     return this._ready.promise;
+   },
+ 
+   /**
+    * Destroys this graph.
+    */
+-  destroy: Task.async(function* () {
+-    yield this.ready();
++  async destroy() {
++    await this.ready();
+ 
+     this._window.removeEventListener("keydown", this._onKeyDown);
+     this._window.removeEventListener("keyup", this._onKeyUp);
+     this._window.removeEventListener("mousemove", this._onMouseMove);
+     this._window.removeEventListener("mousedown", this._onMouseDown);
+     this._window.removeEventListener("mouseup", this._onMouseUp);
+     this._window.removeEventListener("MozMousePixelScroll", this._onMouseWheel);
+ 
+@@ -257,17 +256,17 @@ FlameGraph.prototype = {
+     this._verticalOffsetDragger = null;
+     this._keyboardZoomAccelerationFactor = null;
+     this._keyboardPanAccelerationFactor = null;
+     this._textWidthsCache = null;
+ 
+     this._data = null;
+ 
+     this.emit("destroyed");
+-  }),
++  },
+ 
+   /**
+    * Makes sure the canvas graph is of the specified width or height, and
+    * doesn't flex to fit all the available space.
+    */
+   fixedWidth: null,
+   fixedHeight: null,
+ 
+@@ -307,20 +306,20 @@ FlameGraph.prototype = {
+   /**
+    * Same as `setData`, but waits for this graph to finish initializing first.
+    *
+    * @param object data
+    *        The data source. See the constructor for more information.
+    * @return promise
+    *         A promise resolved once the data is set.
+    */
+-  setDataWhenReady: Task.async(function* (data) {
+-    yield this.ready();
++  async setDataWhenReady(data) {
++    await this.ready();
+     this.setData(data);
+-  }),
++  },
+ 
+   /**
+    * Gets whether or not this graph has a data source.
+    * @return boolean
+    */
+   hasData: function() {
+     return !!this._data;
+   },
+diff --git a/devtools/client/shared/widgets/Graphs.js b/devtools/client/shared/widgets/Graphs.js
+--- a/devtools/client/shared/widgets/Graphs.js
++++ b/devtools/client/shared/widgets/Graphs.js
+@@ -1,14 +1,13 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+-const { Task } = require("devtools/shared/task");
+ const { setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
+ const { getCurrentZoom } = require("devtools/shared/layout/utils");
+ 
+ loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
+ loader.lazyRequireGetter(this, "EventEmitter",
+   "devtools/shared/event-emitter");
+ 
+ loader.lazyImporter(this, "DevToolsWorker",
+@@ -181,18 +180,18 @@ AbstractCanvasGraph.prototype = {
+    */
+   ready: function() {
+     return this._ready.promise;
+   },
+ 
+   /**
+    * Destroys this graph.
+    */
+-  destroy: Task.async(function* () {
+-    yield this.ready();
++  async destroy() {
++    await this.ready();
+ 
+     this._topWindow.removeEventListener("mousemove", this._onMouseMove);
+     this._topWindow.removeEventListener("mouseup", this._onMouseUp);
+     this._window.removeEventListener("mousemove", this._onMouseMove);
+     this._window.removeEventListener("mousedown", this._onMouseDown);
+     this._window.removeEventListener("MozMousePixelScroll", this._onMouseWheel);
+     this._window.removeEventListener("mouseout", this._onMouseOut);
+ 
+@@ -216,17 +215,17 @@ AbstractCanvasGraph.prototype = {
+ 
+     this._cachedBackgroundImage = null;
+     this._cachedGraphImage = null;
+     this._cachedMaskImage = null;
+     this._renderTargets.clear();
+     gCachedStripePattern.clear();
+ 
+     this.emit("destroyed");
+-  }),
++  },
+ 
+   /**
+    * Rendering options. Subclasses should override these.
+    */
+   clipheadLineWidth: 1,
+   clipheadLineColor: "transparent",
+   selectionLineWidth: 1,
+   selectionLineColor: "transparent",
+@@ -292,20 +291,20 @@ AbstractCanvasGraph.prototype = {
+   /**
+    * Same as `setData`, but waits for this graph to finish initializing first.
+    *
+    * @param object data
+    *        The data source. The actual format is specified by subclasses.
+    * @return promise
+    *         A promise resolved once the data is set.
+    */
+-  setDataWhenReady: Task.async(function* (data) {
+-    yield this.ready();
++  async setDataWhenReady(data) {
++    await this.ready();
+     this.setData(data);
+-  }),
++  },
+ 
+   /**
+    * Adds a mask to this graph.
+    *
+    * @param any mask, options
+    *        See `buildMaskImage` in inheriting classes for the required args.
+    */
+   setMask: function(mask, ...options) {
+@@ -1311,35 +1310,35 @@ const gCachedStripePattern = new Map();
+  */
+ this.CanvasGraphUtils = {
+   _graphUtilsWorker: null,
+   _graphUtilsTaskId: 0,
+ 
+   /**
+    * Merges the animation loop of two graphs.
+    */
+-  linkAnimation: Task.async(function* (graph1, graph2) {
++  async linkAnimation(graph1, graph2) {
+     if (!graph1 || !graph2) {
+       return;
+     }
+-    yield graph1.ready();
+-    yield graph2.ready();
++    await graph1.ready();
++    await graph2.ready();
+ 
+     let window = graph1._window;
+     window.cancelAnimationFrame(graph1._animationId);
+     window.cancelAnimationFrame(graph2._animationId);
+ 
+     let loop = () => {
+       window.requestAnimationFrame(loop);
+       graph1._drawWidget();
+       graph2._drawWidget();
+     };
+ 
+     window.requestAnimationFrame(loop);
+-  }),
++  },
+ 
+   /**
+    * Makes sure selections in one graph are reflected in another.
+    */
+   linkSelection: function(graph1, graph2) {
+     if (!graph1 || !graph2) {
+       return;
+     }
+diff --git a/devtools/client/shared/widgets/LineGraphWidget.js b/devtools/client/shared/widgets/LineGraphWidget.js
+--- a/devtools/client/shared/widgets/LineGraphWidget.js
++++ b/devtools/client/shared/widgets/LineGraphWidget.js
+@@ -1,11 +1,10 @@
+ "use strict";
+ 
+-const { Task } = require("devtools/shared/task");
+ const { extend } = require("devtools/shared/extend");
+ const { AbstractCanvasGraph, CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");
+ const { LocalizationHelper } = require("devtools/shared/l10n");
+ 
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const L10N = new LocalizationHelper("devtools/client/locales/graphs.properties");
+ 
+ // Line graph constants.
+@@ -141,27 +140,27 @@ LineGraphWidget.prototype = extend(Abstr
+    *        A list of numbers representing time, ordered ascending. For example,
+    *        this can be the raw data received from the framerate actor, which
+    *        represents the elapsed time on each refresh driver tick.
+    * @param number interval
+    *        The maximum amount of time to wait between calculations.
+    * @param number duration
+    *        The duration of the recording in milliseconds.
+    */
+-  setDataFromTimestamps: Task.async(function* (timestamps, interval, duration) {
++  async setDataFromTimestamps(timestamps, interval, duration) {
+     let {
+       plottedData,
+       plottedMinMaxSum
+-    } = yield CanvasGraphUtils._performTaskInWorker("plotTimestampsGraph", {
++    } = await CanvasGraphUtils._performTaskInWorker("plotTimestampsGraph", {
+       timestamps, interval, duration
+     });
+ 
+     this._tempMinMaxSum = plottedMinMaxSum;
+     this.setData(plottedData);
+-  }),
++  },
+ 
+   /**
+    * Renders the graph's data source.
+    * @see AbstractCanvasGraph.prototype.buildGraphImage
+    */
+   buildGraphImage: function() {
+     let { canvas, ctx } = this._getNamedCanvas("line-graph-data");
+     let width = this._width;
+diff --git a/devtools/client/shared/widgets/VariablesView.jsm b/devtools/client/shared/widgets/VariablesView.jsm
+--- a/devtools/client/shared/widgets/VariablesView.jsm
++++ b/devtools/client/shared/widgets/VariablesView.jsm
+@@ -19,17 +19,16 @@ const EventEmitter = require("devtools/s
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ const Services = require("Services");
+ const { getSourceNames } = require("devtools/client/shared/source-utils");
+ const promise = require("promise");
+ const defer = require("devtools/shared/defer");
+ const { extend } = require("devtools/shared/extend");
+ const { ViewHelpers, setNamedTimeout } =
+   require("devtools/client/shared/widgets/view-helpers");
+-const { Task } = require("devtools/shared/task");
+ const nodeConstants = require("devtools/shared/dom-node-constants");
+ const {KeyCodes} = require("devtools/client/shared/keycodes");
+ const {PluralForm} = require("devtools/shared/plural-form");
+ const {LocalizationHelper, ELLIPSIS} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper(DBG_STRINGS_URI);
+ 
+ XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
+   "@mozilla.org/widget/clipboardhelper;1",
+@@ -2771,33 +2770,33 @@ Variable.prototype = extend(Scope.protot
+    */
+   openNodeInInspector: function (event) {
+     if (!this.toolbox) {
+       return promise.reject(new Error("Toolbox not available"));
+     }
+ 
+     event && event.stopPropagation();
+ 
+-    return Task.spawn(function* () {
+-      yield this.toolbox.initInspector();
++    return (async function () {
++      await this.toolbox.initInspector();
+ 
+       let nodeFront = this._nodeFront;
+       if (!nodeFront) {
+-        nodeFront = yield this.toolbox.walker.getNodeActorFromObjectActor(this._valueGrip.actor);
++        nodeFront = await this.toolbox.walker.getNodeActorFromObjectActor(this._valueGrip.actor);
+       }
+ 
+       if (nodeFront) {
+-        yield this.toolbox.selectTool("inspector");
++        await this.toolbox.selectTool("inspector");
+ 
+         let inspectorReady = defer();
+         this.toolbox.getPanel("inspector").once("inspector-updated", inspectorReady.resolve);
+-        yield this.toolbox.selection.setNodeFront(nodeFront, "variables-view");
+-        yield inspectorReady.promise;
++        await this.toolbox.selection.setNodeFront(nodeFront, "variables-view");
++        await inspectorReady.promise;
+       }
+-    }.bind(this));
++    }.bind(this))();
+   },
+ 
+   /**
+    * In case this variable is a DOMNode and part of a variablesview that has been
+    * linked to the toolbox's inspector, then highlight the corresponding node
+    */
+   highlightDomNode: function () {
+     if (this.toolbox) {
+diff --git a/devtools/client/shared/widgets/tooltip/HTMLTooltip.js b/devtools/client/shared/widgets/tooltip/HTMLTooltip.js
+--- a/devtools/client/shared/widgets/tooltip/HTMLTooltip.js
++++ b/devtools/client/shared/widgets/tooltip/HTMLTooltip.js
+@@ -4,17 +4,16 @@
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ const EventEmitter = require("devtools/shared/event-emitter");
+ const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");
+ const {listenOnce} = require("devtools/shared/async-utils");
+-const {Task} = require("devtools/shared/task");
+ 
+ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ const XHTML_NS = "http://www.w3.org/1999/xhtml";
+ 
+ const POSITION = {
+   TOP: "top",
+   BOTTOM: "bottom",
+ };
+@@ -319,17 +318,17 @@ HTMLTooltip.prototype = {
+    * @param {Object}
+    *        - {String} position: optional, possible values: top|bottom
+    *          If layout permits, the tooltip will be displayed on top/bottom
+    *          of the anchor. If ommitted, the tooltip will be displayed where
+    *          more space is available.
+    *        - {Number} x: optional, horizontal offset between the anchor and the tooltip
+    *        - {Number} y: optional, vertical offset between the anchor and the tooltip
+    */
+-  show: Task.async(function* (anchor, {position, x = 0, y = 0} = {}) {
++  async show(anchor, {position, x = 0, y = 0} = {}) {
+     // Get anchor geometry
+     let anchorRect = getRelativeRect(anchor, this.doc);
+     if (this.useXulWrapper) {
+       anchorRect = this._convertToScreenRect(anchorRect);
+     }
+ 
+     // Get viewport size
+     let viewportRect = this._getViewportRect();
+@@ -368,17 +367,17 @@ HTMLTooltip.prototype = {
+ 
+     this.container.style.width = width + "px";
+ 
+     if (this.type === TYPE.ARROW) {
+       this.arrow.style.left = arrowLeft + "px";
+     }
+ 
+     if (this.useXulWrapper) {
+-      yield this._showXulWrapperAt(left, top);
++      await this._showXulWrapperAt(left, top);
+     } else {
+       this.container.style.left = left + "px";
+       this.container.style.top = top + "px";
+     }
+ 
+     this.container.classList.add("tooltip-visible");
+ 
+     // Keep a pointer on the focused element to refocus it when hiding the tooltip.
+@@ -387,17 +386,17 @@ HTMLTooltip.prototype = {
+     this.doc.defaultView.clearTimeout(this.attachEventsTimer);
+     this.attachEventsTimer = this.doc.defaultView.setTimeout(() => {
+       this._maybeFocusTooltip();
+       // Updated the top window reference each time in case the host changes.
+       this.topWindow = this._getTopWindow();
+       this.topWindow.addEventListener("click", this._onClick, true);
+       this.emit("shown");
+     }, 0);
+-  }),
++  },
+ 
+   /**
+    * Calculate the rect of the viewport that limits the tooltip dimensions. When using a
+    * XUL panel wrapper, the viewport will be able to use the whole screen (excluding space
+    * reserved by the OS for toolbars etc.). Otherwise, the viewport is limited to the
+    * tooltip's document.
+    *
+    * @return {Object} DOMRect-like object with the Number properties: top, right, bottom,
+@@ -441,37 +440,37 @@ HTMLTooltip.prototype = {
+ 
+     return width;
+   },
+ 
+   /**
+    * Hide the current tooltip. The event "hidden" will be fired when the tooltip
+    * is hidden.
+    */
+-  hide: Task.async(function* () {
++  async hide() {
+     this.doc.defaultView.clearTimeout(this.attachEventsTimer);
+     if (!this.isVisible()) {
+       this.emit("hidden");
+       return;
+     }
+ 
+     this.topWindow.removeEventListener("click", this._onClick, true);
+     this.container.classList.remove("tooltip-visible");
+     if (this.useXulWrapper) {
+-      yield this._hideXulWrapper();
++      await this._hideXulWrapper();
+     }
+ 
+     this.emit("hidden");
+ 
+     let tooltipHasFocus = this.container.contains(this.doc.activeElement);
+     if (tooltipHasFocus && this._focusedElement) {
+       this._focusedElement.focus();
+       this._focusedElement = null;
+     }
+-  }),
++  },
+ 
+   /**
+    * Check if the tooltip is currently displayed.
+    * @return {Boolean} true if the tooltip is visible
+    */
+   isVisible: function() {
+     return this.container.classList.contains("tooltip-visible");
+   },
+diff --git a/devtools/client/shared/widgets/tooltip/TooltipToggle.js b/devtools/client/shared/widgets/tooltip/TooltipToggle.js
+--- a/devtools/client/shared/widgets/tooltip/TooltipToggle.js
++++ b/devtools/client/shared/widgets/tooltip/TooltipToggle.js
+@@ -1,18 +1,16 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ft=javascript ts=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/. */
+ 
+ "use strict";
+ 
+-const {Task} = require("devtools/shared/task");
+-
+ const DEFAULT_TOGGLE_DELAY = 50;
+ 
+ /**
+  * Tooltip helper designed to show/hide the tooltip when the mouse hovers over
+  * particular nodes.
+  *
+  * This works by tracking mouse movements on a base container node (baseNode)
+  * and showing the tooltip when the mouse stops moving. A callback can be
+@@ -139,24 +137,24 @@ TooltipToggle.prototype = {
+   },
+ 
+   /**
+    * Is the given target DOMNode a valid node for toggling the tooltip on hover.
+    * This delegates to the user-defined _targetNodeCb callback.
+    * @return {Promise} a promise that will resolve the anchor to use for the
+    *         tooltip or null if no valid target was found.
+    */
+-  isValidHoverTarget: Task.async(function* (target) {
+-    let res = yield this._targetNodeCb(target, this.tooltip);
++  async isValidHoverTarget(target) {
++    let res = await this._targetNodeCb(target, this.tooltip);
+     if (res) {
+       return res.nodeName ? res : target;
+     }
+ 
+     return null;
+-  }),
++  },
+ 
+   _onMouseOut: function(event) {
+     // Only hide the tooltip if the mouse leaves baseNode.
+     if (event && this._baseNode && this._baseNode.contains(event.relatedTarget)) {
+       return;
+     }
+ 
+     this._lastHovered = null;

+ 330 - 0
frg/work-js/mozilla-release/patches/1440321-1n-sourceeditor-61a1.patch

@@ -0,0 +1,330 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  7bff5e08e3f45ecfce4fab700ab9c8380ccbfdd8
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1n. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js b/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
+--- a/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
++++ b/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
+@@ -3,125 +3,125 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const {InspectorFront} = require("devtools/shared/fronts/inspector");
+ const TEST_URI = "data:text/html;charset=UTF-8,<html><body><bar></bar>" +
+                  "<div id='baz'></div><body></html>";
+ 
+-add_task(function* () {
+-  yield addTab(TEST_URI);
+-  yield runTests();
++add_task(async function() {
++  await addTab(TEST_URI);
++  await runTests();
+ });
+ 
+-function* runTests() {
++async function runTests() {
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  yield target.makeRemote();
++  await target.makeRemote();
+   let inspector = InspectorFront(target.client, target.form);
+-  let walker = yield inspector.getWalker();
+-  let {ed, win, edWin} = yield setup(null, {
++  let walker = await inspector.getWalker();
++  let {ed, win, edWin} = await setup(null, {
+     autocomplete: true,
+     mode: Editor.modes.css,
+     autocompleteOpts: {walker: walker, cssProperties: getClientCssProperties()}
+   });
+-  yield testMouse(ed, edWin);
+-  yield testKeyboard(ed, edWin);
+-  yield testKeyboardCycle(ed, edWin);
+-  yield testKeyboardCycleForPrefixedString(ed, edWin);
+-  yield testKeyboardCSSComma(ed, edWin);
++  await testMouse(ed, edWin);
++  await testKeyboard(ed, edWin);
++  await testKeyboardCycle(ed, edWin);
++  await testKeyboardCycleForPrefixedString(ed, edWin);
++  await testKeyboardCSSComma(ed, edWin);
+   teardown(ed, win);
+ }
+ 
+-function* testKeyboard(ed, win) {
++async function testKeyboard(ed, win) {
+   ed.focus();
+   ed.setText("b");
+   ed.setCursor({line: 1, ch: 1});
+ 
+   let popupOpened = ed.getAutocompletionPopup().once("popup-opened");
+ 
+   let autocompleteKey =
+     Editor.keyFor("autocompletion", { noaccel: true }).toUpperCase();
+   EventUtils.synthesizeKey("VK_" + autocompleteKey, { ctrlKey: true }, win);
+ 
+   info("Waiting for popup to be opened");
+-  yield popupOpened;
++  await popupOpened;
+ 
+   EventUtils.synthesizeKey("VK_RETURN", { }, win);
+   is(ed.getText(), "bar", "Editor text has been updated");
+ }
+ 
+-function* testKeyboardCycle(ed, win) {
++async function testKeyboardCycle(ed, win) {
+   ed.focus();
+   ed.setText("b");
+   ed.setCursor({line: 1, ch: 1});
+ 
+   let popupOpened = ed.getAutocompletionPopup().once("popup-opened");
+ 
+   let autocompleteKey =
+     Editor.keyFor("autocompletion", { noaccel: true }).toUpperCase();
+   EventUtils.synthesizeKey("VK_" + autocompleteKey, { ctrlKey: true }, win);
+ 
+   info("Waiting for popup to be opened");
+-  yield popupOpened;
++  await popupOpened;
+ 
+   EventUtils.synthesizeKey("VK_DOWN", { }, win);
+   is(ed.getText(), "bar", "Editor text has been updated");
+ 
+   EventUtils.synthesizeKey("VK_DOWN", { }, win);
+   is(ed.getText(), "body", "Editor text has been updated");
+ 
+   EventUtils.synthesizeKey("VK_DOWN", { }, win);
+   is(ed.getText(), "#baz", "Editor text has been updated");
+ }
+ 
+-function* testKeyboardCycleForPrefixedString(ed, win) {
++async function testKeyboardCycleForPrefixedString(ed, win) {
+   ed.focus();
+   ed.setText("#b");
+   ed.setCursor({line: 1, ch: 2});
+ 
+   let popupOpened = ed.getAutocompletionPopup().once("popup-opened");
+ 
+   let autocompleteKey =
+     Editor.keyFor("autocompletion", { noaccel: true }).toUpperCase();
+   EventUtils.synthesizeKey("VK_" + autocompleteKey, { ctrlKey: true }, win);
+ 
+   info("Waiting for popup to be opened");
+-  yield popupOpened;
++  await popupOpened;
+ 
+   EventUtils.synthesizeKey("VK_DOWN", { }, win);
+   is(ed.getText(), "#baz", "Editor text has been updated");
+ }
+ 
+-function* testKeyboardCSSComma(ed, win) {
++async function testKeyboardCSSComma(ed, win) {
+   ed.focus();
+   ed.setText("b");
+   ed.setCursor({line: 1, ch: 1});
+ 
+   let isPopupOpened = false;
+   let popupOpened = ed.getAutocompletionPopup().once("popup-opened");
+   popupOpened.then(() => {
+     isPopupOpened = true;
+   });
+ 
+   EventUtils.synthesizeKey(",", { }, win);
+ 
+-  yield wait(500);
++  await wait(500);
+ 
+   ok(!isPopupOpened, "Autocompletion shouldn't be opened");
+ }
+ 
+-function* testMouse(ed, win) {
++async function testMouse(ed, win) {
+   ed.focus();
+   ed.setText("b");
+   ed.setCursor({line: 1, ch: 1});
+ 
+   let popupOpened = ed.getAutocompletionPopup().once("popup-opened");
+ 
+   let autocompleteKey =
+     Editor.keyFor("autocompletion", { noaccel: true }).toUpperCase();
+   EventUtils.synthesizeKey("VK_" + autocompleteKey, { ctrlKey: true }, win);
+ 
+   info("Waiting for popup to be opened");
+-  yield popupOpened;
++  await popupOpened;
+   ed.getAutocompletionPopup()._list.children[2].click();
+   is(ed.getText(), "#baz", "Editor text has been updated");
+ }
+diff --git a/devtools/client/sourceeditor/test/browser_editor_find_again.js b/devtools/client/sourceeditor/test/browser_editor_find_again.js
+--- a/devtools/client/sourceeditor/test/browser_editor_find_again.js
++++ b/devtools/client/sourceeditor/test/browser_editor_find_again.js
+@@ -65,17 +65,17 @@ function testFindAgain(ed, inputLine, ex
+   } else {
+     synthesizeKeyShortcut(FINDNEXT_KEY, edWin);
+   }
+ 
+   ch(ed.getCursor(), expectCursor,
+     "find: " + inputLine + " expects cursor: " + expectCursor.toSource());
+ }
+ 
+-const testSearchBoxTextIsSelected = Task.async(function* (ed) {
++const testSearchBoxTextIsSelected = async function(ed) {
+   let edDoc = ed.container.contentDocument;
+   let edWin = edDoc.defaultView;
+ 
+   let input = edDoc.querySelector("input[type=search]");
+   ok(input, "search box is opened");
+ 
+   // Ensure the input has the focus before send the key – necessary on Linux,
+   // it seems that during the tests can be lost
+@@ -88,17 +88,17 @@ const testSearchBoxTextIsSelected = Task
+   ok(!input, "search box is closed");
+ 
+   // Re-open the search box
+   synthesizeKeyShortcut(FIND_KEY, edWin);
+ 
+   input = edDoc.querySelector("input[type=search]");
+   ok(input, "find command key opens the search box");
+ 
+-  yield dispatchAndWaitForFocus(input);
++  await dispatchAndWaitForFocus(input);
+ 
+   let { selectionStart, selectionEnd, value } = input;
+ 
+   ok(selectionStart === 0 && selectionEnd === value.length,
+     "search box's text is selected when re-opened");
+ 
+   // Removing selection
+   input.setSelectionRange(0, 0);
+@@ -107,19 +107,19 @@ const testSearchBoxTextIsSelected = Task
+ 
+   ({ selectionStart, selectionEnd } = input);
+ 
+   ok(selectionStart === 0 && selectionEnd === value.length,
+     "search box's text is selected when find key is pressed");
+ 
+   // Close search box
+   EventUtils.synthesizeKey("VK_ESCAPE", {}, edWin);
+-});
++};
+ 
+-const testReplaceBoxTextIsSelected = Task.async(function* (ed) {
++const testReplaceBoxTextIsSelected = async function(ed) {
+   let edDoc = ed.container.contentDocument;
+   let edWin = edDoc.defaultView;
+ 
+   let input = edDoc.querySelector(".CodeMirror-dialog > input");
+   ok(!input, "dialog box with replace is closed");
+ 
+   // The editor needs the focus to properly receive the `synthesizeKey`
+   ed.focus();
+@@ -130,46 +130,46 @@ const testReplaceBoxTextIsSelected = Tas
+   ok(input, "dialog box with replace is opened");
+ 
+   input.value = "line 5";
+ 
+   // Ensure the input has the focus before send the key – necessary on Linux,
+   // it seems that during the tests can be lost
+   input.focus();
+ 
+-  yield dispatchAndWaitForFocus(input);
++  await dispatchAndWaitForFocus(input);
+ 
+   let { selectionStart, selectionEnd, value } = input;
+ 
+   ok(!(selectionStart === 0 && selectionEnd === value.length),
+     "Text in dialog box is not selected");
+ 
+   synthesizeKeyShortcut(REPLACE_KEY, edWin);
+ 
+   ({ selectionStart, selectionEnd } = input);
+ 
+   ok(selectionStart === 0 && selectionEnd === value.length,
+     "dialog box's text is selected when replace key is pressed");
+ 
+   // Close dialog box
+   EventUtils.synthesizeKey("VK_ESCAPE", {}, edWin);
+-});
++};
+ 
+-add_task(function* () {
+-  let { ed, win } = yield setup();
++add_task(async function() {
++  let { ed, win } = await setup();
+ 
+   ed.setText([
+     "// line 1",
+     "//  line 2",
+     "//   line 3",
+     "//    line 4",
+     "//     line 5"
+   ].join("\n"));
+ 
+-  yield promiseWaitForFocus();
++  await promiseWaitForFocus();
+ 
+   openSearchBox(ed);
+ 
+   let testVectors = [
+     // Starting here expect data needs to get updated for length changes to
+     // "textLines" above.
+     ["line",
+      {line: 0, ch: 7}],
+@@ -199,17 +199,17 @@ add_task(function* () {
+      {line: 1, ch: 8},
+      true],
+     ["line",
+      {line: 0, ch: 7},
+      true]
+   ];
+ 
+   for (let v of testVectors) {
+-    yield testFindAgain(ed, ...v);
++    await testFindAgain(ed, ...v);
+   }
+ 
+-  yield testSearchBoxTextIsSelected(ed);
++  await testSearchBoxTextIsSelected(ed);
+ 
+-  yield testReplaceBoxTextIsSelected(ed);
++  await testReplaceBoxTextIsSelected(ed);
+ 
+   teardown(ed, win);
+ });
+diff --git a/devtools/client/sourceeditor/test/browser_editor_script_injection.js b/devtools/client/sourceeditor/test/browser_editor_script_injection.js
+--- a/devtools/client/sourceeditor/test/browser_editor_script_injection.js
++++ b/devtools/client/sourceeditor/test/browser_editor_script_injection.js
+@@ -1,26 +1,26 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ // Test the externalScripts option, which allows custom language modes or
+ // other scripts to be injected into the editor window.  See Bug 1089428.
+ 
+ "use strict";
+ 
+-add_task(function* () {
+-  yield runTest();
++add_task(async function() {
++  await runTest();
+ });
+ 
+-function* runTest() {
++async function runTest() {
+   const baseURL =
+     "chrome://mochitests/content/browser/devtools/client/sourceeditor/test";
+   const injectedText = "Script successfully injected!";
+ 
+-  let {ed, win} = yield setup(null, {
++  let {ed, win} = await setup(null, {
+     mode: "ruby",
+     externalScripts: [`${baseURL}/cm_script_injection_test.js`,
+                       `${baseURL}/cm_mode_ruby.js`]
+   });
+ 
+   is(ed.getText(), injectedText, "The text has been injected");
+   is(ed.getOption("mode"), "ruby", "The ruby mode is correctly set");
+   teardown(ed, win);

+ 3369 - 0
frg/work-js/mozilla-release/patches/1440321-1o-storage-61a1.patch

@@ -0,0 +1,3369 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  82427acbea499f41efeb1d23f4f93656d36215ec
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1o. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/storage/test/browser_storage_basic.js b/devtools/client/storage/test/browser_storage_basic.js
+--- a/devtools/client/storage/test/browser_storage_basic.js
++++ b/devtools/client/storage/test/browser_storage_basic.js
+@@ -97,44 +97,44 @@ function testTree() {
+     ok(doc.querySelector("[data-id='" + JSON.stringify(item) + "']"),
+        "Tree item " + item[0] + " should be present in the storage tree");
+   }
+ }
+ 
+ /**
+  * Test that correct table entries are shown for each of the tree item
+  */
+-function* testTables() {
++async function testTables() {
+   let doc = gPanelWindow.document;
+   // Expand all nodes so that the synthesized click event actually works
+   gUI.tree.expandAll();
+ 
+   // First tree item is already selected so no clicking and waiting for update
+   for (let id of testCases[0][1]) {
+     ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+        "Table item " + id + " should be present");
+   }
+ 
+   // Click rest of the tree items and wait for the table to be updated
+   for (let [treeItem, items] of testCases.slice(1)) {
+-    yield selectTreeItem(treeItem);
++    await selectTreeItem(treeItem);
+ 
+     // Check whether correct number of items are present in the table
+     is(doc.querySelectorAll(
+          ".table-widget-wrapper:first-of-type .table-widget-cell"
+        ).length, items.length, "Number of items in table is correct");
+ 
+     // Check if all the desired items are present in the table
+     for (let id of items) {
+       ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+          "Table item " + id + " should be present");
+     }
+   }
+ }
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
+ 
+   testTree();
+-  yield testTables();
++  await testTables();
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_basic_usercontextid_1.js b/devtools/client/storage/test/browser_storage_basic_usercontextid_1.js
+--- a/devtools/client/storage/test/browser_storage_basic_usercontextid_1.js
++++ b/devtools/client/storage/test/browser_storage_basic_usercontextid_1.js
+@@ -82,44 +82,44 @@ function testTree(tests) {
+     ok(doc.querySelector("[data-id='" + JSON.stringify(item) + "']"),
+        "Tree item " + item[0] + " should be present in the storage tree");
+   }
+ }
+ 
+ /**
+  * Test that correct table entries are shown for each of the tree item
+  */
+-function* testTables(tests) {
++async function testTables(tests) {
+   let doc = gPanelWindow.document;
+   // Expand all nodes so that the synthesized click event actually works
+   gUI.tree.expandAll();
+ 
+   // First tree item is already selected so no clicking and waiting for update
+   for (let id of tests[0][1]) {
+     ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+        "Table item " + id + " should be present");
+   }
+ 
+   // Click rest of the tree items and wait for the table to be updated
+   for (let [treeItem, items] of tests.slice(1)) {
+-    yield selectTreeItem(treeItem);
++    await selectTreeItem(treeItem);
+ 
+     // Check whether correct number of items are present in the table
+     is(doc.querySelectorAll(
+          ".table-widget-wrapper:first-of-type .table-widget-cell"
+        ).length, items.length, "Number of items in table is correct");
+ 
+     // Check if all the desired items are present in the table
+     for (let id of items) {
+       ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+          "Table item " + id + " should be present");
+     }
+   }
+ }
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
+ 
+   testTree(testCases);
+-  yield testTables(testCases);
++  await testTables(testCases);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_basic_usercontextid_2.js b/devtools/client/storage/test/browser_storage_basic_usercontextid_2.js
+--- a/devtools/client/storage/test/browser_storage_basic_usercontextid_2.js
++++ b/devtools/client/storage/test/browser_storage_basic_usercontextid_2.js
+@@ -76,45 +76,45 @@ function testTree(tests) {
+     ok(doc.querySelector("[data-id='" + JSON.stringify(item) + "']"),
+        "Tree item " + item[0] + " should be present in the storage tree");
+   }
+ }
+ 
+ /**
+  * Test that correct table entries are shown for each of the tree item
+  */
+-function* testTables(tests) {
++async function testTables(tests) {
+   let doc = gPanelWindow.document;
+   // Expand all nodes so that the synthesized click event actually works
+   gUI.tree.expandAll();
+ 
+   // First tree item is already selected so no clicking and waiting for update
+   for (let id of tests[0][1]) {
+     ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+        "Table item " + id + " should be present");
+   }
+ 
+   // Click rest of the tree items and wait for the table to be updated
+   for (let [treeItem, items] of tests.slice(1)) {
+-    yield selectTreeItem(treeItem);
++    await selectTreeItem(treeItem);
+ 
+     // Check whether correct number of items are present in the table
+     is(doc.querySelectorAll(
+          ".table-widget-wrapper:first-of-type .table-widget-cell"
+        ).length, items.length, "Number of items in table is correct");
+ 
+     // Check if all the desired items are present in the table
+     for (let id of items) {
+       ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+          "Table item " + id + " should be present");
+     }
+   }
+ }
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings-usercontextid.html",
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings-usercontextid.html",
+                                {userContextId: 1});
+ 
+   testTree(testCasesUserContextId);
+-  yield testTables(testCasesUserContextId);
++  await testTables(testCasesUserContextId);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_basic_with_fragment.js b/devtools/client/storage/test/browser_storage_basic_with_fragment.js
+--- a/devtools/client/storage/test/browser_storage_basic_with_fragment.js
++++ b/devtools/client/storage/test/browser_storage_basic_with_fragment.js
+@@ -100,45 +100,45 @@ function testTree() {
+     ok(doc.querySelector("[data-id='" + JSON.stringify(item) + "']"),
+        "Tree item " + item[0] + " should be present in the storage tree");
+   }
+ }
+ 
+ /**
+  * Test that correct table entries are shown for each of the tree item
+  */
+-function* testTables() {
++async function testTables() {
+   let doc = gPanelWindow.document;
+   // Expand all nodes so that the synthesized click event actually works
+   gUI.tree.expandAll();
+ 
+   // First tree item is already selected so no clicking and waiting for update
+   for (let id of testCases[0][1]) {
+     ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+        "Table item " + id + " should be present");
+   }
+ 
+   // Click rest of the tree items and wait for the table to be updated
+   for (let [treeItem, items] of testCases.slice(1)) {
+-    yield selectTreeItem(treeItem);
++    await selectTreeItem(treeItem);
+ 
+     // Check whether correct number of items are present in the table
+     is(doc.querySelectorAll(
+          ".table-widget-wrapper:first-of-type .table-widget-cell"
+        ).length, items.length, "Number of items in table is correct");
+ 
+     // Check if all the desired items are present in the table
+     for (let id of items) {
+       ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+          "Table item " + id + " should be present");
+     }
+   }
+ }
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(
++add_task(async function() {
++  await openTabAndSetupStorage(
+     MAIN_DOMAIN + "storage-listings-with-fragment.html#abc");
+ 
+   testTree();
+-  yield testTables();
++  await testTables();
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_cache_delete.js b/devtools/client/storage/test/browser_storage_cache_delete.js
+--- a/devtools/client/storage/test/browser_storage_cache_delete.js
++++ b/devtools/client/storage/test/browser_storage_cache_delete.js
+@@ -3,44 +3,44 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
+ "use strict";
+ 
+ // Test deleting a Cache object from the tree using context menu
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
+ 
+   let contextMenu = gPanelWindow.document.getElementById("storage-tree-popup");
+   let menuDeleteItem = contextMenu.querySelector("#storage-tree-popup-delete");
+ 
+   let cacheToDelete = ["Cache", "http://test1.example.org", "plop"];
+ 
+   info("test state before delete");
+-  yield selectTreeItem(cacheToDelete);
++  await selectTreeItem(cacheToDelete);
+   ok(gUI.tree.isSelected(cacheToDelete), "Cache item is present in the tree");
+ 
+   info("do the delete");
+   let eventWait = gUI.once("store-objects-updated");
+ 
+   let selector = `[data-id='${JSON.stringify(cacheToDelete)}'] > .tree-widget-item`;
+   let target = gPanelWindow.document.querySelector(selector);
+   ok(target, "Cache item's tree element is present");
+ 
+-  yield waitForContextMenu(contextMenu, target, () => {
++  await waitForContextMenu(contextMenu, target, () => {
+     info("Opened tree context menu");
+     menuDeleteItem.click();
+ 
+     let cacheName = cacheToDelete[2];
+     ok(menuDeleteItem.getAttribute("label").includes(cacheName),
+       `Context menu item label contains '${cacheName}')`);
+   });
+ 
+-  yield eventWait;
++  await eventWait;
+ 
+   info("test state after delete");
+-  yield selectTreeItem(cacheToDelete);
++  await selectTreeItem(cacheToDelete);
+   ok(!gUI.tree.isSelected(cacheToDelete), "Cache item is no longer present in the tree");
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_cache_error.js b/devtools/client/storage/test/browser_storage_cache_error.js
+--- a/devtools/client/storage/test/browser_storage_cache_error.js
++++ b/devtools/client/storage/test/browser_storage_cache_error.js
+@@ -1,19 +1,19 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ // Test handling errors in CacheStorage
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cache-error.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-cache-error.html");
+ 
+   const cacheItemId = ["Cache", "javascript:parent.frameContent"];
+ 
+-  yield selectTreeItem(cacheItemId);
++  await selectTreeItem(cacheItemId);
+   ok(gUI.tree.isSelected(cacheItemId),
+     `The item ${cacheItemId.join(" > ")} is present in the tree`);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_cookies_add.js b/devtools/client/storage/test/browser_storage_cookies_add.js
+--- a/devtools/client/storage/test/browser_storage_cookies_add.js
++++ b/devtools/client/storage/test/browser_storage_cookies_add.js
+@@ -1,20 +1,20 @@
+ /* 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/. */
+ 
+ // Basic test to check the adding of cookies.
+ 
+ "use strict";
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
+   showAllColumns(true);
+ 
+-  yield performAdd(["cookies", "http://test1.example.org"]);
+-  yield performAdd(["cookies", "http://test1.example.org"]);
+-  yield performAdd(["cookies", "http://test1.example.org"]);
+-  yield performAdd(["cookies", "http://test1.example.org"]);
+-  yield performAdd(["cookies", "http://test1.example.org"]);
++  await performAdd(["cookies", "http://test1.example.org"]);
++  await performAdd(["cookies", "http://test1.example.org"]);
++  await performAdd(["cookies", "http://test1.example.org"]);
++  await performAdd(["cookies", "http://test1.example.org"]);
++  await performAdd(["cookies", "http://test1.example.org"]);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_cookies_delete_all.js b/devtools/client/storage/test/browser_storage_cookies_delete_all.js
+--- a/devtools/client/storage/test/browser_storage_cookies_delete_all.js
++++ b/devtools/client/storage/test/browser_storage_cookies_delete_all.js
+@@ -3,34 +3,34 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
+ "use strict";
+ 
+ // Test deleting all cookies
+ 
+-function* performDelete(store, rowName, action) {
++async function performDelete(store, rowName, action) {
+   let contextMenu = gPanelWindow.document.getElementById(
+     "storage-table-popup");
+   let menuDeleteAllItem = contextMenu.querySelector(
+     "#storage-table-popup-delete-all");
+   let menuDeleteAllSessionCookiesItem = contextMenu.querySelector(
+     "#storage-table-popup-delete-all-session-cookies");
+   let menuDeleteAllFromItem = contextMenu.querySelector(
+     "#storage-table-popup-delete-all-from");
+ 
+   let storeName = store.join(" > ");
+ 
+-  yield selectTreeItem(store);
++  await selectTreeItem(store);
+ 
+   let eventWait = gUI.once("store-objects-edit");
+   let cells = getRowCells(rowName, true);
+ 
+-  yield waitForContextMenu(contextMenu, cells.name, () => {
++  await waitForContextMenu(contextMenu, cells.name, () => {
+     info(`Opened context menu in ${storeName}, row '${rowName}'`);
+     switch (action) {
+       case "deleteAll":
+         menuDeleteAllItem.click();
+         break;
+       case "deleteAllSessionCookies":
+         menuDeleteAllSessionCookiesItem.click();
+         break;
+@@ -38,24 +38,24 @@ function* performDelete(store, rowName, 
+         menuDeleteAllFromItem.click();
+         let hostName = cells.host.value;
+         ok(menuDeleteAllFromItem.getAttribute("label").includes(hostName),
+         `Context menu item label contains '${hostName}'`);
+         break;
+     }
+   });
+ 
+-  yield eventWait;
++  await eventWait;
+ }
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
+ 
+   info("test state before delete");
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org"], [
+         getCookieId("c1", "test1.example.org", "/browser"),
+         getCookieId("c3", "test1.example.org", "/"),
+         getCookieId("cs2", ".example.org", "/"),
+         getCookieId("c4", ".example.org", "/"),
+         getCookieId("uc1", ".example.org", "/"),
+         getCookieId("uc2", ".example.org", "/")
+@@ -73,20 +73,20 @@ add_task(function* () {
+         getCookieId("uc2", ".example.org", "/")
+       ]
+     ],
+   ]);
+ 
+   info("delete all from domain");
+   // delete only cookies that match the host exactly
+   let id = getCookieId("c1", "test1.example.org", "/browser");
+-  yield performDelete(["cookies", "http://test1.example.org"], id, "deleteAllFrom");
++  await performDelete(["cookies", "http://test1.example.org"], id, "deleteAllFrom");
+ 
+   info("test state after delete all from domain");
+-  yield checkState([
++  await checkState([
+     // Domain cookies (.example.org) must not be deleted.
+     [
+       ["cookies", "http://test1.example.org"],
+       [
+         getCookieId("cs2", ".example.org", "/"),
+         getCookieId("c4", ".example.org", "/"),
+         getCookieId("uc1", ".example.org", "/"),
+         getCookieId("uc2", ".example.org", "/")
+@@ -105,21 +105,21 @@ add_task(function* () {
+                     "/browser/devtools/client/storage/test/")
+       ]
+     ],
+   ]);
+ 
+   info("delete all session cookies");
+   // delete only session cookies
+   id = getCookieId("cs2", ".example.org", "/");
+-  yield performDelete(["cookies", "http://sectest1.example.org"], id,
++  await performDelete(["cookies", "http://sectest1.example.org"], id,
+     "deleteAllSessionCookies");
+ 
+   info("test state after delete all session cookies");
+-  yield checkState([
++  await checkState([
+     // Cookies with expiry date must not be deleted.
+     [
+       ["cookies", "http://test1.example.org"],
+       [
+         getCookieId("c4", ".example.org", "/"),
+         getCookieId("uc2", ".example.org", "/")
+       ]
+     ],
+@@ -132,20 +132,20 @@ add_task(function* () {
+         "/browser/devtools/client/storage/test/")
+       ]
+     ],
+   ]);
+ 
+   info("delete all");
+   // delete all cookies for host, including domain cookies
+   id = getCookieId("uc2", ".example.org", "/");
+-  yield performDelete(["cookies", "http://sectest1.example.org"], id, "deleteAll");
++  await performDelete(["cookies", "http://sectest1.example.org"], id, "deleteAll");
+ 
+   info("test state after delete all");
+-  yield checkState([
++  await checkState([
+     // Domain cookies (.example.org) are deleted too, so deleting in sectest1
+     // also removes stuff from test1.
+     [["cookies", "http://test1.example.org"], []],
+     [["cookies", "https://sectest1.example.org"], []],
+   ]);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_cookies_domain.js b/devtools/client/storage/test/browser_storage_cookies_domain.js
+--- a/devtools/client/storage/test/browser_storage_cookies_domain.js
++++ b/devtools/client/storage/test/browser_storage_cookies_domain.js
+@@ -4,26 +4,26 @@
+ 
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
+ "use strict";
+ 
+ // Test that cookies with domain equal to full host name are listed.
+ // E.g., ".example.org" vs. example.org). Bug 1149497.
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org"],
+       [
+         getCookieId("test1", ".test1.example.org", "/browser"),
+         getCookieId("test2", "test1.example.org", "/browser"),
+         getCookieId("test3", ".test1.example.org", "/browser"),
+         getCookieId("test4", "test1.example.org", "/browser"),
+         getCookieId("test5", ".test1.example.org", "/browser")
+       ]
+     ],
+   ]);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_cookies_domain_port.js b/devtools/client/storage/test/browser_storage_cookies_domain_port.js
+--- a/devtools/client/storage/test/browser_storage_cookies_domain_port.js
++++ b/devtools/client/storage/test/browser_storage_cookies_domain_port.js
+@@ -4,26 +4,26 @@
+ 
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
+ "use strict";
+ 
+ // Test that cookies with domain equal to full host name and port are listed.
+ // E.g., ".example.org:8000" vs. example.org:8000).
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN_WITH_PORT + "storage-cookies.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN_WITH_PORT + "storage-cookies.html");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org:8000"],
+       [
+         getCookieId("test1", ".test1.example.org", "/browser"),
+         getCookieId("test2", "test1.example.org", "/browser"),
+         getCookieId("test3", ".test1.example.org", "/browser"),
+         getCookieId("test4", "test1.example.org", "/browser"),
+         getCookieId("test5", ".test1.example.org", "/browser")
+       ]
+     ],
+   ]);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_cookies_edit.js b/devtools/client/storage/test/browser_storage_cookies_edit.js
+--- a/devtools/client/storage/test/browser_storage_cookies_edit.js
++++ b/devtools/client/storage/test/browser_storage_cookies_edit.js
+@@ -1,29 +1,29 @@
+ /* 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/. */
+ 
+ // Basic test to check the editing of cookies.
+ 
+ "use strict";
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
+   showAllColumns(true);
+ 
+   let id = getCookieId("test3", ".test1.example.org", "/browser");
+-  yield editCell(id, "name", "newTest3");
++  await editCell(id, "name", "newTest3");
+ 
+   id = getCookieId("newTest3", ".test1.example.org", "/browser");
+-  yield editCell(id, "host", "test1.example.org");
++  await editCell(id, "host", "test1.example.org");
+ 
+   id = getCookieId("newTest3", "test1.example.org", "/browser");
+-  yield editCell(id, "path", "/");
++  await editCell(id, "path", "/");
+ 
+   id = getCookieId("newTest3", "test1.example.org", "/");
+-  yield editCell(id, "expires", "Tue, 14 Feb 2040 17:41:14 GMT");
+-  yield editCell(id, "value", "newValue3");
+-  yield editCell(id, "isSecure", "true");
+-  yield editCell(id, "isHttpOnly", "true");
++  await editCell(id, "expires", "Tue, 14 Feb 2040 17:41:14 GMT");
++  await editCell(id, "value", "newValue3");
++  await editCell(id, "isSecure", "true");
++  await editCell(id, "isHttpOnly", "true");
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_cookies_edit_keyboard.js b/devtools/client/storage/test/browser_storage_cookies_edit_keyboard.js
+--- a/devtools/client/storage/test/browser_storage_cookies_edit_keyboard.js
++++ b/devtools/client/storage/test/browser_storage_cookies_edit_keyboard.js
+@@ -1,25 +1,25 @@
+ /* 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/. */
+ 
+ // Basic test to check the editing of cookies with the keyboard.
+ 
+ "use strict";
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
+   showAllColumns(true);
+   showColumn("uniqueKey", false);
+ 
+   let id = getCookieId("test4", "test1.example.org", "/browser");
+-  yield startCellEdit(id, "name");
+-  yield typeWithTerminator("test6", "KEY_Tab");
+-  yield typeWithTerminator(".example.org", "KEY_Tab");
+-  yield typeWithTerminator("/", "KEY_Tab");
+-  yield typeWithTerminator("Tue, 25 Dec 2040 12:00:00 GMT", "KEY_Tab");
+-  yield typeWithTerminator("test6value", "KEY_Tab");
+-  yield typeWithTerminator("false", "KEY_Tab");
+-  yield typeWithTerminator("false", "KEY_Tab");
++  await startCellEdit(id, "name");
++  await typeWithTerminator("test6", "KEY_Tab");
++  await typeWithTerminator(".example.org", "KEY_Tab");
++  await typeWithTerminator("/", "KEY_Tab");
++  await typeWithTerminator("Tue, 25 Dec 2040 12:00:00 GMT", "KEY_Tab");
++  await typeWithTerminator("test6value", "KEY_Tab");
++  await typeWithTerminator("false", "KEY_Tab");
++  await typeWithTerminator("false", "KEY_Tab");
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_cookies_samesite.js b/devtools/client/storage/test/browser_storage_cookies_samesite.js
+--- a/devtools/client/storage/test/browser_storage_cookies_samesite.js
++++ b/devtools/client/storage/test/browser_storage_cookies_samesite.js
+@@ -3,35 +3,35 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
+ "use strict";
+ 
+ // Test that the samesite cookie attribute is displayed correctly.
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies-samesite.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies-samesite.html");
+ 
+   let id1 = getCookieId("test1", "test1.example.org",
+                         "/browser/devtools/client/storage/test/");
+   let id2 = getCookieId("test2", "test1.example.org",
+                         "/browser/devtools/client/storage/test/");
+   let id3 = getCookieId("test3", "test1.example.org",
+                         "/browser/devtools/client/storage/test/");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org"],
+       [ id1, id2, id3 ]
+     ]
+   ]);
+ 
+   let sameSite1 = getRowValues(id1).sameSite;
+   let sameSite2 = getRowValues(id2).sameSite;
+   let sameSite3 = getRowValues(id3).sameSite;
+ 
+   is(sameSite1, "Unset", `sameSite1 is "Unset"`);
+   is(sameSite2, "Lax", `sameSite2 is "Lax"`);
+   is(sameSite3, "Strict", `sameSite3 is "Strict"`);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_cookies_tab_navigation.js b/devtools/client/storage/test/browser_storage_cookies_tab_navigation.js
+--- a/devtools/client/storage/test/browser_storage_cookies_tab_navigation.js
++++ b/devtools/client/storage/test/browser_storage_cookies_tab_navigation.js
+@@ -1,25 +1,25 @@
+ /* 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/. */
+ 
+ // Basic test to check cookie table tab navigation.
+ 
+ "use strict";
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
+   showAllColumns(true);
+ 
+   let id = getCookieId("test1", ".test1.example.org", "/browser");
+-  yield startCellEdit(id, "name");
++  await startCellEdit(id, "name");
+ 
+   PressKeyXTimes("VK_TAB", 18);
+   is(getCurrentEditorValue(), "value3",
+      "We have tabbed to the correct cell.");
+ 
+   PressKeyXTimes("VK_TAB", 18, {shiftKey: true});
+   is(getCurrentEditorValue(), "test1",
+      "We have shift-tabbed to the correct cell.");
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_delete.js b/devtools/client/storage/test/browser_storage_delete.js
+--- a/devtools/client/storage/test/browser_storage_delete.js
++++ b/devtools/client/storage/test/browser_storage_delete.js
+@@ -18,41 +18,41 @@ const TEST_CASES = [
+     getCookieId("c1", "test1.example.org", "/browser"), "name"
+   ],
+   [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
+    1, "name"],
+   [["Cache", "http://test1.example.org", "plop"],
+    MAIN_DOMAIN + "404_cached_file.js", "url"],
+ ];
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
+ 
+   let contextMenu = gPanelWindow.document.getElementById("storage-table-popup");
+   let menuDeleteItem = contextMenu.querySelector("#storage-table-popup-delete");
+ 
+   for (let [ treeItem, rowName, cellToClick] of TEST_CASES) {
+     let treeItemName = treeItem.join(" > ");
+ 
+     info(`Selecting tree item ${treeItemName}`);
+-    yield selectTreeItem(treeItem);
++    await selectTreeItem(treeItem);
+ 
+     let row = getRowCells(rowName);
+     ok(gUI.table.items.has(rowName), `There is a row '${rowName}' in ${treeItemName}`);
+ 
+     let eventWait = gUI.once("store-objects-edit");
+ 
+-    yield waitForContextMenu(contextMenu, row[cellToClick], () => {
++    await waitForContextMenu(contextMenu, row[cellToClick], () => {
+       info(`Opened context menu in ${treeItemName}, row '${rowName}'`);
+       menuDeleteItem.click();
+       let truncatedRowName = String(rowName).replace(SEPARATOR_GUID, "-").substr(0, 16);
+       ok(menuDeleteItem.getAttribute("label").includes(truncatedRowName),
+         `Context menu item label contains '${rowName}' (maybe truncated)`);
+     });
+ 
+-    yield eventWait;
++    await eventWait;
+ 
+     ok(!gUI.table.items.has(rowName),
+       `There is no row '${rowName}' in ${treeItemName} after deletion`);
+   }
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_delete_all.js b/devtools/client/storage/test/browser_storage_delete_all.js
+--- a/devtools/client/storage/test/browser_storage_delete_all.js
++++ b/devtools/client/storage/test/browser_storage_delete_all.js
+@@ -3,18 +3,18 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
+ "use strict";
+ 
+ // Test deleting all storage items
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
+ 
+   let contextMenu = gPanelWindow.document.getElementById("storage-table-popup");
+   let menuDeleteAllItem = contextMenu.querySelector(
+     "#storage-table-popup-delete-all");
+ 
+   info("test state before delete");
+   const beforeState = [
+     [["localStorage", "http://test1.example.org"],
+@@ -30,41 +30,41 @@ add_task(function* () {
+     [["sessionStorage", "https://sectest1.example.org"],
+       ["iframe-s-ss1"]],
+     [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
+       [1, 2, 3]],
+     [["Cache", "http://test1.example.org", "plop"],
+       [MAIN_DOMAIN + "404_cached_file.js", MAIN_DOMAIN + "browser_storage_basic.js"]],
+   ];
+ 
+-  yield checkState(beforeState);
++  await checkState(beforeState);
+ 
+   info("do the delete");
+   const deleteHosts = [
+     [["localStorage", "https://sectest1.example.org"], "iframe-s-ls1", "name"],
+     [["sessionStorage", "https://sectest1.example.org"], "iframe-s-ss1", "name"],
+     [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], 1, "name"],
+     [["Cache", "http://test1.example.org", "plop"],
+      MAIN_DOMAIN + "404_cached_file.js", "url"],
+   ];
+ 
+   for (let [store, rowName, cellToClick] of deleteHosts) {
+     let storeName = store.join(" > ");
+ 
+-    yield selectTreeItem(store);
++    await selectTreeItem(store);
+ 
+     let eventWait = gUI.once("store-objects-cleared");
+ 
+     let cell = getRowCells(rowName)[cellToClick];
+-    yield waitForContextMenu(contextMenu, cell, () => {
++    await waitForContextMenu(contextMenu, cell, () => {
+       info(`Opened context menu in ${storeName}, row '${rowName}'`);
+       menuDeleteAllItem.click();
+     });
+ 
+-    yield eventWait;
++    await eventWait;
+   }
+ 
+   info("test state after delete");
+   const afterState = [
+     // iframes from the same host, one secure, one unsecure, are independent
+     // from each other. Delete all in one doesn't touch the other one.
+     [["localStorage", "http://test1.example.org"],
+       ["ls1", "ls2"]],
+@@ -79,12 +79,12 @@ add_task(function* () {
+     [["sessionStorage", "https://sectest1.example.org"],
+       []],
+     [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
+       []],
+     [["Cache", "http://test1.example.org", "plop"],
+       []],
+   ];
+ 
+-  yield checkState(afterState);
++  await checkState(afterState);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_delete_tree.js b/devtools/client/storage/test/browser_storage_delete_tree.js
+--- a/devtools/client/storage/test/browser_storage_delete_tree.js
++++ b/devtools/client/storage/test/browser_storage_delete_tree.js
+@@ -3,25 +3,25 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
+ "use strict";
+ 
+ // Test deleting all storage items from the tree.
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
+ 
+   let contextMenu = gPanelWindow.document.getElementById("storage-tree-popup");
+   let menuDeleteAllItem = contextMenu.querySelector(
+     "#storage-tree-popup-delete-all");
+ 
+   info("test state before delete");
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org"],
+       [
+         getCookieId("c1", "test1.example.org", "/browser"),
+         getCookieId("cs2", ".example.org", "/"),
+         getCookieId("c3", "test1.example.org", "/"),
+         getCookieId("c4", ".example.org", "/"),
+         getCookieId("uc1", ".example.org", "/"),
+@@ -42,36 +42,36 @@ add_task(function* () {
+     ["sessionStorage", "http://test1.example.org"],
+     ["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
+     ["Cache", "http://test1.example.org", "plop"],
+   ];
+ 
+   for (let store of deleteHosts) {
+     let storeName = store.join(" > ");
+ 
+-    yield selectTreeItem(store);
++    await selectTreeItem(store);
+ 
+     let eventName = "store-objects-" +
+       (store[0] == "cookies" ? "edit" : "cleared");
+     let eventWait = gUI.once(eventName);
+ 
+     let selector = `[data-id='${JSON.stringify(store)}'] > .tree-widget-item`;
+     let target = gPanelWindow.document.querySelector(selector);
+     ok(target, `tree item found in ${storeName}`);
+-    yield waitForContextMenu(contextMenu, target, () => {
++    await waitForContextMenu(contextMenu, target, () => {
+       info(`Opened tree context menu in ${storeName}`);
+       menuDeleteAllItem.click();
+     });
+ 
+-    yield eventWait;
++    await eventWait;
+   }
+ 
+   info("test state after delete");
+-  yield checkState([
++  await checkState([
+     [["cookies", "http://test1.example.org"], []],
+     [["localStorage", "http://test1.example.org"], []],
+     [["sessionStorage", "http://test1.example.org"], []],
+     [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], []],
+     [["Cache", "http://test1.example.org", "plop"], []],
+   ]);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_delete_usercontextid.js b/devtools/client/storage/test/browser_storage_delete_usercontextid.js
+--- a/devtools/client/storage/test/browser_storage_delete_usercontextid.js
++++ b/devtools/client/storage/test/browser_storage_delete_usercontextid.js
+@@ -100,82 +100,82 @@ function testTree(tests) {
+     ok(doc.querySelector("[data-id='" + JSON.stringify(item) + "']"),
+        "Tree item " + item[0] + " should be present in the storage tree");
+   }
+ }
+ 
+ /**
+  * Test that correct table entries are shown for each of the tree item
+  */
+-function* testTables(tests) {
++async function testTables(tests) {
+   let doc = gPanelWindow.document;
+   // Expand all nodes so that the synthesized click event actually works
+   gUI.tree.expandAll();
+ 
+   // First tree item is already selected so no clicking and waiting for update
+   for (let id of tests[0][1]) {
+     ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+        "Table item " + id + " should be present");
+   }
+ 
+   // Click rest of the tree items and wait for the table to be updated
+   for (let [treeItem, items] of tests.slice(1)) {
+-    yield selectTreeItem(treeItem);
++    await selectTreeItem(treeItem);
+ 
+     // Check whether correct number of items are present in the table
+     is(doc.querySelectorAll(
+          ".table-widget-wrapper:first-of-type .table-widget-cell"
+        ).length, items.length, "Number of items in table is correct");
+ 
+     // Check if all the desired items are present in the table
+     for (let id of items) {
+       ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+          "Table item " + id + " should be present");
+     }
+   }
+ }
+ 
+-add_task(function* () {
++add_task(async function() {
+   // First, open a tab with the default userContextId and setup its storages.
+-  let tabDefault = yield openTab(MAIN_DOMAIN + "storage-listings.html");
++  let tabDefault = await openTab(MAIN_DOMAIN + "storage-listings.html");
+ 
+   // Second, start testing for userContextId 1.
+   // We use the same item name as the default page has to see deleting items
+   // from userContextId 1 will affect default one or not.
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html", {userContextId: 1});
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html", {userContextId: 1});
+ 
+   let contextMenu = gPanelWindow.document.getElementById("storage-table-popup");
+   let menuDeleteItem = contextMenu.querySelector("#storage-table-popup-delete");
+ 
+   for (let [ treeItem, rowName, cellToClick] of TEST_CASES) {
+     let treeItemName = treeItem.join(" > ");
+ 
+     info(`Selecting tree item ${treeItemName}`);
+-    yield selectTreeItem(treeItem);
++    await selectTreeItem(treeItem);
+ 
+     let row = getRowCells(rowName);
+     ok(gUI.table.items.has(rowName), `There is a row '${rowName}' in ${treeItemName}`);
+ 
+     let eventWait = gUI.once("store-objects-edit");
+ 
+-    yield waitForContextMenu(contextMenu, row[cellToClick], () => {
++    await waitForContextMenu(contextMenu, row[cellToClick], () => {
+       info(`Opened context menu in ${treeItemName}, row '${rowName}'`);
+       menuDeleteItem.click();
+       let truncatedRowName = String(rowName).replace(SEPARATOR_GUID, "-").substr(0, 16);
+       ok(menuDeleteItem.getAttribute("label").includes(truncatedRowName),
+         `Context menu item label contains '${rowName}' (maybe truncated)`);
+     });
+ 
+-    yield eventWait;
++    await eventWait;
+ 
+     ok(!gUI.table.items.has(rowName),
+       `There is no row '${rowName}' in ${treeItemName} after deletion`);
+   }
+ 
+   // Final, we see that the default tab is intact or not.
+-  yield BrowserTestUtils.switchTab(gBrowser, tabDefault);
+-  yield openStoragePanel();
++  await BrowserTestUtils.switchTab(gBrowser, tabDefault);
++  await openStoragePanel();
+ 
+   testTree(storageItemsForDefault);
+-  yield testTables(storageItemsForDefault);
++  await testTables(storageItemsForDefault);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_dom_cache_disabled.js b/devtools/client/storage/test/browser_storage_dom_cache_disabled.js
+--- a/devtools/client/storage/test/browser_storage_dom_cache_disabled.js
++++ b/devtools/client/storage/test/browser_storage_dom_cache_disabled.js
+@@ -3,21 +3,21 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
+ "use strict";
+ 
+ // Test the storage inspector when dom.caches.enabled=false.
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Disable the DOM cache
+   Services.prefs.setBoolPref(DOM_CACHE, false);
+ 
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
+ 
+   const state = [
+     [["localStorage", "http://test1.example.org"],
+       ["ls1", "ls2"]],
+     [["localStorage", "http://sectest1.example.org"],
+       ["iframe-u-ls1"]],
+     [["localStorage", "https://sectest1.example.org"],
+       ["iframe-s-ls1"]],
+@@ -26,12 +26,12 @@ add_task(function* () {
+     [["sessionStorage", "http://sectest1.example.org"],
+       ["iframe-u-ss1", "iframe-u-ss2"]],
+     [["sessionStorage", "https://sectest1.example.org"],
+       ["iframe-s-ss1"]],
+     [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
+       [1, 2, 3]],
+   ];
+ 
+-  yield checkState(state);
++  await checkState(state);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_dynamic_updates_cookies.js b/devtools/client/storage/test/browser_storage_dynamic_updates_cookies.js
+--- a/devtools/client/storage/test/browser_storage_dynamic_updates_cookies.js
++++ b/devtools/client/storage/test/browser_storage_dynamic_updates_cookies.js
+@@ -1,24 +1,24 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ // Test dynamic updates in the storage inspector for cookies.
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
+ 
+   gUI.tree.expandAll();
+ 
+   ok(gUI.sidebar.hidden, "Sidebar is initially hidden");
+   let c1id = getCookieId("c1", "test1.example.org", "/browser");
+-  yield selectTableItem(c1id);
++  await selectTableItem(c1id);
+ 
+   // test that value is something initially
+   let initialValue = [[
+     {name: "c1", value: "1.2.3.4.5.6.7"},
+     {name: "c1.Path", value: "/browser"}
+   ], [
+     {name: "c1", value: "Array"},
+     {name: "c1.0", value: "1"},
+@@ -31,75 +31,75 @@ add_task(function* () {
+     {name: "c1.Path", value: "/browser"}
+   ], [
+     {name: "c1", value: "Object"},
+     {name: "c1.foo", value: "4"},
+     {name: "c1.bar", value: "6"}
+   ]];
+ 
+   // Check that sidebar shows correct initial value
+-  yield findVariableViewProperties(initialValue[0], false);
++  await findVariableViewProperties(initialValue[0], false);
+ 
+-  yield findVariableViewProperties(initialValue[1], true);
++  await findVariableViewProperties(initialValue[1], true);
+ 
+   // Check if table shows correct initial value
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org"],
+       [
+         getCookieId("c1", "test1.example.org", "/browser"),
+         getCookieId("c2", "test1.example.org", "/browser")
+       ]
+     ],
+   ]);
+   checkCell(c1id, "value", "1.2.3.4.5.6.7");
+ 
+-  yield addCookie("c1", '{"foo": 4,"bar":6}', "/browser");
+-  yield gUI.once("store-objects-edit");
++  await addCookie("c1", '{"foo": 4,"bar":6}', "/browser");
++  await gUI.once("store-objects-edit");
+ 
+-  yield findVariableViewProperties(finalValue[0], false);
+-  yield findVariableViewProperties(finalValue[1], true);
++  await findVariableViewProperties(finalValue[0], false);
++  await findVariableViewProperties(finalValue[1], true);
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org"],
+       [
+         getCookieId("c1", "test1.example.org", "/browser"),
+         getCookieId("c2", "test1.example.org", "/browser")
+       ]
+     ],
+   ]);
+   checkCell(c1id, "value", '{"foo": 4,"bar":6}');
+ 
+   // Add a new entry
+-  yield addCookie("c3", "booyeah");
++  await addCookie("c3", "booyeah");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org"],
+       [
+         getCookieId("c1", "test1.example.org", "/browser"),
+         getCookieId("c2", "test1.example.org", "/browser"),
+         getCookieId("c3", "test1.example.org",
+                     "/browser/devtools/client/storage/test/")
+       ]
+     ],
+   ]);
+   let c3id = getCookieId("c3", "test1.example.org",
+                          "/browser/devtools/client/storage/test/");
+   checkCell(c3id, "value", "booyeah");
+ 
+   // Add another
+-  yield addCookie("c4", "booyeah");
++  await addCookie("c4", "booyeah");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org"],
+       [
+         getCookieId("c1", "test1.example.org", "/browser"),
+         getCookieId("c2", "test1.example.org", "/browser"),
+         getCookieId("c3", "test1.example.org",
+                     "/browser/devtools/client/storage/test/"),
+         getCookieId("c4", "test1.example.org",
+@@ -107,94 +107,94 @@ add_task(function* () {
+       ]
+     ],
+   ]);
+   let c4id = getCookieId("c4", "test1.example.org",
+                          "/browser/devtools/client/storage/test/");
+   checkCell(c4id, "value", "booyeah");
+ 
+   // Removing cookies
+-  yield removeCookie("c1", "/browser");
++  await removeCookie("c1", "/browser");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org"],
+       [
+         getCookieId("c2", "test1.example.org", "/browser"),
+         getCookieId("c3", "test1.example.org",
+                     "/browser/devtools/client/storage/test/"),
+         getCookieId("c4", "test1.example.org",
+                     "/browser/devtools/client/storage/test/")
+       ]
+     ],
+   ]);
+ 
+   ok(!gUI.sidebar.hidden, "Sidebar still visible for next row");
+ 
+   // Check if next element's value is visible in sidebar
+-  yield findVariableViewProperties([{name: "c2", value: "foobar"}]);
++  await findVariableViewProperties([{name: "c2", value: "foobar"}]);
+ 
+   // Keep deleting till no rows
+-  yield removeCookie("c3");
++  await removeCookie("c3");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org"],
+       [
+         getCookieId("c2", "test1.example.org", "/browser"),
+         getCookieId("c4", "test1.example.org",
+                     "/browser/devtools/client/storage/test/")
+       ]
+     ],
+   ]);
+ 
+   // Check if next element's value is visible in sidebar
+-  yield findVariableViewProperties([{name: "c2", value: "foobar"}]);
++  await findVariableViewProperties([{name: "c2", value: "foobar"}]);
+ 
+-  yield removeCookie("c2", "/browser");
++  await removeCookie("c2", "/browser");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["cookies", "http://test1.example.org"],
+       [
+         getCookieId("c4", "test1.example.org",
+                     "/browser/devtools/client/storage/test/")
+       ]
+     ],
+   ]);
+ 
+   // Check if next element's value is visible in sidebar
+-  yield findVariableViewProperties([{name: "c4", value: "booyeah"}]);
++  await findVariableViewProperties([{name: "c4", value: "booyeah"}]);
+ 
+-  yield removeCookie("c4");
++  await removeCookie("c4");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+-  yield checkState([
++  await checkState([
+     [["cookies", "http://test1.example.org"], [ ]],
+   ]);
+ 
+   ok(gUI.sidebar.hidden, "Sidebar is hidden when no rows");
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+ 
+-function* addCookie(name, value, path) {
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, [name, value, path],
++async function addCookie(name, value, path) {
++  await ContentTask.spawn(gBrowser.selectedBrowser, [name, value, path],
+     ([nam, valu, pat]) => {
+       content.wrappedJSObject.addCookie(nam, valu, pat);
+     }
+   );
+ }
+ 
+-function* removeCookie(name, path) {
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, [name, path],
++async function removeCookie(name, path) {
++  await ContentTask.spawn(gBrowser.selectedBrowser, [name, path],
+     ([nam, pat]) => {
+       content.wrappedJSObject.removeCookie(nam, pat);
+     }
+   );
+ }
+diff --git a/devtools/client/storage/test/browser_storage_dynamic_updates_localStorage.js b/devtools/client/storage/test/browser_storage_dynamic_updates_localStorage.js
+--- a/devtools/client/storage/test/browser_storage_dynamic_updates_localStorage.js
++++ b/devtools/client/storage/test/browser_storage_dynamic_updates_localStorage.js
+@@ -1,82 +1,82 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ // Test dynamic updates in the storage inspector for localStorage.
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
+ 
+   gUI.tree.expandAll();
+ 
+   ok(gUI.sidebar.hidden, "Sidebar is initially hidden");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["localStorage", "http://test1.example.org"],
+       ["ls1", "ls2", "ls3", "ls4", "ls5", "ls6", "ls7"]
+     ],
+   ]);
+ 
+-  yield removeLocalStorageItem("ls4");
++  await removeLocalStorageItem("ls4");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["localStorage", "http://test1.example.org"],
+       ["ls1", "ls2", "ls3", "ls5", "ls6", "ls7"]
+     ],
+   ]);
+ 
+-  yield setLocalStorageItem("ls4", "again");
++  await setLocalStorageItem("ls4", "again");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["localStorage", "http://test1.example.org"],
+       ["ls1", "ls2", "ls3", "ls4", "ls5", "ls6", "ls7"]
+     ],
+   ]);
+   // Updating a row
+-  yield setLocalStorageItem("ls2", "ls2-changed");
++  await setLocalStorageItem("ls2", "ls2-changed");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+   checkCell("ls2", "value", "ls2-changed");
+ 
+   // Clearing items.
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
++  await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+     content.wrappedJSObject.clear();
+   });
+ 
+-  yield gUI.once("store-objects-cleared");
++  await gUI.once("store-objects-cleared");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["localStorage", "http://test1.example.org"],
+       [ ]
+     ],
+   ]);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+ 
+-function* setLocalStorageItem(key, value) {
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, [key, value],
++async function setLocalStorageItem(key, value) {
++  await ContentTask.spawn(gBrowser.selectedBrowser, [key, value],
+     ([innerKey, innerValue]) => {
+       content.wrappedJSObject.localStorage.setItem(innerKey, innerValue);
+     }
+   );
+ }
+ 
+-function* removeLocalStorageItem(key) {
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, key,
++async function removeLocalStorageItem(key) {
++  await ContentTask.spawn(gBrowser.selectedBrowser, key,
+     innerKey => {
+       content.wrappedJSObject.localStorage.removeItem(innerKey);
+     }
+   );
+ }
+diff --git a/devtools/client/storage/test/browser_storage_dynamic_updates_sessionStorage.js b/devtools/client/storage/test/browser_storage_dynamic_updates_sessionStorage.js
+--- a/devtools/client/storage/test/browser_storage_dynamic_updates_sessionStorage.js
++++ b/devtools/client/storage/test/browser_storage_dynamic_updates_sessionStorage.js
+@@ -1,97 +1,97 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ // Test dynamic updates in the storage inspector for sessionStorage.
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
+ 
+   gUI.tree.expandAll();
+ 
+   ok(gUI.sidebar.hidden, "Sidebar is initially hidden");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["sessionStorage", "http://test1.example.org"],
+       ["ss1", "ss2", "ss3"]
+     ],
+   ]);
+ 
+-  yield setSessionStorageItem("ss4", "new-item");
++  await setSessionStorageItem("ss4", "new-item");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["sessionStorage", "http://test1.example.org"],
+       ["ss1", "ss2", "ss3", "ss4"]
+     ],
+   ]);
+ 
+   // deleting item
+ 
+-  yield removeSessionStorageItem("ss3");
++  await removeSessionStorageItem("ss3");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+-  yield removeSessionStorageItem("ss1");
++  await removeSessionStorageItem("ss1");
+ 
+-  yield gUI.once("store-objects-edit");
++  await gUI.once("store-objects-edit");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["sessionStorage", "http://test1.example.org"],
+       ["ss2", "ss4"]
+     ],
+   ]);
+ 
+-  yield selectTableItem("ss2");
++  await selectTableItem("ss2");
+ 
+   ok(!gUI.sidebar.hidden, "sidebar is visible");
+ 
+   // Checking for correct value in sidebar before update
+-  yield findVariableViewProperties([{name: "ss2", value: "foobar"}]);
++  await findVariableViewProperties([{name: "ss2", value: "foobar"}]);
+ 
+-  yield setSessionStorageItem("ss2", "changed=ss2");
++  await setSessionStorageItem("ss2", "changed=ss2");
+ 
+-  yield gUI.once("sidebar-updated");
++  await gUI.once("sidebar-updated");
+ 
+   checkCell("ss2", "value", "changed=ss2");
+ 
+-  yield findVariableViewProperties([{name: "ss2", value: "changed=ss2"}]);
++  await findVariableViewProperties([{name: "ss2", value: "changed=ss2"}]);
+ 
+   // Clearing items.
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
++  await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+     content.wrappedJSObject.clear();
+   });
+ 
+-  yield gUI.once("store-objects-cleared");
++  await gUI.once("store-objects-cleared");
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["sessionStorage", "http://test1.example.org"],
+       [ ]
+     ],
+   ]);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+ 
+-function* setSessionStorageItem(key, value) {
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, [key, value],
++async function setSessionStorageItem(key, value) {
++  await ContentTask.spawn(gBrowser.selectedBrowser, [key, value],
+     ([innerKey, innerValue]) => {
+       content.wrappedJSObject.sessionStorage.setItem(innerKey, innerValue);
+     }
+   );
+ }
+ 
+-function* removeSessionStorageItem(key) {
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, key,
++async function removeSessionStorageItem(key) {
++  await ContentTask.spawn(gBrowser.selectedBrowser, key,
+     innerKey => {
+       content.wrappedJSObject.sessionStorage.removeItem(innerKey);
+     }
+   );
+ }
+diff --git a/devtools/client/storage/test/browser_storage_empty_objectstores.js b/devtools/client/storage/test/browser_storage_empty_objectstores.js
+--- a/devtools/client/storage/test/browser_storage_empty_objectstores.js
++++ b/devtools/client/storage/test/browser_storage_empty_objectstores.js
+@@ -41,37 +41,37 @@ function testTree() {
+     ok(doc.querySelector(`[data-id='${JSON.stringify(item)}']`),
+       `Tree item ${item} should be present in the storage tree`);
+   }
+ }
+ 
+ /**
+  * Test that correct table entries are shown for each of the tree item
+  */
+-let testTables = function* () {
++let testTables = async function() {
+   let doc = gPanelWindow.document;
+   // Expand all nodes so that the synthesized click event actually works
+   gUI.tree.expandAll();
+ 
+   // Click the tree items and wait for the table to be updated
+   for (let [item, ids] of storeItems) {
+-    yield selectTreeItem(item);
++    await selectTreeItem(item);
+ 
+     // Check whether correct number of items are present in the table
+     is(doc.querySelectorAll(
+          ".table-widget-wrapper:first-of-type .table-widget-cell"
+        ).length, ids.length, "Number of items in table is correct");
+ 
+     // Check if all the desired items are present in the table
+     for (let id of ids) {
+       ok(doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
+         `Table item ${id} should be present`);
+     }
+   }
+ };
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-empty-objectstores.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-empty-objectstores.html");
+ 
+   testTree();
+-  yield testTables();
+-  yield finishTests();
++  await testTables();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_indexeddb_delete.js b/devtools/client/storage/test/browser_storage_indexeddb_delete.js
+--- a/devtools/client/storage/test/browser_storage_indexeddb_delete.js
++++ b/devtools/client/storage/test/browser_storage_indexeddb_delete.js
+@@ -3,44 +3,44 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
+ "use strict";
+ 
+ // Test deleting indexedDB database from the tree using context menu
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-empty-objectstores.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-empty-objectstores.html");
+ 
+   let contextMenu = gPanelWindow.document.getElementById("storage-tree-popup");
+   let menuDeleteDb = contextMenu.querySelector("#storage-tree-popup-delete");
+ 
+   info("test state before delete");
+-  yield checkState([
++  await checkState([
+     [["indexedDB", "http://test1.example.org"], ["idb1 (default)", "idb2 (default)"]],
+   ]);
+ 
+   info("do the delete");
+   const deletedDb = ["indexedDB", "http://test1.example.org", "idb1 (default)"];
+ 
+-  yield selectTreeItem(deletedDb);
++  await selectTreeItem(deletedDb);
+ 
+   // Wait once for update and another time for value fetching
+   let eventWait = gUI.once("store-objects-updated");
+ 
+   let selector = `[data-id='${JSON.stringify(deletedDb)}'] > .tree-widget-item`;
+   let target = gPanelWindow.document.querySelector(selector);
+   ok(target, `tree item found in ${deletedDb.join(" > ")}`);
+-  yield waitForContextMenu(contextMenu, target, () => {
++  await waitForContextMenu(contextMenu, target, () => {
+     info(`Opened tree context menu in ${deletedDb.join(" > ")}`);
+     menuDeleteDb.click();
+   });
+ 
+-  yield eventWait;
++  await eventWait;
+ 
+   info("test state after delete");
+-  yield checkState([
++  await checkState([
+     [["indexedDB", "http://test1.example.org"], ["idb2 (default)"]],
+   ]);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_indexeddb_delete_blocked.js b/devtools/client/storage/test/browser_storage_indexeddb_delete_blocked.js
+--- a/devtools/client/storage/test/browser_storage_indexeddb_delete_blocked.js
++++ b/devtools/client/storage/test/browser_storage_indexeddb_delete_blocked.js
+@@ -3,56 +3,56 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
+ "use strict";
+ 
+ // Test what happens when deleting indexedDB database is blocked
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-idb-delete-blocked.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-idb-delete-blocked.html");
+ 
+   info("test state before delete");
+-  yield checkState([
++  await checkState([
+     [["indexedDB", "http://test1.example.org"], ["idb (default)"]]
+   ]);
+ 
+   info("do the delete");
+-  yield selectTreeItem(["indexedDB", "http://test1.example.org"]);
++  await selectTreeItem(["indexedDB", "http://test1.example.org"]);
+   let front = gUI.getCurrentFront();
+-  let result = yield front.removeDatabase("http://test1.example.org", "idb (default)");
++  let result = await front.removeDatabase("http://test1.example.org", "idb (default)");
+ 
+   ok(result.blocked, "removeDatabase attempt is blocked");
+ 
+   info("test state after blocked delete");
+-  yield checkState([
++  await checkState([
+     [["indexedDB", "http://test1.example.org"], ["idb (default)"]]
+   ]);
+ 
+   let eventWait = gUI.once("store-objects-edit");
+ 
+   info("telling content to close the db");
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
++  await ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
+     let win = content.wrappedJSObject;
+-    yield win.closeDb();
++    await win.closeDb();
+   });
+ 
+   info("waiting for store edit events");
+-  yield eventWait;
++  await eventWait;
+ 
+   info("test state after real delete");
+-  yield checkState([
++  await checkState([
+     [["indexedDB", "http://test1.example.org"], []]
+   ]);
+ 
+   info("try to delete database from nonexistent host");
+   let errorThrown = false;
+   try {
+-    result = yield front.removeDatabase("http://test2.example.org", "idb (default)");
++    result = await front.removeDatabase("http://test2.example.org", "idb (default)");
+   } catch (ex) {
+     errorThrown = true;
+   }
+ 
+   ok(errorThrown, "error was reported when trying to delete");
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_indexeddb_duplicate_names.js b/devtools/client/storage/test/browser_storage_indexeddb_duplicate_names.js
+--- a/devtools/client/storage/test/browser_storage_indexeddb_duplicate_names.js
++++ b/devtools/client/storage/test/browser_storage_indexeddb_duplicate_names.js
+@@ -2,30 +2,30 @@
+  * 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/. */
+ 
+ // Test to verify that indexedDBs with duplicate names (different types / paths)
+ // work as expected.
+ 
+ "use strict";
+ 
+-add_task(function* () {
++add_task(async function() {
+   const TESTPAGE = MAIN_DOMAIN + "storage-indexeddb-duplicate-names.html";
+ 
+   setPermission(TESTPAGE, "indexedDB");
+ 
+-  yield openTabAndSetupStorage(TESTPAGE);
++  await openTabAndSetupStorage(TESTPAGE);
+ 
+-  yield checkState([
++  await checkState([
+     [
+       ["indexedDB", "http://test1.example.org"], [
+         "idb1 (default)",
+         "idb1 (temporary)",
+         "idb1 (persistent)",
+         "idb2 (default)",
+         "idb2 (temporary)",
+         "idb2 (persistent)"
+       ]
+     ]
+   ]);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_localstorage_add.js b/devtools/client/storage/test/browser_storage_localstorage_add.js
+--- a/devtools/client/storage/test/browser_storage_localstorage_add.js
++++ b/devtools/client/storage/test/browser_storage_localstorage_add.js
+@@ -1,20 +1,20 @@
+ /* 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/. */
+ 
+ // Basic test to check the adding of localStorage entries.
+ 
+ "use strict";
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-localstorage.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-localstorage.html");
+   showAllColumns(true);
+ 
+-  yield performAdd(["localStorage", "http://test1.example.org"]);
+-  yield performAdd(["localStorage", "http://test1.example.org"]);
+-  yield performAdd(["localStorage", "http://test1.example.org"]);
+-  yield performAdd(["localStorage", "http://test1.example.org"]);
+-  yield performAdd(["localStorage", "http://test1.example.org"]);
++  await performAdd(["localStorage", "http://test1.example.org"]);
++  await performAdd(["localStorage", "http://test1.example.org"]);
++  await performAdd(["localStorage", "http://test1.example.org"]);
++  await performAdd(["localStorage", "http://test1.example.org"]);
++  await performAdd(["localStorage", "http://test1.example.org"]);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_localstorage_edit.js b/devtools/client/storage/test/browser_storage_localstorage_edit.js
+--- a/devtools/client/storage/test/browser_storage_localstorage_edit.js
++++ b/devtools/client/storage/test/browser_storage_localstorage_edit.js
+@@ -1,24 +1,24 @@
+ /* 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/. */
+ 
+ // Basic test to check the editing of localStorage.
+ 
+ "use strict";
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-localstorage.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-localstorage.html");
+ 
+-  yield selectTreeItem(["localStorage", "http://test1.example.org"]);
++  await selectTreeItem(["localStorage", "http://test1.example.org"]);
+ 
+-  yield editCell("TestLS1", "name", "newTestLS1");
+-  yield editCell("newTestLS1", "value", "newValueLS1");
++  await editCell("TestLS1", "name", "newTestLS1");
++  await editCell("newTestLS1", "value", "newValueLS1");
+ 
+-  yield editCell("TestLS3", "name", "newTestLS3");
+-  yield editCell("newTestLS3", "value", "newValueLS3");
++  await editCell("TestLS3", "name", "newTestLS3");
++  await editCell("newTestLS3", "value", "newValueLS3");
+ 
+-  yield editCell("TestLS5", "name", "newTestLS5");
+-  yield editCell("newTestLS5", "value", "newValueLS5");
++  await editCell("TestLS5", "name", "newTestLS5");
++  await editCell("newTestLS5", "value", "newValueLS5");
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_localstorage_error.js b/devtools/client/storage/test/browser_storage_localstorage_error.js
+--- a/devtools/client/storage/test/browser_storage_localstorage_error.js
++++ b/devtools/client/storage/test/browser_storage_localstorage_error.js
+@@ -2,23 +2,23 @@
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ // Test that for pages where local/sessionStorage is not available (like about:home),
+ // the host still appears in the storage tree and no unhandled exception is thrown.
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage("about:home");
++add_task(async function() {
++  await openTabAndSetupStorage("about:home");
+ 
+   let itemsToOpen = [
+     ["localStorage", "about:home"],
+     ["sessionStorage", "about:home"]
+   ];
+ 
+   for (let item of itemsToOpen) {
+-    yield selectTreeItem(item);
++    await selectTreeItem(item);
+     ok(gUI.tree.isSelected(item), `Item ${item.join(" > ")} is present in the tree`);
+   }
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_overflow.js b/devtools/client/storage/test/browser_storage_overflow.js
+--- a/devtools/client/storage/test/browser_storage_overflow.js
++++ b/devtools/client/storage/test/browser_storage_overflow.js
+@@ -1,37 +1,37 @@
+ // Test endless scrolling when a lot of items are present in the storage
+ // inspector table.
+ "use strict";
+ 
+ const ITEMS_PER_PAGE = 50;
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-overflow.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-overflow.html");
+ 
+   gUI.tree.expandAll();
+-  yield selectTreeItem(["localStorage", "http://test1.example.org"]);
++  await selectTreeItem(["localStorage", "http://test1.example.org"]);
+   checkCellLength(ITEMS_PER_PAGE);
+ 
+-  yield scroll();
++  await scroll();
+   checkCellLength(ITEMS_PER_PAGE * 2);
+ 
+-  yield scroll();
++  await scroll();
+   checkCellLength(ITEMS_PER_PAGE * 3);
+ 
+   // Check that the columns are sorted in a human readable way (ascending).
+   checkCellValues("ASC");
+ 
+   // Sort descending.
+   clickColumnHeader("name");
+ 
+   // Check that the columns are sorted in a human readable way (descending).
+   checkCellValues("DEC");
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+ 
+ function checkCellLength(len) {
+   let cells = gPanelWindow.document
+                           .querySelectorAll("#name .table-widget-cell");
+   let msg = `Table should initially display ${len} items`;
+ 
+   is(cells.length, len, msg);
+@@ -41,18 +41,18 @@ function checkCellValues(order) {
+   let cells = [...gPanelWindow.document
+                               .querySelectorAll("#name .table-widget-cell")];
+   cells.forEach(function(cell, index, arr) {
+     let i = order === "ASC" ? index + 1 : arr.length - index;
+     is(cell.value, `item-${i}`, `Cell value is correct (${order}).`);
+   });
+ }
+ 
+-function* scroll() {
++async function scroll() {
+   let $ = id => gPanelWindow.document.querySelector(id);
+   let table = $("#storage-table .table-widget-body");
+   let cell = $("#name .table-widget-cell");
+   let cellHeight = cell.getBoundingClientRect().height;
+ 
+   let onStoresUpdate = gUI.once("store-objects-updated");
+   table.scrollTop += cellHeight * 50;
+-  yield onStoresUpdate;
++  await onStoresUpdate;
+ }
+diff --git a/devtools/client/storage/test/browser_storage_search.js b/devtools/client/storage/test/browser_storage_search.js
+--- a/devtools/client/storage/test/browser_storage_search.js
++++ b/devtools/client/storage/test/browser_storage_search.js
+@@ -1,16 +1,16 @@
+ // Tests the filter search box in the storage inspector
+ "use strict";
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-search.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-search.html");
+ 
+   gUI.tree.expandAll();
+-  yield selectTreeItem(["cookies", "http://test1.example.org"]);
++  await selectTreeItem(["cookies", "http://test1.example.org"]);
+ 
+   showColumn("expires", false);
+   showColumn("host", false);
+   showColumn("isHttpOnly", false);
+   showColumn("lastAccessed", false);
+   showColumn("path", false);
+ 
+   // Results: 0=hidden, 1=visible
+@@ -108,17 +108,17 @@ add_task(function* () {
+       results: [0, 0, 0, 0, 0, 0, 0]
+     },
+   ];
+ 
+   runTests(testcases);
+   showColumn("value", false);
+   runTests(testcasesAfterHiding);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+ 
+ function runTests(testcases) {
+   let $$ = sel => gPanelWindow.document.querySelectorAll(sel);
+   let names = $$("#name .table-widget-cell");
+   let rows = $$("#value .table-widget-cell");
+   for (let testcase of testcases) {
+     let {value, results} = testcase;
+diff --git a/devtools/client/storage/test/browser_storage_search_keyboard_trap.js b/devtools/client/storage/test/browser_storage_search_keyboard_trap.js
+--- a/devtools/client/storage/test/browser_storage_search_keyboard_trap.js
++++ b/devtools/client/storage/test/browser_storage_search_keyboard_trap.js
+@@ -1,15 +1,15 @@
+ // Test ability to focus search field by using keyboard
+ "use strict";
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-search.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-search.html");
+ 
+   gUI.tree.expandAll();
+-  yield selectTreeItem(["localStorage", "http://test1.example.org"]);
++  await selectTreeItem(["localStorage", "http://test1.example.org"]);
+ 
+-  yield focusSearchBoxUsingShortcut(gPanelWindow);
++  await focusSearchBoxUsingShortcut(gPanelWindow);
+   ok(containsFocus(gPanelWindow.document, gUI.searchBox),
+      "Focus is in a searchbox");
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_sessionstorage_add.js b/devtools/client/storage/test/browser_storage_sessionstorage_add.js
+--- a/devtools/client/storage/test/browser_storage_sessionstorage_add.js
++++ b/devtools/client/storage/test/browser_storage_sessionstorage_add.js
+@@ -1,20 +1,20 @@
+ /* 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/. */
+ 
+ // Basic test to check the adding of sessionStorage entries.
+ 
+ "use strict";
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-sessionstorage.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-sessionstorage.html");
+   showAllColumns(true);
+ 
+-  yield performAdd(["sessionStorage", "http://test1.example.org"]);
+-  yield performAdd(["sessionStorage", "http://test1.example.org"]);
+-  yield performAdd(["sessionStorage", "http://test1.example.org"]);
+-  yield performAdd(["sessionStorage", "http://test1.example.org"]);
+-  yield performAdd(["sessionStorage", "http://test1.example.org"]);
++  await performAdd(["sessionStorage", "http://test1.example.org"]);
++  await performAdd(["sessionStorage", "http://test1.example.org"]);
++  await performAdd(["sessionStorage", "http://test1.example.org"]);
++  await performAdd(["sessionStorage", "http://test1.example.org"]);
++  await performAdd(["sessionStorage", "http://test1.example.org"]);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_sessionstorage_edit.js b/devtools/client/storage/test/browser_storage_sessionstorage_edit.js
+--- a/devtools/client/storage/test/browser_storage_sessionstorage_edit.js
++++ b/devtools/client/storage/test/browser_storage_sessionstorage_edit.js
+@@ -1,24 +1,24 @@
+ /* 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/. */
+ 
+ // Basic test to check the editing of localStorage.
+ 
+ "use strict";
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-sessionstorage.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-sessionstorage.html");
+ 
+-  yield selectTreeItem(["sessionStorage", "http://test1.example.org"]);
++  await selectTreeItem(["sessionStorage", "http://test1.example.org"]);
+ 
+-  yield editCell("TestSS1", "name", "newTestSS1");
+-  yield editCell("newTestSS1", "value", "newValueSS1");
++  await editCell("TestSS1", "name", "newTestSS1");
++  await editCell("newTestSS1", "value", "newValueSS1");
+ 
+-  yield editCell("TestSS3", "name", "newTestSS3");
+-  yield editCell("newTestSS3", "value", "newValueSS3");
++  await editCell("TestSS3", "name", "newTestSS3");
++  await editCell("newTestSS3", "value", "newValueSS3");
+ 
+-  yield editCell("TestSS5", "name", "newTestSS5");
+-  yield editCell("newTestSS5", "value", "newValueSS5");
++  await editCell("TestSS5", "name", "newTestSS5");
++  await editCell("newTestSS5", "value", "newValueSS5");
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_sidebar.js b/devtools/client/storage/test/browser_storage_sidebar.js
+--- a/devtools/client/storage/test/browser_storage_sidebar.js
++++ b/devtools/client/storage/test/browser_storage_sidebar.js
+@@ -92,34 +92,34 @@ const testCases = [
+   {
+     sendEscape: true
+   }, {
+     location: "obj-s2",
+     sidebarHidden: true
+   }
+ ];
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
+ 
+   for (let test of testCases) {
+     let { location, sidebarHidden, sendEscape } = test;
+ 
+     info("running " + JSON.stringify(test));
+ 
+     if (Array.isArray(location)) {
+-      yield selectTreeItem(location);
++      await selectTreeItem(location);
+     } else if (location) {
+-      yield selectTableItem(location);
++      await selectTableItem(location);
+     }
+ 
+     if (sendEscape) {
+       EventUtils.sendKey("ESCAPE", gPanelWindow);
+     } else {
+       is(gUI.sidebar.hidden, sidebarHidden,
+         "correct visibility state of sidebar.");
+     }
+ 
+     info("-".repeat(80));
+   }
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_sidebar_toggle.js b/devtools/client/storage/test/browser_storage_sidebar_toggle.js
+--- a/devtools/client/storage/test/browser_storage_sidebar_toggle.js
++++ b/devtools/client/storage/test/browser_storage_sidebar_toggle.js
+@@ -21,37 +21,37 @@ const testCases = [
+     clickToggle: true
+   },
+   {
+     location: getCookieId("cs2", ".example.org", "/"),
+     sidebarHidden: true
+   }
+ ];
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
+ 
+   for (let test of testCases) {
+     let { location, sidebarHidden, clickToggle, toggleButtonVisible } = test;
+ 
+     info("running " + JSON.stringify(test));
+ 
+     if (Array.isArray(location)) {
+-      yield selectTreeItem(location);
++      await selectTreeItem(location);
+     } else if (location) {
+-      yield selectTableItem(location);
++      await selectTableItem(location);
+     }
+ 
+     if (clickToggle) {
+       toggleSidebar();
+     } else if (typeof toggleButtonHidden !== "undefined") {
+       is(sidebarToggleVisible(), toggleButtonVisible,
+          "correct visibility state of toggle button");
+     } else {
+       is(gUI.sidebar.hidden, sidebarHidden,
+         "correct visibility state of sidebar.");
+     }
+ 
+     info("-".repeat(80));
+   }
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_sidebar_update.js b/devtools/client/storage/test/browser_storage_sidebar_update.js
+--- a/devtools/client/storage/test/browser_storage_sidebar_update.js
++++ b/devtools/client/storage/test/browser_storage_sidebar_update.js
+@@ -3,39 +3,39 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ // Test to verify that the sidebar is not broken when several updates
+ // come in quick succession. See bug 1260380 - it could happen that the
+ // "Parsed Value" section gets duplicated.
+ 
+ "use strict";
+ 
+-add_task(function* () {
++add_task(async function() {
+   const ITEM_NAME = "ls1";
+   const UPDATE_COUNT = 3;
+ 
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-complex-values.html");
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-complex-values.html");
+ 
+   let updated = gUI.once("sidebar-updated");
+-  yield selectTreeItem(["localStorage", "http://test1.example.org"]);
+-  yield selectTableItem(ITEM_NAME);
+-  yield updated;
++  await selectTreeItem(["localStorage", "http://test1.example.org"]);
++  await selectTableItem(ITEM_NAME);
++  await updated;
+ 
+   is(gUI.sidebar.hidden, false, "sidebar is visible");
+ 
+   // do several updates in a row and wait for them to finish
+   let updates = [];
+   for (let i = 0; i < UPDATE_COUNT; i++) {
+     info(`Performing update #${i}`);
+     updates.push(gUI.once("sidebar-updated"));
+     gUI.updateObjectSidebar();
+   }
+-  yield promise.all(updates);
++  await promise.all(updates);
+ 
+   info("Updates performed, going to verify result");
+   let parsedScope = gUI.view.getScopeAtIndex(1);
+   let elements = parsedScope.target.querySelectorAll(
+     `.name[value="${ITEM_NAME}"]`);
+   is(elements.length, 1,
+     `There is only one displayed variable named '${ITEM_NAME}'`);
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/browser_storage_values.js b/devtools/client/storage/test/browser_storage_values.js
+--- a/devtools/client/storage/test/browser_storage_values.js
++++ b/devtools/client/storage/test/browser_storage_values.js
+@@ -142,28 +142,28 @@ const testCases = [
+   [null, [
+     {name: "1.id2", value: "1"},
+     {name: "1.name", value: "foo"},
+     {name: "1.email", value: "foo@bar.com"},
+     {name: "1.extra", value: "baz"},
+   ], true]
+ ];
+ 
+-add_task(function* () {
+-  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-complex-values.html");
++add_task(async function() {
++  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-complex-values.html");
+ 
+   gUI.tree.expandAll();
+ 
+   for (let item of testCases) {
+     info("clicking for item " + item);
+ 
+     if (Array.isArray(item[0])) {
+-      yield selectTreeItem(item[0]);
++      await selectTreeItem(item[0]);
+       continue;
+     } else if (item[0]) {
+-      yield selectTableItem(item[0]);
++      await selectTableItem(item[0]);
+     }
+ 
+-    yield findVariableViewProperties(item[1], item[2]);
++    await findVariableViewProperties(item[1], item[2]);
+   }
+ 
+-  yield finishTests();
++  await finishTests();
+ });
+diff --git a/devtools/client/storage/test/head.js b/devtools/client/storage/test/head.js
+--- a/devtools/client/storage/test/head.js
++++ b/devtools/client/storage/test/head.js
+@@ -50,21 +50,21 @@ registerCleanupFunction(() => {
+  * This generator function opens the given url in a new tab, then sets up the
+  * page by waiting for all cookies, indexedDB items etc.
+  *
+  * @param url {String} The url to be opened in the new tab
+  * @param options {Object} The tab options for the new tab
+  *
+  * @return {Promise} A promise that resolves after the tab is ready
+  */
+-function* openTab(url, options = {}) {
+-  let tab = yield addTab(url, options);
++async function openTab(url, options = {}) {
++  let tab = await addTab(url, options);
+ 
+   // Setup the async storages in main window and for all its iframes
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
++  await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
+     /**
+      * Get all windows including frames recursively.
+      *
+      * @param {Window} [baseWindow]
+      *        The base window at which to start looking for child windows
+      *        (optional).
+      * @return {Set}
+      *         A set of windows.
+@@ -84,26 +84,26 @@ function* openTab(url, options = {}) {
+       return windows;
+     }
+ 
+     let windows = getAllWindows(content);
+     for (let win of windows) {
+       let readyState = win.document.readyState;
+       info(`Found a window: ${readyState}`);
+       if (readyState != "complete") {
+-        yield new Promise(resolve => {
++        await new Promise(resolve => {
+           let onLoad = () => {
+             win.removeEventListener("load", onLoad);
+             resolve();
+           };
+           win.addEventListener("load", onLoad);
+         });
+       }
+       if (win.setup) {
+-        yield win.setup();
++        await win.setup();
+       }
+     }
+   });
+ 
+   return tab;
+ }
+ 
+ /**
+@@ -112,33 +112,33 @@ function* openTab(url, options = {}) {
+  * opens the storage inspector and waits for the storage tree and table to be
+  * populated.
+  *
+  * @param url {String} The url to be opened in the new tab
+  * @param options {Object} The tab options for the new tab
+  *
+  * @return {Promise} A promise that resolves after storage inspector is ready
+  */
+-function* openTabAndSetupStorage(url, options = {}) {
++async function openTabAndSetupStorage(url, options = {}) {
+   // open tab
+-  yield openTab(url, options);
++  await openTab(url, options);
+ 
+   // open storage inspector
+-  return yield openStoragePanel();
++  return openStoragePanel();
+ }
+ 
+ /**
+  * Open the toolbox, with the storage tool visible.
+  *
+  * @param cb {Function} Optional callback, if you don't want to use the returned
+  *                      promise
+  *
+  * @return {Promise} a promise that resolves when the storage inspector is ready
+  */
+-var openStoragePanel = Task.async(function* (cb) {
++var openStoragePanel = async function(cb) {
+   info("Opening the storage inspector");
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+ 
+   let storage, toolbox;
+ 
+   // Checking if the toolbox and the storage are already loaded
+   // The storage-updated event should only be waited for if the storage
+   // isn't loaded yet
+@@ -157,40 +157,40 @@ var openStoragePanel = Task.async(functi
+       return {
+         toolbox: toolbox,
+         storage: storage
+       };
+     }
+   }
+ 
+   info("Opening the toolbox");
+-  toolbox = yield gDevTools.showToolbox(target, "storage");
++  toolbox = await gDevTools.showToolbox(target, "storage");
+   storage = toolbox.getPanel("storage");
+   gPanelWindow = storage.panelWindow;
+   gUI = storage.UI;
+   gToolbox = toolbox;
+ 
+   // The table animation flash causes some timeouts on Linux debug tests,
+   // so we disable it
+   gUI.animationsEnabled = false;
+ 
+   info("Waiting for the stores to update");
+-  yield gUI.once("store-objects-updated");
++  await gUI.once("store-objects-updated");
+ 
+-  yield waitForToolboxFrameFocus(toolbox);
++  await waitForToolboxFrameFocus(toolbox);
+ 
+   if (cb) {
+     return cb(storage, toolbox);
+   }
+ 
+   return {
+     toolbox: toolbox,
+     storage: storage
+   };
+-});
++};
+ 
+ /**
+  * Wait for the toolbox frame to receive focus after it loads
+  *
+  * @param toolbox {Toolbox}
+  *
+  * @return a promise that resolves when focus has been received
+  */
+@@ -210,19 +210,19 @@ function forceCollections() {
+   Cu.forceGC();
+   Cu.forceCC();
+   Cu.forceShrinkingGC();
+ }
+ 
+ /**
+  * Cleans up and finishes the test
+  */
+-function* finishTests() {
++async function finishTests() {
+   while (gBrowser.tabs.length > 1) {
+-    yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
++    await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
+       /**
+        * Get all windows including frames recursively.
+        *
+        * @param {Window} [baseWindow]
+        *        The base window at which to start looking for child windows
+        *        (optional).
+        * @return {Set}
+        *         A set of windows.
+@@ -248,31 +248,31 @@ function* finishTests() {
+         try {
+           win.localStorage.clear();
+           win.sessionStorage.clear();
+         } catch (ex) {
+           // ignore
+         }
+ 
+         if (win.clear) {
+-          yield win.clear();
++          await win.clear();
+         }
+       }
+     });
+ 
+-    yield closeTabAndToolbox(gBrowser.selectedTab);
++    await closeTabAndToolbox(gBrowser.selectedTab);
+   }
+ 
+   Services.cookies.removeAll();
+   forceCollections();
+   finish();
+ }
+ 
+ // Sends a click event on the passed DOM node in an async manner
+-function* click(node) {
++function click(node) {
+   node.scrollIntoView();
+ 
+   return new Promise(resolve => {
+     // We need setTimeout here to allow any scrolling to complete before clicking
+     // the node.
+     setTimeout(() => {
+       node.click();
+       resolve();
+@@ -501,55 +501,55 @@ function matchVariablesViewProperty(prop
+ }
+ 
+ /**
+  * Click selects a row in the table.
+  *
+  * @param {[String]} ids
+  *        The array id of the item in the tree
+  */
+-function* selectTreeItem(ids) {
++async function selectTreeItem(ids) {
+   if (gUI.tree.isSelected(ids)) {
+     info(`"${ids}" is already selected, returning.`);
+     return;
+   }
+   if (!gUI.tree.exists(ids)) {
+     info(`"${ids}" does not exist, returning.`);
+     return;
+   }
+ 
+   // The item exists but is not selected... select it.
+   info(`Selecting "${ids}".`);
+   let updated = gUI.once("store-objects-updated");
+   gUI.tree.selectedItem = ids;
+-  yield updated;
++  await updated;
+ }
+ 
+ /**
+  * Click selects a row in the table.
+  *
+  * @param {String} id
+  *        The id of the row in the table widget
+  */
+-function* selectTableItem(id) {
++async function selectTableItem(id) {
+   let table = gUI.table;
+   let selector = ".table-widget-column#" + table.uniqueId +
+                  " .table-widget-cell[value='" + id + "']";
+   let target = gPanelWindow.document.querySelector(selector);
+ 
+   ok(target, "table item found with ids " + id);
+ 
+   if (!target) {
+     showAvailableIds();
+   }
+ 
+   let updated = gUI.once("sidebar-updated");
+ 
+-  yield click(target);
+-  yield updated;
++  await click(target);
++  await updated;
+ }
+ 
+ /**
+  * Wait for eventName on target.
+  * @param {Object} target An observable object that either supports on/off or
+  * addEventListener/removeEventListener
+  * @param {String} eventName
+  * @param {Boolean} useCapture Optional, for addEventListener/removeEventListener
+@@ -710,36 +710,36 @@ function getCellValue(id, column) {
+  * @param {String} newValue
+  *        Replacement value.
+  * @param {Boolean} validate
+  *        Validate result? Default true.
+  *
+  * @yield {String}
+  *        The uniqueId of the changed row.
+  */
+-function* editCell(id, column, newValue, validate = true) {
++async function editCell(id, column, newValue, validate = true) {
+   let row = getRowCells(id, true);
+   let editableFieldsEngine = gUI.table._editableFieldsEngine;
+ 
+   editableFieldsEngine.edit(row[column]);
+ 
+-  yield typeWithTerminator(newValue, "KEY_Enter", validate);
++  await typeWithTerminator(newValue, "KEY_Enter", validate);
+ }
+ 
+ /**
+  * Begin edit mode for a cell.
+  *
+  * @param {String} id
+  *        The uniqueId of the row.
+  * @param {String} column
+  *        The id of the column
+  * @param {Boolean} selectText
+  *        Select text? Default true.
+  */
+-function* startCellEdit(id, column, selectText = true) {
++function startCellEdit(id, column, selectText = true) {
+   let row = getRowCells(id, true);
+   let editableFieldsEngine = gUI.table._editableFieldsEngine;
+   let cell = row[column];
+ 
+   info("Selecting row " + id);
+   gUI.table.selectedRow = id;
+ 
+   info("Starting cell edit (" + id + ", " + column + ")");
+@@ -819,17 +819,17 @@ function showAllColumns(state) {
+  *
+  * @param  {String} str
+  *         The string to type.
+  * @param  {String} terminator
+  *         The terminating key e.g. KEY_Enter or KEY_Tab
+  * @param  {Boolean} validate
+  *         Validate result? Default true.
+  */
+-function* typeWithTerminator(str, terminator, validate = true) {
++async function typeWithTerminator(str, terminator, validate = true) {
+   let editableFieldsEngine = gUI.table._editableFieldsEngine;
+   let textbox = editableFieldsEngine.textbox;
+   let colName = textbox.closest(".table-widget-column").id;
+ 
+   let changeExpected = str !== textbox.value;
+ 
+   if (!changeExpected) {
+     return editableFieldsEngine.currentTarget.getAttribute("data-id");
+@@ -838,23 +838,23 @@ function* typeWithTerminator(str, termin
+   info("Typing " + str);
+   EventUtils.sendString(str);
+ 
+   info("Pressing " + terminator);
+   EventUtils.synthesizeKey(terminator);
+ 
+   if (validate) {
+     info("Validating results... waiting for ROW_EDIT event.");
+-    let uniqueId = yield gUI.table.once(TableWidget.EVENTS.ROW_EDIT);
++    let uniqueId = await gUI.table.once(TableWidget.EVENTS.ROW_EDIT);
+ 
+     checkCell(uniqueId, colName, str);
+     return uniqueId;
+   }
+ 
+-  return yield gUI.table.once(TableWidget.EVENTS.ROW_EDIT);
++  return gUI.table.once(TableWidget.EVENTS.ROW_EDIT);
+ }
+ 
+ function getCurrentEditorValue() {
+   let editableFieldsEngine = gUI.table._editableFieldsEngine;
+   let textbox = editableFieldsEngine.textbox;
+ 
+   return textbox.value;
+ }
+@@ -879,21 +879,21 @@ function PressKeyXTimes(key, x, modifier
+  * Verify the storage inspector state: check that given type/host exists
+  * in the tree, and that the table contains rows with specified names.
+  *
+  * @param {Array} state Array of state specifications. For example,
+  *        [["cookies", "example.com"], ["c1", "c2"]] means to select the
+  *        "example.com" host in cookies and then verify there are "c1" and "c2"
+  *        cookies (and no other ones).
+  */
+-function* checkState(state) {
++async function checkState(state) {
+   for (let [store, names] of state) {
+     let storeName = store.join(" > ");
+     info(`Selecting tree item ${storeName}`);
+-    yield selectTreeItem(store);
++    await selectTreeItem(store);
+ 
+     let items = gUI.table.items;
+ 
+     is(items.size, names.length,
+       `There is correct number of rows in ${storeName}`);
+ 
+     if (names.length === 0) {
+       showAvailableIds();
+@@ -922,32 +922,32 @@ function containsFocus(doc, container) {
+     if (elm === container) {
+       return true;
+     }
+     elm = elm.parentNode;
+   }
+   return false;
+ }
+ 
+-var focusSearchBoxUsingShortcut = Task.async(function* (panelWin, callback) {
++var focusSearchBoxUsingShortcut = async function(panelWin, callback) {
+   info("Focusing search box");
+   let searchBox = panelWin.document.getElementById("storage-searchbox");
+   let focused = once(searchBox, "focus");
+ 
+   panelWin.focus();
+   let strings = Services.strings.createBundle(
+     "chrome://devtools/locale/storage.properties");
+   synthesizeKeyShortcut(strings.GetStringFromName("storage.filter.key"));
+ 
+-  yield focused;
++  await focused;
+ 
+   if (callback) {
+     callback();
+   }
+-});
++};
+ 
+ function getCookieId(name, domain, path) {
+   return `${name}${SEPARATOR_GUID}${domain}${SEPARATOR_GUID}${path}`;
+ }
+ 
+ function setPermission(url, permission) {
+   const nsIPermissionManager = Ci.nsIPermissionManager;
+ 
+@@ -969,37 +969,37 @@ function sidebarToggleVisible() {
+ }
+ 
+ /**
+  * Add an item.
+  * @param  {Array} store
+  *         An array containing the path to the store to which we wish to add an
+  *         item.
+  */
+-function* performAdd(store) {
++async function performAdd(store) {
+   let storeName = store.join(" > ");
+   let toolbar = gPanelWindow.document.getElementById("storage-toolbar");
+   let type = store[0];
+ 
+-  yield selectTreeItem(store);
++  await selectTreeItem(store);
+ 
+   let menuAdd = toolbar.querySelector(
+     "#add-button");
+ 
+   if (menuAdd.hidden) {
+     is(menuAdd.hidden, false,
+        `performAdd called for ${storeName} but it is not supported`);
+     return;
+   }
+ 
+   let eventEdit = gUI.table.once("row-edit");
+   let eventWait = gUI.once("store-objects-edit");
+ 
+   menuAdd.click();
+ 
+-  let rowId = yield eventEdit;
+-  yield eventWait;
++  let rowId = await eventEdit;
++  await eventWait;
+ 
+   let key = type === "cookies" ? "uniqueKey" : "name";
+   let value = getCellValue(rowId, key);
+ 
+   is(rowId, value, `Row '${rowId}' was successfully added.`);
+ }
+diff --git a/devtools/client/storage/test/storage-complex-values.html b/devtools/client/storage/test/storage-complex-values.html
+--- a/devtools/client/storage/test/storage-complex-values.html
++++ b/devtools/client/storage/test/storage-complex-values.html
+@@ -1,9 +1,9 @@
+-<!DOCTYPE HTML>
++<!DOCTYPE HTML>
+ <html>
+ <!--
+ Bug 970517 - Storage inspector front end - tests
+ -->
+ <head>
+   <meta charset="utf-8">
+   <title>Storage inspector test for correct values in the sidebar</title>
+ </head>
+@@ -37,36 +37,36 @@ sessionStorage.setItem("ss2", "This~is~a
+ sessionStorage.setItem("ss3", "this#is~an#object~foo#bar");
+ sessionStorage.setItem("ss4", "#array##with#empty#items");
+ // long string that is almost an object and might trigger exponential
+ // regexp backtracking
+ let s = "a".repeat(1000);
+ sessionStorage.setItem("ss5", `${s}=${s}=${s}=${s}&${s}=${s}&${s}`);
+ console.log("added cookies and stuff from main page");
+ 
+-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 });
+       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"});
+@@ -76,48 +76,48 @@ let idbGenerator = function*() {
+     id2: 1,
+     name: "foo",
+     email: "foo@bar.com",
+     extra: "baz"});
+ 
+   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();
+   console.log("added cookies and stuff from main page");
+ };
+ 
+ function deleteDB(dbName) {
+   return new Promise(resolve => {
+     dump("removing database " + dbName + " from " + document.location + "\n");
+     indexedDB.deleteDatabase(dbName).onsuccess = resolve;
+   });
+ }
+ 
+-window.setup = function*() {
+-  yield idbGenerator();
++window.setup = async function () {
++  await idbGenerator();
+ };
+ 
+-window.clear = function*() {
+-  yield deleteDB("idb1");
+-  yield deleteDB("idb2");
++window.clear = async function () {
++  await deleteDB("idb1");
++  await deleteDB("idb2");
+ 
+   dump("removed indexedDB data from " + document.location + "\n");
+ };
+ </script>
+ </body>
+ </html>
+diff --git a/devtools/client/storage/test/storage-empty-objectstores.html b/devtools/client/storage/test/storage-empty-objectstores.html
+--- a/devtools/client/storage/test/storage-empty-objectstores.html
++++ b/devtools/client/storage/test/storage-empty-objectstores.html
+@@ -2,60 +2,60 @@
+ <html>
+ <head>
+   <meta charset="utf-8">
+   <title>Test for proper listing indexedDB databases with no object stores</title>
+ </head>
+ <body>
+ <script type="application/javascript">
+ 
+-window.setup = function* () {
++window.setup = async function () {
+   let request = indexedDB.open("idb1", 1);
+-  let db = yield new Promise((resolve, reject) => {
++  let db = await new Promise((resolve, reject) => {
+     request.onerror = e => reject(Error("error opening db connection"));
+     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 = () => resolve(db);
+     };
+   });
+ 
+-  yield new Promise(resolve => request.onsuccess = resolve);
++  await new Promise(resolve => request.onsuccess = resolve);
+ 
+   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"});
+ 
+-  yield new Promise(resolve => transaction.oncomplete = resolve);
++  await new Promise(resolve => transaction.oncomplete = resolve);
+ 
+   db.close();
+ 
+   request = indexedDB.open("idb2", 1);
+-  let db2 = yield new Promise((resolve, reject) => {
++  let db2 = await new Promise((resolve, reject) => {
+     request.onerror = e => reject(Error("error opening db2 connection"));
+     request.onupgradeneeded = event => resolve(event.target.result);
+   });
+ 
+-  yield new Promise(resolve => request.onsuccess = resolve);
++  await new Promise(resolve => request.onsuccess = resolve);
+ 
+   db2.close();
+   dump("added indexedDB items from main page\n");
+ };
+ 
+-window.clear = function* () {
++window.clear = async function () {
+   for (let dbName of ["idb1", "idb2"]) {
+-    yield new Promise(resolve => {
++    await new Promise(resolve => {
+       indexedDB.deleteDatabase(dbName).onsuccess = resolve;
+     });
+   }
+   dump("removed indexedDB items from main page\n");
+ };
+ 
+ </script>
+ </body>
+diff --git a/devtools/client/storage/test/storage-idb-delete-blocked.html b/devtools/client/storage/test/storage-idb-delete-blocked.html
+--- a/devtools/client/storage/test/storage-idb-delete-blocked.html
++++ b/devtools/client/storage/test/storage-idb-delete-blocked.html
+@@ -4,48 +4,48 @@
+   <meta charset="utf-8">
+   <title>Test for proper listing indexedDB databases with no object stores</title>
+ </head>
+ <body>
+ <script type="application/javascript">
+ 
+ let db;
+ 
+-window.setup = function* () {
+-  db = yield new Promise((resolve, reject) => {
++window.setup = async function () {
++  db = await new Promise((resolve, reject) => {
+     let request = indexedDB.open("idb", 1);
+ 
+     request.onsuccess = e => resolve(e.target.result);
+     request.onerror = e => reject(new Error("error opening db connection"));
+ 
+     request.onupgradeneeded = e => {
+       let db = e.target.result;
+       let store = db.createObjectStore("obj", { keyPath: "id" });
+     };
+   });
+ 
+   dump("opened indexedDB\n");
+ };
+ 
+-window.closeDb = function* () {
++window.closeDb = function() {
+   db.close();
+ };
+ 
+-window.deleteDb = function* () {
+-  yield new Promise((resolve, reject) => {
++window.deleteDb = async function () {
++  await new Promise((resolve, reject) => {
+     let request = indexedDB.deleteDatabase("idb");
+ 
+     request.onsuccess = resolve;
+     request.onerror = e => reject(new Error("error deleting db"));
+   });
+ };
+ 
+-window.clear = function* () {
++window.clear = async function () {
+   for (let dbName of ["idb1", "idb2"]) {
+-    yield new Promise(resolve => {
++    await new Promise(resolve => {
+       indexedDB.deleteDatabase(dbName).onsuccess = resolve;
+     });
+   }
+   dump("removed indexedDB items from main page\n");
+ };
+ 
+ </script>
+ </body>
+diff --git a/devtools/client/storage/test/storage-indexeddb-duplicate-names.html b/devtools/client/storage/test/storage-indexeddb-duplicate-names.html
+--- a/devtools/client/storage/test/storage-indexeddb-duplicate-names.html
++++ b/devtools/client/storage/test/storage-indexeddb-duplicate-names.html
+@@ -27,23 +27,23 @@
+ 
+     function deleteDB(dbName, storage) {
+       return new Promise(resolve => {
+         dump(`removing database ${dbName} (${storage}) from ${document.location}\n`);
+         indexedDB.deleteDatabase(dbName, { storage: storage }).onsuccess = resolve;
+       });
+     }
+ 
+-    window.clear = function* () {
+-      yield deleteDB("idb1", "temporary");
+-      yield deleteDB("idb1", "default");
+-      yield deleteDB("idb1", "persistent");
+-      yield deleteDB("idb2", "temporary");
+-      yield deleteDB("idb2", "default");
+-      yield deleteDB("idb2", "persistent");
++    window.clear = async function () {
++      await deleteDB("idb1", "temporary");
++      await deleteDB("idb1", "default");
++      await deleteDB("idb1", "persistent");
++      await deleteDB("idb2", "temporary");
++      await deleteDB("idb2", "default");
++      await deleteDB("idb2", "persistent");
+ 
+       dump(`removed indexedDB data from ${document.location}\n`);
+     };
+   </script>
+ </head>
+ <body onload="createIndexedDBs()">
+   <h1>storage-indexeddb-duplicate-names.html</h1>
+ </body>
+diff --git a/devtools/client/storage/test/storage-listings-usercontextid.html b/devtools/client/storage/test/storage-listings-usercontextid.html
+--- a/devtools/client/storage/test/storage-listings-usercontextid.html
++++ b/devtools/client/storage/test/storage-listings-usercontextid.html
+@@ -23,17 +23,17 @@ document.cookie = "c3uc1=foobar-2; expir
+   new Date(cookieExpiresTime2).toGMTString() + "; path=/";
+ // ... and some local storage items ..
+ localStorage.setItem("ls1uc1", "foobar");
+ localStorage.setItem("ls2uc1", "foobar-2");
+ // ... and finally some session storage items too
+ sessionStorage.setItem("ss1uc1", "foobar-3");
+ dump("added cookies and storage from main page\n");
+ 
+-let idbGenerator = async function() {
++let idbGenerator = async function () {
+   let request = indexedDB.open("idb1uc1", 1);
+   request.onerror = function() {
+     throw new Error("error opening db connection");
+   };
+   let db = await new Promise(done => {
+     request.onupgradeneeded = event => {
+       let db = event.target.result;
+       let store1 = db.createObjectStore("obj1uc1", { keyPath: "id" });
+@@ -97,35 +97,35 @@ function deleteDB(dbName) {
+   });
+ }
+ 
+ async function fetchPut(cache, url) {
+   let response = await fetch(url);
+   await cache.put(url, response);
+ }
+ 
+-let cacheGenerator = async function() {
++let cacheGenerator = async function () {
+   let cache = await caches.open("plopuc1");
+   await fetchPut(cache, "404_cached_file.js");
+   await fetchPut(cache, "browser_storage_basic.js");
+ };
+ 
+-window.setup = function*() {
+-  yield idbGenerator();
++window.setup = async function () {
++  await idbGenerator();
+ 
+   if (window.caches) {
+-    yield cacheGenerator();
++    await cacheGenerator();
+   }
+ };
+ 
+-window.clear = function*() {
+-  yield deleteDB("idb1uc1");
+-  yield deleteDB("idb2uc1");
++window.clear = async function () {
++  await deleteDB("idb1uc1");
++  await deleteDB("idb2uc1");
+ 
+   if (window.caches) {
+-    yield caches.delete("plopuc1");
++    await caches.delete("plopuc1");
+   }
+ 
+   dump("removed indexedDB and cache data from " + document.location + "\n");
+ };
+ </script>
+ </body>
+ </html>
+diff --git a/devtools/client/storage/test/storage-listings-with-fragment.html b/devtools/client/storage/test/storage-listings-with-fragment.html
+--- a/devtools/client/storage/test/storage-listings-with-fragment.html
++++ b/devtools/client/storage/test/storage-listings-with-fragment.html
+@@ -1,9 +1,9 @@
+-<!DOCTYPE HTML>
++<!DOCTYPE HTML>
+ <html>
+ <!--
+ This test differs from browser_storage_listings.html only because the URLs we load
+ include fragments e.g. http://example.com/test.js#abcdefg
+                                                  ^^^^^^^^
+                                                  fragment
+ -->
+ <head>
+@@ -26,17 +26,17 @@ 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 = async function() {
++let idbGenerator = async function () {
+   let request = indexedDB.open("idb1", 1);
+   request.onerror = function() {
+     throw new Error("error opening db connection");
+   };
+   let db = await new Promise(done => {
+     request.onupgradeneeded = event => {
+       let db = event.target.result;
+       let store1 = db.createObjectStore("obj1", { keyPath: "id" });
+@@ -100,35 +100,35 @@ function deleteDB(dbName) {
+   });
+ }
+ 
+ async function fetchPut(cache, url) {
+   let response = await fetch(url);
+   await cache.put(url, response);
+ }
+ 
+-let cacheGenerator = async function() {
++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();
++window.setup = async function () {
++  await idbGenerator();
+ 
+   if (window.caches) {
+-    yield cacheGenerator();
++    await cacheGenerator();
+   }
+ };
+ 
+-window.clear = function*() {
+-  yield deleteDB("idb1");
+-  yield deleteDB("idb2");
++window.clear = async function () {
++  await deleteDB("idb1");
++  await deleteDB("idb2");
+ 
+   if (window.caches) {
+-    yield caches.delete("plop");
++    await caches.delete("plop");
+   }
+ 
+   dump("removed indexedDB and cache data from " + document.location + "\n");
+ };
+ </script>
+ </body>
+ </html>
+diff --git a/devtools/client/storage/test/storage-listings.html b/devtools/client/storage/test/storage-listings.html
+--- a/devtools/client/storage/test/storage-listings.html
++++ b/devtools/client/storage/test/storage-listings.html
+@@ -1,9 +1,9 @@
+-<!DOCTYPE HTML>
++<!DOCTYPE HTML>
+ <html>
+ <!--
+ Storage inspector front end - tests
+ -->
+ <head>
+   <meta charset="utf-8">
+   <title>Storage inspector test for listing hosts and storages</title>
+ </head>
+@@ -26,17 +26,17 @@ document.cookie = "c4=foobar-3; expires=
+   partialHostname;
+ // ... 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 = async function() {
++let idbGenerator = async function () {
+   let request = indexedDB.open("idb1", 1);
+   request.onerror = function() {
+     throw new Error("error opening db connection");
+   };
+   let db = await new Promise(done => {
+     request.onupgradeneeded = event => {
+       let db = event.target.result;
+       let store1 = db.createObjectStore("obj1", { keyPath: "id" });
+@@ -100,35 +100,35 @@ function deleteDB(dbName) {
+   });
+ }
+ 
+ async function fetchPut(cache, url) {
+   let response = await fetch(url);
+   await cache.put(url, response);
+ }
+ 
+-let cacheGenerator = async function() {
++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();
++window.setup = async function () {
++  await idbGenerator();
+ 
+   if (window.caches) {
+-    yield cacheGenerator();
++    await cacheGenerator();
+   }
+ };
+ 
+-window.clear = function*() {
+-  yield deleteDB("idb1");
+-  yield deleteDB("idb2");
++window.clear = async function () {
++  await deleteDB("idb1");
++  await deleteDB("idb2");
+ 
+   if (window.caches) {
+-    yield caches.delete("plop");
++    await caches.delete("plop");
+   }
+ 
+   dump("removed indexedDB and cache data from " + document.location + "\n");
+ };
+ </script>
+ </body>
+ </html>
+diff --git a/devtools/client/storage/test/storage-secured-iframe-usercontextid.html b/devtools/client/storage/test/storage-secured-iframe-usercontextid.html
+--- a/devtools/client/storage/test/storage-secured-iframe-usercontextid.html
++++ b/devtools/client/storage/test/storage-secured-iframe-usercontextid.html
+@@ -9,83 +9,83 @@ Iframe for testing multiple host detetio
+ <body>
+ <script type="application/javascript">
+ "use strict";
+ document.cookie = "sc1uc1=foobar;";
+ localStorage.setItem("iframe-s-ls1uc1", "foobar");
+ sessionStorage.setItem("iframe-s-ss1uc1", "foobar-2");
+ dump("added cookies and storage from secured iframe\n");
+ 
+-let idbGenerator = function*() {
++let idbGenerator = async function () {
+   let request = indexedDB.open("idb-s1uc1", 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("obj-s1uc1", { keyPath: "id" });
+       store1.transaction.oncomplete = () => {
+         done(db);
+       };
+     };
+   });
+-  yield new Promise(done => {
++  await new Promise(done => {
+     request.onsuccess = done;
+   });
+ 
+   let transaction = db.transaction(["obj-s1uc1"], "readwrite");
+   let store1 = transaction.objectStore("obj-s1uc1");
+   store1.add({id: 6, name: "foo", email: "foo@bar.com"});
+   store1.add({id: 7, name: "foo2", email: "foo2@bar.com"});
+-  yield new Promise(success => {
++  await new Promise(success => {
+     transaction.oncomplete = success;
+   });
+ 
+   db.close();
+ 
+   request = indexedDB.open("idb-s2uc1", 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("obj-s2uc1", { keyPath: "id3", autoIncrement: true });
+       store3.createIndex("name2", "name2", { unique: true });
+       store3.transaction.oncomplete = () => {
+         done(db2);
+       };
+     };
+   });
+-  yield new Promise(done => {
++  await new Promise(done => {
+     request.onsuccess = done;
+   });
+ 
+   transaction = db2.transaction(["obj-s2uc1"], "readwrite");
+   let store3 = transaction.objectStore("obj-s2uc1");
+   store3.add({id3: 16, name2: "foo", email: "foo@bar.com"});
+-  yield new Promise(success => {
++  await new Promise(success => {
+     transaction.oncomplete = success;
+   });
+ 
+   db2.close();
+   dump("added indexedDB from secured iframe\n");
+ };
+ 
+ function deleteDB(dbName) {
+   return new Promise(resolve => {
+     dump("removing database " + dbName + " from " + document.location + "\n");
+     indexedDB.deleteDatabase(dbName).onsuccess = resolve;
+   });
+ }
+ 
+-window.setup = function*() {
+-  yield idbGenerator();
++window.setup = async function () {
++  await idbGenerator();
+ };
+ 
+-window.clear = function*() {
+-  yield deleteDB("idb-s1uc1");
+-  yield deleteDB("idb-s2uc1");
++window.clear = async function () {
++  await deleteDB("idb-s1uc1");
++  await deleteDB("idb-s2uc1");
+ 
+   dump("removed indexedDB data from " + document.location + "\n");
+ };
+ </script>
+ </body>
+ </html>
+diff --git a/devtools/client/storage/test/storage-secured-iframe.html b/devtools/client/storage/test/storage-secured-iframe.html
+--- a/devtools/client/storage/test/storage-secured-iframe.html
++++ b/devtools/client/storage/test/storage-secured-iframe.html
+@@ -1,9 +1,9 @@
+-<!DOCTYPE HTML>
++<!DOCTYPE HTML>
+ <html>
+ <!--
+ Iframe for testing multiple host detetion in storage actor
+ -->
+ <head>
+   <meta charset="utf-8">
+ </head>
+ <body>
+@@ -12,83 +12,83 @@ Iframe for testing multiple host detetio
+ let cookieExpiresTime = 2000000000000;
+ document.cookie = "sc1=foobar;";
+ document.cookie = "sc2=foobar-2; expires=" +
+   new Date(cookieExpiresTime).toGMTString() + ";";
+ localStorage.setItem("iframe-s-ls1", "foobar");
+ sessionStorage.setItem("iframe-s-ss1", "foobar-2");
+ dump("added cookies and storage from secured iframe\n");
+ 
+-let idbGenerator = function*() {
++let idbGenerator = async function () {
+   let request = indexedDB.open("idb-s1", 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("obj-s1", { keyPath: "id" });
+       store1.transaction.oncomplete = () => {
+         done(db);
+       };
+     };
+   });
+-  yield new Promise(done => {
++  await new Promise(done => {
+     request.onsuccess = done;
+   });
+ 
+   let transaction = db.transaction(["obj-s1"], "readwrite");
+   let store1 = transaction.objectStore("obj-s1");
+   store1.add({id: 6, name: "foo", email: "foo@bar.com"});
+   store1.add({id: 7, name: "foo2", email: "foo2@bar.com"});
+-  yield new Promise(success => {
++  await new Promise(success => {
+     transaction.oncomplete = success;
+   });
+ 
+   db.close();
+ 
+   request = indexedDB.open("idb-s2", 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("obj-s2", { keyPath: "id3", autoIncrement: true });
+       store3.createIndex("name2", "name2", { unique: true });
+       store3.transaction.oncomplete = () => {
+         done(db2);
+       };
+     };
+   });
+-  yield new Promise(done => {
++  await new Promise(done => {
+     request.onsuccess = done;
+   });
+ 
+   transaction = db2.transaction(["obj-s2"], "readwrite");
+   let store3 = transaction.objectStore("obj-s2");
+   store3.add({id3: 16, name2: "foo", email: "foo@bar.com"});
+-  yield new Promise(success => {
++  await new Promise(success => {
+     transaction.oncomplete = success;
+   });
+ 
+   db2.close();
+   dump("added indexedDB from secured iframe\n");
+ };
+ 
+ function deleteDB(dbName) {
+   return new Promise(resolve => {
+     dump("removing database " + dbName + " from " + document.location + "\n");
+     indexedDB.deleteDatabase(dbName).onsuccess = resolve;
+   });
+ }
+ 
+-window.setup = function*() {
+-  yield idbGenerator();
++window.setup = async function () {
++  await idbGenerator();
+ };
+ 
+-window.clear = function*() {
+-  yield deleteDB("idb-s1");
+-  yield deleteDB("idb-s2");
++window.clear = async function () {
++  await deleteDB("idb-s1");
++  await deleteDB("idb-s2");
+ 
+   dump("removed indexedDB data from " + document.location + "\n");
+ };
+ </script>
+ </body>
+ </html>

+ 2649 - 0
frg/work-js/mozilla-release/patches/1440321-1p-styleeditor-61a1.patch

@@ -0,0 +1,2649 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  0de7eac84f941b2472bde06ed9d3877e5ae92b10
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1p. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/styleeditor/StyleEditorUI.jsm b/devtools/client/styleeditor/StyleEditorUI.jsm
+--- a/devtools/client/styleeditor/StyleEditorUI.jsm
++++ b/devtools/client/styleeditor/StyleEditorUI.jsm
+@@ -6,17 +6,16 @@
+ "use strict";
+ 
+ this.EXPORTED_SYMBOLS = ["StyleEditorUI"];
+ 
+ const {loader, require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ const Services = require("Services");
+ const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
+ const {OS} = require("resource://gre/modules/osfile.jsm");
+-const {Task} = require("devtools/shared/task");
+ const EventEmitter = require("devtools/shared/old-event-emitter");
+ const {gDevTools} = require("devtools/client/framework/devtools");
+ const {
+   getString,
+   text,
+   wire,
+   showFilePicker,
+ } = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
+@@ -107,47 +106,47 @@ StyleEditorUI.prototype = {
+     return this.selectedEditor ?
+            this.selectedEditor.styleSheet.styleSheetIndex : -1;
+   },
+ 
+   /**
+    * Initiates the style editor ui creation, the inspector front to get
+    * reference to the walker and the selector highlighter if available
+    */
+-  initialize: Task.async(function* () {
+-    yield this.initializeHighlighter();
++  async initialize() {
++    await this.initializeHighlighter();
+ 
+     this.createUI();
+ 
+-    let styleSheets = yield this._debuggee.getStyleSheets();
+-    yield this._resetStyleSheetList(styleSheets);
++    let styleSheets = await this._debuggee.getStyleSheets();
++    await this._resetStyleSheetList(styleSheets);
+ 
+     this._target.on("will-navigate", this._clear);
+     this._target.on("navigate", this._onNewDocument);
+-  }),
++  },
+ 
+-  initializeHighlighter: Task.async(function* () {
++  async initializeHighlighter() {
+     let toolbox = gDevTools.getToolbox(this._target);
+-    yield toolbox.initInspector();
++    await toolbox.initInspector();
+     this._walker = toolbox.walker;
+ 
+     let hUtils = toolbox.highlighterUtils;
+     if (hUtils.supportsCustomHighlighters()) {
+       try {
+         this._highlighter =
+-          yield hUtils.getHighlighterByType(SELECTOR_HIGHLIGHTER_TYPE);
++          await hUtils.getHighlighterByType(SELECTOR_HIGHLIGHTER_TYPE);
+       } catch (e) {
+         // The selectorHighlighter can't always be instantiated, for example
+         // it doesn't work with XUL windows (until bug 1094959 gets fixed);
+         // or the selectorHighlighter doesn't exist on the backend.
+         console.warn("The selectorHighlighter couldn't be instantiated, " +
+           "elements matching hovered selectors will not be highlighted");
+       }
+     }
+-  }),
++  },
+ 
+   /**
+    * Build the initial UI and wire buttons with event handlers.
+    */
+   createUI: function() {
+     let viewRoot = this._root.parentNode.querySelector(".splitview-root");
+ 
+     this._view = new SplitView(viewRoot);
+@@ -228,33 +227,33 @@ StyleEditorUI.prototype = {
+   },
+ 
+   /**
+    * Add editors for all the given stylesheets to the UI.
+    *
+    * @param  {array} styleSheets
+    *         Array of StyleSheetFront
+    */
+-  _resetStyleSheetList: Task.async(function* (styleSheets) {
++  async _resetStyleSheetList(styleSheets) {
+     this._clear();
+     this._suppressAdd = false;
+ 
+     for (let sheet of styleSheets) {
+       try {
+-        yield this._addStyleSheet(sheet);
++        await this._addStyleSheet(sheet);
+       } catch (e) {
+         console.error(e);
+         this.emit("error", { key: LOAD_ERROR, level: "warning" });
+       }
+     }
+ 
+     this._root.classList.remove("loading");
+ 
+     this.emit("stylesheets-reset");
+-  }),
++  },
+ 
+   /**
+    * Remove all editors and add loading indicator.
+    */
+   _clear: function() {
+     // remember selected sheet and line number for next load
+     if (this.selectedEditor && this.selectedEditor.sourceEditor) {
+       let href = this.selectedEditor.styleSheet.href;
+@@ -354,17 +353,17 @@ StyleEditorUI.prototype = {
+    *
+    * @param {StyleSheet}  styleSheet
+    *        Object representing stylesheet
+    * @param {Boolean} isNew
+    *         Optional if stylesheet is a new sheet created by user
+    * @return {Promise} that is resolved with the created StyleSheetEditor when
+    *                   the editor is fully initialized or rejected on error.
+    */
+-  _addStyleSheetEditor: Task.async(function* (styleSheet, isNew) {
++  async _addStyleSheetEditor(styleSheet, isNew) {
+     // recall location of saved file for this sheet after page reload
+     let file = null;
+     let identifier = this.getStyleSheetIdentifier(styleSheet);
+     let savedFile = this.savedLocations[identifier];
+     if (savedFile) {
+       file = savedFile;
+     }
+ 
+@@ -374,21 +373,21 @@ StyleEditorUI.prototype = {
+     editor.on("property-change", this._summaryChange.bind(this, editor));
+     editor.on("media-rules-changed", this._updateMediaList.bind(this, editor));
+     editor.on("linked-css-file", this._summaryChange.bind(this, editor));
+     editor.on("linked-css-file-error", this._summaryChange.bind(this, editor));
+     editor.on("error", this._onError);
+ 
+     this.editors.push(editor);
+ 
+-    yield editor.fetchSource();
++    await editor.fetchSource();
+     this._sourceLoaded(editor);
+ 
+     return editor;
+-  }),
++  },
+ 
+   /**
+    * Import a style sheet from file and asynchronously create a
+    * new stylesheet on the debuggee for it.
+    *
+    * @param {mixed} file
+    *        Optional nsIFile or filename string.
+    *        If not set a file picker will be shown.
+@@ -622,52 +621,52 @@ StyleEditorUI.prototype = {
+         }
+         this.emit("editor-added", createdEditor);
+       },
+ 
+       onShow: (summary, details, data) => {
+         let showEditor = data.editor;
+         this.selectedEditor = showEditor;
+ 
+-        Task.spawn(function* () {
++        (async function() {
+           if (!showEditor.sourceEditor) {
+             // only initialize source editor when we switch to this view
+             let inputElement =
+                 details.querySelector(".stylesheet-editor-input");
+-            yield showEditor.load(inputElement, this._cssProperties);
++            await showEditor.load(inputElement, this._cssProperties);
+           }
+ 
+           showEditor.onShow();
+ 
+           this.emit("editor-selected", showEditor);
+ 
+           // Is there any CSS coverage markup to include?
+-          let usage = yield csscoverage.getUsage(this._target);
++          let usage = await csscoverage.getUsage(this._target);
+           if (usage == null) {
+             return;
+           }
+ 
+           let sheet = showEditor.styleSheet;
+-          let {reports} = yield usage.createEditorReportForSheet(sheet);
++          let {reports} = await usage.createEditorReportForSheet(sheet);
+ 
+           showEditor.removeAllUnusedRegions();
+ 
+           if (reports.length > 0) {
+             // Only apply if this file isn't compressed. We detect a
+             // compressed file if there are more rules than lines.
+             let editorText = showEditor.sourceEditor.getText();
+             let lineCount = editorText.split("\n").length;
+             let ruleCount = showEditor.styleSheet.ruleCount;
+             if (lineCount >= ruleCount) {
+               showEditor.addUnusedRegions(reports);
+             } else {
+               this.emit("error", { key: "error-compressed", level: "info" });
+             }
+           }
+-        }.bind(this)).catch(console.error);
++        }.bind(this))().catch(console.error);
+       }
+     });
+   },
+ 
+   /**
+    * Switch to the editor that has been marked to be selected.
+    *
+    * @return {Promise}
+@@ -879,18 +878,18 @@ StyleEditorUI.prototype = {
+    * Update the @media rules sidebar for an editor. Hide if there are no rules
+    * Display a list of the @media rules in the editor's associated style sheet.
+    * Emits a 'media-list-changed' event after updating the UI.
+    *
+    * @param  {StyleSheetEditor} editor
+    *         Editor to update @media sidebar of
+    */
+   _updateMediaList: function(editor) {
+-    Task.spawn(function* () {
+-      let details = yield this.getEditorDetails(editor);
++    (async function() {
++      let details = await this.getEditorDetails(editor);
+       let list = details.querySelector(".stylesheet-media-list");
+ 
+       while (list.firstChild) {
+         list.firstChild.remove();
+       }
+ 
+       let rules = editor.mediaRules;
+       let showSidebar = Services.prefs.getBoolPref(PREF_MEDIA_SIDEBAR);
+@@ -904,17 +903,17 @@ StyleEditorUI.prototype = {
+         let location = {
+           line: line,
+           column: column,
+           source: editor.styleSheet.href,
+           styleSheet: parentStyleSheet
+         };
+         if (editor.styleSheet.isOriginalSource) {
+           let styleSheet = editor.cssSheet;
+-          location = yield editor.styleSheet.getOriginalLocation(styleSheet, line,
++          location = await editor.styleSheet.getOriginalLocation(styleSheet, line,
+                                                                  column);
+         }
+ 
+         // this @media rule is from a different original source
+         if (location.source != editor.styleSheet.href) {
+           continue;
+         }
+         inSource = true;
+@@ -944,17 +943,17 @@ StyleEditorUI.prototype = {
+         div.appendChild(link);
+ 
+         list.appendChild(div);
+       }
+ 
+       sidebar.hidden = !showSidebar || !inSource;
+ 
+       this.emit("media-list-changed", editor);
+-    }.bind(this)).catch(console.error);
++    }.bind(this))().catch(console.error);
+   },
+ 
+   /**
+    * Used to safely inject media query links
+    *
+    * @param {HTMLElement} element
+    *        The element corresponding to the media sidebar condition
+    * @param {String} rawText
+@@ -1008,23 +1007,23 @@ StyleEditorUI.prototype = {
+   },
+ 
+   /**
+    * Launches the responsive mode with a specific width or height
+    *
+    * @param  {object} options
+    *         Object with width or/and height properties.
+    */
+-  _launchResponsiveMode: Task.async(function* (options = {}) {
++  async _launchResponsiveMode(options = {}) {
+     let tab = this._target.tab;
+     let win = this._target.tab.ownerDocument.defaultView;
+ 
+-    yield ResponsiveUIManager.openIfNeeded(win, tab);
++    await ResponsiveUIManager.openIfNeeded(win, tab);
+     ResponsiveUIManager.getResponsiveUIForTab(tab).setViewportSize(options);
+-  }),
++  },
+ 
+   /**
+    * Jump cursor to the editor for a stylesheet and line number for a rule.
+    *
+    * @param  {object} location
+    *         Location object with 'line', 'column', and 'source' properties.
+    */
+   _jumpToLocation: function(location) {
+diff --git a/devtools/client/styleeditor/StyleSheetEditor.jsm b/devtools/client/styleeditor/StyleSheetEditor.jsm
+--- a/devtools/client/styleeditor/StyleSheetEditor.jsm
++++ b/devtools/client/styleeditor/StyleSheetEditor.jsm
+@@ -9,17 +9,16 @@ this.EXPORTED_SYMBOLS = ["StyleSheetEdit
+ 
+ const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ const Editor = require("devtools/client/sourceeditor/editor");
+ const promise = require("promise");
+ const {shortSource, prettifyCSS} = require("devtools/shared/inspector/css-logic");
+ const {console} = require("resource://gre/modules/Console.jsm");
+ const Services = require("Services");
+ const EventEmitter = require("devtools/shared/old-event-emitter");
+-const {Task} = require("devtools/shared/task");
+ const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
+ const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
+ const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ const {
+   getString,
+   showFilePicker,
+ } = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
+ 
+@@ -584,34 +583,34 @@ StyleSheetEditor.prototype = {
+ 
+   /**
+    * Highlight nodes matching the selector found at coordinates x,y in the
+    * editor, if any.
+    *
+    * @param {Number} x
+    * @param {Number} y
+    */
+-  _highlightSelectorAt: Task.async(function* (x, y) {
++  async _highlightSelectorAt(x, y) {
+     let pos = this.sourceEditor.getPositionFromCoords({left: x, top: y});
+     let info = this.sourceEditor.getInfoAt(pos);
+     if (!info || info.state !== "selector") {
+       return;
+     }
+ 
+     let node =
+-        yield this.walker.getStyleSheetOwnerNode(this.styleSheet.actorID);
+-    yield this.highlighter.show(node, {
++        await this.walker.getStyleSheetOwnerNode(this.styleSheet.actorID);
++    await this.highlighter.show(node, {
+       selector: info.selector,
+       hideInfoBar: true,
+       showOnly: "border",
+       region: "border"
+     });
+ 
+     this.emit("node-highlighted");
+-  }),
++  },
+ 
+   /**
+    * Save the editor contents into a file and set savedFile property.
+    * A file picker UI will open if file is not set and editor is not headless.
+    *
+    * @param mixed file
+    *        Optional nsIFile or string representing the filename to save in the
+    *        background, no UI will be displayed.
+diff --git a/devtools/client/styleeditor/styleeditor-panel.js b/devtools/client/styleeditor/styleeditor-panel.js
+--- a/devtools/client/styleeditor/styleeditor-panel.js
++++ b/devtools/client/styleeditor/styleeditor-panel.js
+@@ -1,17 +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/. */
+ 
+ "use strict";
+ 
+ var Services = require("Services");
+ var promise = require("promise");
+-var {Task} = require("devtools/shared/task");
+ var {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
+ var EventEmitter = require("devtools/shared/old-event-emitter");
+ 
+ var {StyleEditorUI} = require("resource://devtools/client/styleeditor/StyleEditorUI.jsm");
+ var {getString} = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
+ var {initCssProperties} = require("devtools/shared/fronts/css-properties");
+ 
+ var StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
+@@ -35,39 +34,39 @@ StyleEditorPanel.prototype = {
+ 
+   get panelWindow() {
+     return this._panelWin;
+   },
+ 
+   /**
+    * open is effectively an asynchronous constructor
+    */
+-  open: Task.async(function* () {
++  async open() {
+     // We always interact with the target as if it were remote
+     if (!this.target.isRemote) {
+-      yield this.target.makeRemote();
++      await this.target.makeRemote();
+     }
+ 
+     this.target.on("close", this.destroy);
+ 
+     this._debuggee = this._toolbox.initStyleSheetsFront();
+ 
+     // Initialize the CSS properties database.
+-    const {cssProperties} = yield initCssProperties(this._toolbox);
++    const {cssProperties} = await initCssProperties(this._toolbox);
+ 
+     // Initialize the UI
+     this.UI = new StyleEditorUI(this._debuggee, this.target, this._panelDoc,
+                                 cssProperties);
+     this.UI.on("error", this._showError);
+-    yield this.UI.initialize();
++    await this.UI.initialize();
+ 
+     this.isReady = true;
+ 
+     return this;
+-  }),
++  },
+ 
+   /**
+    * Show an error message from the style editor in the toolbox
+    * notification box.
+    *
+    * @param  {string} event
+    *         Type of event
+    * @param  {string} data
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_add_stylesheet.js b/devtools/client/styleeditor/test/browser_styleeditor_add_stylesheet.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_add_stylesheet.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_add_stylesheet.js
+@@ -2,36 +2,36 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ // Test that a newly-added style sheet shows up in the style editor.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, 2, "Two sheets present after load.");
+ 
+   // We have to wait for the length to change, because we might still
+   // be seeing events from the initial open.
+   let added = new Promise(resolve => {
+     let handler = () => {
+       if (ui.editors.length === 3) {
+         ui.off("editor-added", handler);
+         resolve();
+       }
+     };
+     ui.on("editor-added", handler);
+   });
+ 
+   info("Adding a style sheet");
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
++  await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
+     let document = content.document;
+     const style = document.createElement("style");
+     style.appendChild(document.createTextNode("div { background: #f06; }"));
+     document.head.appendChild(style);
+   });
+-  yield added;
++  await added;
+ 
+   is(ui.editors.length, 3, "Three sheets present after new style sheet");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_autocomplete-disabled.js b/devtools/client/styleeditor/test/browser_styleeditor_autocomplete-disabled.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_autocomplete-disabled.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_autocomplete-disabled.js
+@@ -5,20 +5,20 @@
+ 
+ // Test that autocomplete can be disabled.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTP + "autocomplete.html";
+ 
+ // Pref which decides if CSS autocompletion is enabled in Style Editor or not.
+ const AUTOCOMPLETION_PREF = "devtools.styleeditor.autocompletion-enabled";
+ 
+-add_task(function* () {
++add_task(async function() {
+   Services.prefs.setBoolPref(AUTOCOMPLETION_PREF, false);
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
+-  let editor = yield ui.editors[0].getSourceEditor();
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
++  let editor = await ui.editors[0].getSourceEditor();
+ 
+   is(editor.sourceEditor.getOption("autocomplete"), false,
+      "Autocompletion option does not exist");
+   ok(!editor.sourceEditor.getAutocompletionPopup(),
+      "Autocompletion popup does not exist");
+ });
+ 
+ registerCleanupFunction(() => {
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_autocomplete.js b/devtools/client/styleeditor/test/browser_styleeditor_autocomplete.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_autocomplete.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_autocomplete.js
+@@ -100,32 +100,32 @@ function getTestCases(cssProperties) {
+     ["VK_RIGHT"],
+     ["VK_RIGHT"],
+     ["VK_RIGHT"],
+     ["VK_RIGHT"],
+     ["Ctrl+Space", {total: 1, current: 0}],
+   ];
+ }
+ 
+-add_task(function* () {
+-  let { panel, ui } = yield openStyleEditorForURL(TESTCASE_URI);
+-  let { cssProperties } = yield initCssProperties(panel._toolbox);
++add_task(async function() {
++  let { panel, ui } = await openStyleEditorForURL(TESTCASE_URI);
++  let { cssProperties } = await initCssProperties(panel._toolbox);
+   let testCases = getTestCases(cssProperties);
+ 
+-  yield ui.selectStyleSheet(ui.editors[1].styleSheet);
+-  let editor = yield ui.editors[1].getSourceEditor();
++  await ui.selectStyleSheet(ui.editors[1].styleSheet);
++  let editor = await ui.editors[1].getSourceEditor();
+ 
+   let sourceEditor = editor.sourceEditor;
+   let popup = sourceEditor.getAutocompletionPopup();
+ 
+-  yield SimpleTest.promiseFocus(panel.panelWindow);
++  await SimpleTest.promiseFocus(panel.panelWindow);
+ 
+   for (let index in testCases) {
+-    yield testState(testCases, index, sourceEditor, popup, panel.panelWindow);
+-    yield checkState(testCases, index, sourceEditor, popup);
++    await testState(testCases, index, sourceEditor, popup, panel.panelWindow);
++    await checkState(testCases, index, sourceEditor, popup);
+   }
+ });
+ 
+ function testState(testCases, index, sourceEditor, popup, panelWindow) {
+   let [key, details] = testCases[index];
+   let entered;
+   if (details) {
+     entered = details.entered;
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_bom.js b/devtools/client/styleeditor/test/browser_styleeditor_bom.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_bom.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_bom.js
+@@ -16,19 +16,19 @@ const DOCUMENT = "data:text/html;charset
+            " <body>",
+            " </body>",
+            "</html>"
+           ].join("\n"));
+ 
+ const CONTENTS = "// Note that this file must be utf-16 with a " +
+       "BOM for the test to make sense.\n";
+ 
+-add_task(function* () {
+-  let {ui} = yield openStyleEditorForURL(DOCUMENT);
++add_task(async function() {
++  let {ui} = await openStyleEditorForURL(DOCUMENT);
+ 
+   is(ui.editors.length, 1, "correct number of editors");
+ 
+   let editor = ui.editors[0];
+-  yield editor.getSourceEditor();
++  await editor.getSourceEditor();
+ 
+   let text = editor.sourceEditor.getText();
+   is(text, CONTENTS, "editor contains expected text");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_bug_740541_iframes.js b/devtools/client/styleeditor/test/browser_styleeditor_bug_740541_iframes.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_bug_740541_iframes.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_bug_740541_iframes.js
+@@ -1,15 +1,15 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ // Test that sheets inside iframes are shown in the editor.
+ 
+-add_task(function* () {
++add_task(async function() {
+   function makeStylesheet(selector) {
+     return ("data:text/css;charset=UTF-8," +
+             encodeURIComponent(selector + " { }"));
+   }
+ 
+   function makeDocument(stylesheets, framedDocuments) {
+     stylesheets = stylesheets || [];
+     framedDocuments = framedDocuments || [];
+@@ -63,13 +63,13 @@ add_task(function* () {
+        [makeDocument([makeStylesheet(".c")],
+                                 [])]),
+      makeDocument([SIMPLE], []),
+      SIMPLE_DOCUMENT
+     ]);
+ 
+   const EXPECTED_STYLE_SHEET_COUNT = 12;
+ 
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, EXPECTED_STYLE_SHEET_COUNT,
+     "Got the expected number of style sheets.");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_bug_851132_middle_click.js b/devtools/client/styleeditor/test/browser_styleeditor_bug_851132_middle_click.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_bug_851132_middle_click.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_bug_851132_middle_click.js
+@@ -2,51 +2,51 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ // Test that middle click on style sheet doesn't open styleeditor.xul in a new
+ // tab (bug 851132).
+ 
+ const TESTCASE_URI = TEST_BASE_HTTP + "four.html";
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+   gBrowser.tabContainer.addEventListener("TabOpen", onTabAdded);
+ 
+-  yield ui.editors[0].getSourceEditor();
++  await ui.editors[0].getSourceEditor();
+   info("first editor selected");
+ 
+   info("Left-clicking on the second editor link.");
+-  yield clickOnStyleSheetLink(ui.editors[1], 0);
++  await clickOnStyleSheetLink(ui.editors[1], 0);
+ 
+   info("Waiting for the second editor to be selected.");
+-  let editor = yield ui.once("editor-selected");
++  let editor = await ui.once("editor-selected");
+ 
+   ok(editor.sourceEditor.hasFocus(),
+      "Left mouse click gave second editor focus.");
+ 
+   // middle mouse click should not open a new tab
+   info("Middle clicking on the third editor link.");
+-  yield clickOnStyleSheetLink(ui.editors[2], 1);
++  await clickOnStyleSheetLink(ui.editors[2], 1);
+ });
+ 
+ /**
+  * A helper that clicks on style sheet link in the sidebar.
+  *
+  * @param {StyleSheetEditor} editor
+  *        The editor of which link should be clicked.
+  * @param {MouseEvent.button} button
+  *        The button to click the link with.
+  */
+-function* clickOnStyleSheetLink(editor, button) {
++async function clickOnStyleSheetLink(editor, button) {
+   let window = editor._window;
+   let link = editor.summary.querySelector(".stylesheet-name");
+ 
+   info("Waiting for focus.");
+-  yield SimpleTest.promiseFocus(window);
++  await SimpleTest.promiseFocus(window);
+ 
+   info("Pressing button " + button + " on style sheet name link.");
+   EventUtils.synthesizeMouseAtCenter(link, { button }, window);
+ }
+ 
+ function onTabAdded() {
+   ok(false, "middle mouse click has opened a new tab");
+ }
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_bug_870339.js b/devtools/client/styleeditor/test/browser_styleeditor_bug_870339.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_bug_870339.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_bug_870339.js
+@@ -11,29 +11,29 @@ const DOCUMENT_WITH_ONE_STYLESHEET = "da
+            "  <title>Bug 870339</title>",
+            '  <link rel="stylesheet" type="text/css" href="' + SIMPLE + '">',
+            " </head>",
+            " <body>",
+            " </body>",
+            "</html>"
+           ].join("\n"));
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(DOCUMENT_WITH_ONE_STYLESHEET);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(DOCUMENT_WITH_ONE_STYLESHEET);
+ 
+   // Spam the _onNewDocument callback multiple times before the
+   // StyleEditorActor has a chance to respond to the first one.
+   const SPAM_COUNT = 2;
+   for (let i = 0; i < SPAM_COUNT; ++i) {
+     ui._onNewDocument();
+   }
+ 
+   // Wait for the StyleEditorActor to respond to each "newDocument"
+   // message.
+-  yield new Promise(resolve => {
++  await new Promise(resolve => {
+     let loadCount = 0;
+     ui.on("stylesheets-reset", function onReset() {
+       ++loadCount;
+       if (loadCount == SPAM_COUNT) {
+         ui.off("stylesheets-reset", onReset);
+         // No matter how large SPAM_COUNT is, the number of style
+         // sheets should never be more than the number of style sheets
+         // in the document.
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_cmd_edit.js b/devtools/client/styleeditor/test/browser_styleeditor_cmd_edit.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_cmd_edit.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_cmd_edit.js
+@@ -9,21 +9,21 @@
+ /* import-globals-from ../../commandline/test/helpers.js */
+ Services.scriptloader.loadSubScript(
+   "chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
+   this);
+ 
+ const TEST_URI = "http://example.com/browser/devtools/client/styleeditor/" +
+                  "test/browser_styleeditor_cmd_edit.html";
+ 
+-add_task(function* () {
+-  let options = yield helpers.openTab(TEST_URI);
+-  yield helpers.openToolbar(options);
++add_task(async function() {
++  let options = await helpers.openTab(TEST_URI);
++  await helpers.openToolbar(options);
+ 
+-  yield helpers.audit(options, [
++  await helpers.audit(options, [
+     {
+       setup: "edit",
+       check: {
+         input: "edit",
+         hints: " <resource> [line]",
+         markup: "VVVV",
+         status: "ERROR",
+         args: {
+@@ -187,29 +187,29 @@ add_task(function* () {
+         }
+       },
+     }
+   ]);
+ 
+   let toolbox = gDevTools.getToolbox(options.target);
+   ok(toolbox == null, "toolbox is closed");
+ 
+-  yield helpers.audit(options, [
++  await helpers.audit(options, [
+     {
+       setup: "edit css#style2",
+       check: {
+         input: "edit css#style2",
+       },
+       exec: { output: "" }
+     },
+   ]);
+ 
+   toolbox = gDevTools.getToolbox(options.target);
+   ok(toolbox != null, "toolbox is open");
+ 
+   let styleEditor = toolbox.getCurrentPanel();
+   ok(typeof styleEditor.selectStyleSheet === "function", "styleeditor is open");
+ 
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+ 
+-  yield helpers.closeToolbar(options);
+-  yield helpers.closeTab(options);
++  await helpers.closeToolbar(options);
++  await helpers.closeTab(options);
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_enabled.js b/devtools/client/styleeditor/test/browser_styleeditor_enabled.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_enabled.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_enabled.js
+@@ -3,54 +3,54 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ // Test that style sheets can be disabled and enabled.
+ 
+ // https rather than chrome to improve coverage
+ const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
+ 
+-add_task(function* () {
+-  let { panel, ui } = yield openStyleEditorForURL(TESTCASE_URI);
+-  let editor = yield ui.editors[0].getSourceEditor();
++add_task(async function() {
++  let { panel, ui } = await openStyleEditorForURL(TESTCASE_URI);
++  let editor = await ui.editors[0].getSourceEditor();
+ 
+   let summary = editor.summary;
+   let enabledToggle = summary.querySelector(".stylesheet-enabled");
+   ok(enabledToggle, "enabled toggle button exists");
+ 
+   is(editor.styleSheet.disabled, false,
+      "first stylesheet is initially enabled");
+ 
+   is(summary.classList.contains("disabled"), false,
+      "first stylesheet is initially enabled, UI does not have DISABLED class");
+ 
+   info("Disabling the first stylesheet.");
+-  yield toggleEnabled(editor, enabledToggle, panel.panelWindow);
++  await toggleEnabled(editor, enabledToggle, panel.panelWindow);
+ 
+   is(editor.styleSheet.disabled, true, "first stylesheet is now disabled");
+   is(summary.classList.contains("disabled"), true,
+      "first stylesheet is now disabled, UI has DISABLED class");
+ 
+   info("Enabling the first stylesheet again.");
+-  yield toggleEnabled(editor, enabledToggle, panel.panelWindow);
++  await toggleEnabled(editor, enabledToggle, panel.panelWindow);
+ 
+   is(editor.styleSheet.disabled, false,
+      "first stylesheet is now enabled again");
+   is(summary.classList.contains("disabled"), false,
+      "first stylesheet is now enabled again, UI does not have DISABLED class");
+ });
+ 
+-function* toggleEnabled(editor, enabledToggle, panelWindow) {
++async function toggleEnabled(editor, enabledToggle, panelWindow) {
+   let changed = editor.once("property-change");
+ 
+   info("Waiting for focus.");
+-  yield SimpleTest.promiseFocus(panelWindow);
++  await SimpleTest.promiseFocus(panelWindow);
+ 
+   info("Clicking on the toggle.");
+   EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, panelWindow);
+ 
+   info("Waiting for stylesheet to be disabled.");
+-  let property = yield changed;
++  let property = await changed;
+   while (property !== "disabled") {
+     info("Ignoring property-change for '" + property + "'.");
+-    property = yield editor.once("property-change");
++    property = await editor.once("property-change");
+   }
+ }
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_fetch-from-netmonitor.js b/devtools/client/styleeditor/test/browser_styleeditor_fetch-from-netmonitor.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_fetch-from-netmonitor.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_fetch-from-netmonitor.js
+@@ -3,41 +3,41 @@
+ 
+ "use strict";
+ 
+ // A test to ensure Style Editor only issues 1 request for each stylesheet (instead of 2)
+ // by using the network monitor's request history (bug 1306892).
+ 
+ const TEST_URL = TEST_BASE_HTTP + "doc_fetch_from_netmonitor.html";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Opening netmonitor");
+-  let tab = yield addTab("about:blank");
++  let tab = await addTab("about:blank");
+   let target = TargetFactory.forTab(tab);
+-  let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
++  let toolbox = await gDevTools.showToolbox(target, "netmonitor");
+   let monitor = toolbox.getPanel("netmonitor");
+   let { store, windowRequire } = monitor.panelWin;
+   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
+   let {
+     getSortedRequests,
+   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
+ 
+   store.dispatch(Actions.batchEnable(false));
+ 
+   info("Navigating to test page");
+-  yield navigateTo(TEST_URL);
++  await navigateTo(TEST_URL);
+ 
+   info("Opening Style Editor");
+-  let styleeditor = yield toolbox.selectTool("styleeditor");
++  let styleeditor = await toolbox.selectTool("styleeditor");
+   let ui = styleeditor.UI;
+ 
+   info("Waiting for the sources to be loaded.");
+-  yield ui.editors[0].getSourceEditor();
+-  yield ui.selectStyleSheet(ui.editors[1].styleSheet);
+-  yield ui.editors[1].getSourceEditor();
++  await ui.editors[0].getSourceEditor();
++  await ui.selectStyleSheet(ui.editors[1].styleSheet);
++  await ui.editors[1].getSourceEditor();
+ 
+   info("Checking Netmonitor contents.");
+   let shortRequests = [];
+   let longRequests = [];
+   let hugeRequests = [];
+   for (let item of getSortedRequests(store.getState())) {
+     if (item.url.endsWith("doc_short_string.css")) {
+       shortRequests.push(item);
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_filesave.js b/devtools/client/styleeditor/test/browser_styleeditor_filesave.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_filesave.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_filesave.js
+@@ -9,46 +9,46 @@ const TESTCASE_URI_HTML = TEST_BASE_HTTP
+ const TESTCASE_URI_CSS = TEST_BASE_HTTP + "simple.css";
+ 
+ var tempScope = {};
+ ChromeUtils.import("resource://gre/modules/FileUtils.jsm", tempScope);
+ ChromeUtils.import("resource://gre/modules/NetUtil.jsm", tempScope);
+ var FileUtils = tempScope.FileUtils;
+ var NetUtil = tempScope.NetUtil;
+ 
+-add_task(function* () {
+-  let htmlFile = yield copy(TESTCASE_URI_HTML, "simple.html");
+-  yield copy(TESTCASE_URI_CSS, "simple.css");
++add_task(async function() {
++  let htmlFile = await copy(TESTCASE_URI_HTML, "simple.html");
++  await copy(TESTCASE_URI_CSS, "simple.css");
+   let uri = Services.io.newFileURI(htmlFile);
+   let filePath = uri.resolve("");
+ 
+-  let { ui } = yield openStyleEditorForURL(filePath);
++  let { ui } = await openStyleEditorForURL(filePath);
+ 
+   let editor = ui.editors[0];
+-  yield editor.getSourceEditor();
++  await editor.getSourceEditor();
+ 
+   info("Editing the style sheet.");
+   let dirty = editor.sourceEditor.once("dirty-change");
+   let beginCursor = {line: 0, ch: 0};
+   editor.sourceEditor.replaceText("DIRTY TEXT", beginCursor, beginCursor);
+ 
+-  yield dirty;
++  await dirty;
+ 
+   is(editor.sourceEditor.isClean(), false, "Editor is dirty.");
+   ok(editor.summary.classList.contains("unsaved"),
+      "Star icon is present in the corresponding summary.");
+ 
+   info("Saving the changes.");
+   dirty = editor.sourceEditor.once("dirty-change");
+ 
+   editor.saveToFile(null, function(file) {
+     ok(file, "file should get saved directly when using a file:// URI");
+   });
+ 
+-  yield dirty;
++  await dirty;
+ 
+   is(editor.sourceEditor.isClean(), true, "Editor is clean.");
+   ok(!editor.summary.classList.contains("unsaved"),
+      "Star icon is not present in the corresponding summary.");
+ });
+ 
+ function copy(srcChromeURL, destFileName) {
+   return new Promise(resolve => {
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_highlight-selector.js b/devtools/client/styleeditor/test/browser_styleeditor_highlight-selector.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_highlight-selector.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_highlight-selector.js
+@@ -2,19 +2,19 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // Test that hovering over a simple selector in the style-editor requests the
+ // highlighting of the corresponding nodes
+ 
+-add_task(function* () {
++add_task(async function() {
+   let url = TEST_BASE_HTTP + "selector-highlighter.html";
+-  let { ui } = yield openStyleEditorForURL(url);
++  let { ui } = await openStyleEditorForURL(url);
+   let editor = ui.editors[0];
+ 
+   // Mock the highlighter so we can locally assert that things happened
+   // correctly instead of accessing the highlighter elements
+   editor.highlighter = {
+     isShown: false,
+     options: null,
+ 
+@@ -29,17 +29,17 @@ add_task(function* () {
+     }
+   };
+ 
+   info("Expecting a node-highlighted event");
+   let onHighlighted = editor.once("node-highlighted");
+ 
+   info("Simulate a mousemove event on the div selector");
+   editor._onMouseMove({clientX: 56, clientY: 10});
+-  yield onHighlighted;
++  await onHighlighted;
+ 
+   ok(editor.highlighter.isShown, "The highlighter is now shown");
+   is(editor.highlighter.options.selector, "div", "The selector is correct");
+ 
+   info("Simulate a mousemove event elsewhere in the editor");
+   editor._onMouseMove({clientX: 16, clientY: 0});
+ 
+   ok(!editor.highlighter.isShown, "The highlighter is now hidden");
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_import.js b/devtools/client/styleeditor/test/browser_styleeditor_import.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_import.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_import.js
+@@ -10,24 +10,24 @@ const TESTCASE_URI = TEST_BASE_HTTP + "s
+ 
+ var tempScope = {};
+ ChromeUtils.import("resource://gre/modules/FileUtils.jsm", tempScope);
+ var FileUtils = tempScope.FileUtils;
+ 
+ const FILENAME = "styleeditor-import-test.css";
+ const SOURCE = "body{background:red;}";
+ 
+-add_task(function* () {
+-  let { panel, ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { panel, ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   let added = ui.once("test:editor-updated");
+   importSheet(ui, panel.panelWindow);
+ 
+   info("Waiting for editor to be added for the imported sheet.");
+-  let editor = yield added;
++  let editor = await added;
+ 
+   is(editor.savedFile.leafName, FILENAME,
+      "imported stylesheet will be saved directly into the same file");
+   is(editor.friendlyName, FILENAME,
+      "imported stylesheet has the same name as the filename");
+ });
+ 
+ function importSheet(ui, panelWindow) {
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_import_rule.js b/devtools/client/styleeditor/test/browser_styleeditor_import_rule.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_import_rule.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_import_rule.js
+@@ -3,18 +3,18 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ // Test that style editor shows sheets loaded with @import rules.
+ 
+ // http rather than chrome to improve coverage
+ const TESTCASE_URI = TEST_BASE_HTTP + "import.html";
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, 3,
+     "there are 3 stylesheets after loading @imports");
+ 
+   is(ui.editors[0].styleSheet.href, TEST_BASE_HTTP + "simple.css",
+     "stylesheet 1 is simple.css");
+ 
+   is(ui.editors[1].styleSheet.href, TEST_BASE_HTTP + "import.css",
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_init.js b/devtools/client/styleeditor/test/browser_styleeditor_init.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_init.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_init.js
+@@ -15,18 +15,18 @@ const EXPECTED_SHEETS = [
+   }, {
+     sheetIndex: 1,
+     name: /^<.*>$/,
+     rules: 3,
+     active: false
+   }
+ ];
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, 2, "The UI contains two style sheets.");
+   checkSheet(ui.editors[0], EXPECTED_SHEETS[0]);
+   checkSheet(ui.editors[1], EXPECTED_SHEETS[1]);
+ });
+ 
+ function checkSheet(editor, expected) {
+   is(editor.styleSheet.styleSheetIndex, expected.sheetIndex,
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_inline_friendly_names.js b/devtools/client/styleeditor/test/browser_styleeditor_inline_friendly_names.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_inline_friendly_names.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_inline_friendly_names.js
+@@ -5,27 +5,27 @@
+ 
+ // Test that inline style sheets get correct names if they are saved to disk and
+ // that those names survice a reload but not navigation to another page.
+ 
+ const FIRST_TEST_PAGE = TEST_BASE_HTTP + "inline-1.html";
+ const SECOND_TEST_PAGE = TEST_BASE_HTTP + "inline-2.html";
+ const SAVE_PATH = "test.css";
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(FIRST_TEST_PAGE);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(FIRST_TEST_PAGE);
+ 
+   testIndentifierGeneration(ui);
+ 
+-  yield saveFirstInlineStyleSheet(ui);
+-  yield testFriendlyNamesAfterSave(ui);
+-  yield reloadPageAndWaitForStyleSheets(ui);
+-  yield testFriendlyNamesAfterSave(ui);
+-  yield navigateToAndWaitForStyleSheets(SECOND_TEST_PAGE, ui);
+-  yield testFriendlyNamesAfterNavigation(ui);
++  await saveFirstInlineStyleSheet(ui);
++  await testFriendlyNamesAfterSave(ui);
++  await reloadPageAndWaitForStyleSheets(ui);
++  await testFriendlyNamesAfterSave(ui);
++  await navigateToAndWaitForStyleSheets(SECOND_TEST_PAGE, ui);
++  await testFriendlyNamesAfterNavigation(ui);
+ });
+ 
+ function testIndentifierGeneration(ui) {
+   let fakeStyleSheetFile = {
+     "href": "http://example.com/test.css",
+     "nodeHref": "http://example.com/",
+     "styleSheetIndex": 1
+   };
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_loading.js b/devtools/client/styleeditor/test/browser_styleeditor_loading.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_loading.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_loading.js
+@@ -2,26 +2,26 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ // Test that style editor loads correctly.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTP + "longload.html";
+ 
+-add_task(function* () {
++add_task(async function() {
+   // launch Style Editor right when the tab is created (before load)
+   // this checks that the Style Editor still launches correctly when it is
+   // opened *while* the page is still loading. The Style Editor should not
+   // signal that it is loaded until the accompanying content page is loaded.
+   let tabAdded = addTab(TESTCASE_URI);
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+   let styleEditorLoaded = gDevTools.showToolbox(target, "styleeditor");
+ 
+-  yield Promise.all([tabAdded, styleEditorLoaded]);
++  await Promise.all([tabAdded, styleEditorLoaded]);
+ 
+   let toolbox = gDevTools.getToolbox(target);
+   let panel = toolbox.getPanel("styleeditor");
+   let { panelWindow } = panel;
+ 
+   let root = panelWindow.document.querySelector(".splitview-root");
+   ok(!root.classList.contains("loading"),
+      "style editor root element does not have 'loading' class name anymore");
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_loading_with_containers.js b/devtools/client/styleeditor/test/browser_styleeditor_loading_with_containers.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_loading_with_containers.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_loading_with_containers.js
+@@ -17,37 +17,37 @@ const EXPECTED_SHEETS = [
+   }, {
+     sheetIndex: 1,
+     name: /^<.*>$/,
+     rules: 3,
+     active: false
+   }
+ ];
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Using the personal container.
+   let userContextId = 1;
+-  let { tab } = yield* openTabInUserContext(TESTCASE_URI, userContextId);
+-  let { ui } = yield openStyleEditor(tab);
++  let { tab } = await openTabInUserContext(TESTCASE_URI, userContextId);
++  let { ui } = await openStyleEditor(tab);
+ 
+   is(ui.editors.length, 2, "The UI contains two style sheets.");
+   checkSheet(ui.editors[0], EXPECTED_SHEETS[0]);
+   checkSheet(ui.editors[1], EXPECTED_SHEETS[1]);
+ });
+ 
+-function* openTabInUserContext(uri, userContextId) {
++async function openTabInUserContext(uri, userContextId) {
+   // Open the tab in the correct userContextId.
+   let tab = BrowserTestUtils.addTab(gBrowser, uri, {userContextId});
+ 
+   // Select tab and make sure its browser is focused.
+   gBrowser.selectedTab = tab;
+   tab.ownerDocument.defaultView.focus();
+ 
+   let browser = gBrowser.getBrowserForTab(tab);
+-  yield BrowserTestUtils.browserLoaded(browser);
++  await BrowserTestUtils.browserLoaded(browser);
+   return {tab, browser};
+ }
+ 
+ function checkSheet(editor, expected) {
+   is(editor.styleSheet.styleSheetIndex, expected.sheetIndex,
+     "Style sheet has correct index.");
+ 
+   let summary = editor.summary;
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar.js b/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar.js
+@@ -12,44 +12,44 @@ const RESIZE = 300;
+ const LABELS = ["not all", "all", "(max-width: 400px)",
+                 "(min-height: 300px) and (max-height: 320px)",
+                 "(max-width: 600px)"];
+ const LINE_NOS = [1, 7, 19, 25, 31];
+ const NEW_RULE = "\n@media (max-width: 600px) { div { color: blue; } }";
+ 
+ waitForExplicitFinish();
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, 2, "correct number of editors");
+ 
+   // Test first plain css editor
+   let plainEditor = ui.editors[0];
+-  yield openEditor(plainEditor);
++  await openEditor(plainEditor);
+   testPlainEditor(plainEditor);
+ 
+   // Test editor with @media rules
+   let mediaEditor = ui.editors[1];
+-  yield openEditor(mediaEditor);
++  await openEditor(mediaEditor);
+   testMediaEditor(mediaEditor);
+ 
+   // Test that sidebar hides when flipping pref
+-  yield testShowHide(ui, mediaEditor);
++  await testShowHide(ui, mediaEditor);
+ 
+   // Test adding a rule updates the list
+-  yield testMediaRuleAdded(ui, mediaEditor);
++  await testMediaRuleAdded(ui, mediaEditor);
+ 
+   // Test resizing and seeing @media matching state change
+   let originalWidth = window.outerWidth;
+   let originalHeight = window.outerHeight;
+ 
+   let onMatchesChange = listenForMediaChange(ui);
+   window.resizeTo(RESIZE, RESIZE);
+-  yield onMatchesChange;
++  await onMatchesChange;
+ 
+   testMediaMatchChanged(mediaEditor);
+ 
+   window.resizeTo(originalWidth, originalHeight);
+ });
+ 
+ function testPlainEditor(editor) {
+   let sidebar = editor.details.querySelector(".stylesheet-sidebar");
+@@ -74,39 +74,39 @@ function testMediaMatchChanged(editor) {
+ 
+   let cond = sidebar.querySelectorAll(".media-rule-condition")[2];
+   is(cond.textContent, "(max-width: 400px)",
+      "third rule condition text is correct");
+   ok(!cond.classList.contains("media-condition-unmatched"),
+      "media rule is now matched after resizing");
+ }
+ 
+-function* testShowHide(UI, editor) {
++async function testShowHide(UI, editor) {
+   let sidebarChange = listenForMediaChange(UI);
+   Services.prefs.setBoolPref(MEDIA_PREF, false);
+-  yield sidebarChange;
++  await sidebarChange;
+ 
+   let sidebar = editor.details.querySelector(".stylesheet-sidebar");
+   is(sidebar.hidden, true, "sidebar is hidden after flipping pref");
+ 
+   sidebarChange = listenForMediaChange(UI);
+   Services.prefs.clearUserPref(MEDIA_PREF);
+-  yield sidebarChange;
++  await sidebarChange;
+ 
+   is(sidebar.hidden, false, "sidebar is showing after flipping pref back");
+ }
+ 
+-function* testMediaRuleAdded(UI, editor) {
+-  yield editor.getSourceEditor();
++async function testMediaRuleAdded(UI, editor) {
++  await editor.getSourceEditor();
+   let text = editor.sourceEditor.getText();
+   text += NEW_RULE;
+ 
+   let listChange = listenForMediaChange(UI);
+   editor.sourceEditor.setText(text);
+-  yield listChange;
++  await listChange;
+ 
+   let sidebar = editor.details.querySelector(".stylesheet-sidebar");
+   let entries = [...sidebar.querySelectorAll(".media-rule-label")];
+   is(entries.length, 5, "five @media rules after changing text");
+ 
+   testRule(entries[4], LABELS[4], false, LINE_NOS[4]);
+ }
+ 
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_links.js b/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_links.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_links.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_links.js
+@@ -16,28 +16,28 @@ registerCleanupFunction(() => {
+   asyncStorage.removeItem("devtools.devices.url_cache");
+ });
+ 
+ loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
+ 
+ const TESTCASE_URI = TEST_BASE_HTTPS + "media-rules.html";
+ const responsiveModeToggleClass = ".media-responsive-mode-toggle";
+ 
+-add_task(function* () {
+-  let {ui} = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let {ui} = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   let editor = ui.editors[1];
+-  yield openEditor(editor);
++  await openEditor(editor);
+ 
+   let tab = gBrowser.selectedTab;
+   testNumberOfLinks(editor);
+-  yield testMediaLink(editor, tab, ui, 2, "width", 400);
+-  yield testMediaLink(editor, tab, ui, 3, "height", 300);
++  await testMediaLink(editor, tab, ui, 2, "width", 400);
++  await testMediaLink(editor, tab, ui, 3, "height", 300);
+ 
+-  yield closeRDM(tab, ui);
++  await closeRDM(tab, ui);
+   doFinalChecks(editor);
+ });
+ 
+ function testNumberOfLinks(editor) {
+   let sidebar = editor.details.querySelector(".stylesheet-sidebar");
+   let conditions = sidebar.querySelectorAll(".media-rule-condition");
+ 
+   info("Testing if media rules have the appropriate number of links");
+@@ -46,49 +46,49 @@ function testNumberOfLinks(editor) {
+   ok(!conditions[1].querySelector(responsiveModeToggleClass),
+      "There should be no links in the second media rule.");
+   ok(conditions[2].querySelector(responsiveModeToggleClass),
+      "There should be 1 responsive mode link in the media rule");
+   is(conditions[3].querySelectorAll(responsiveModeToggleClass).length, 2,
+        "There should be 2 responsive mode links in the media rule");
+ }
+ 
+-function* testMediaLink(editor, tab, ui, itemIndex, type, value) {
++async function testMediaLink(editor, tab, ui, itemIndex, type, value) {
+   let sidebar = editor.details.querySelector(".stylesheet-sidebar");
+   let conditions = sidebar.querySelectorAll(".media-rule-condition");
+ 
+   let onMediaChange = once(ui, "media-list-changed");
+ 
+   info("Launching responsive mode");
+   conditions[itemIndex].querySelector(responsiveModeToggleClass).click();
+ 
+   let rdmUI = ResponsiveUIManager.getResponsiveUIForTab(tab);
+   let onContentResize = waitForResizeTo(rdmUI, type, value);
+   rdmUI.transitionsEnabled = false;
+ 
+   info("Waiting for the @media list to update");
+-  yield onMediaChange;
+-  yield onContentResize;
++  await onMediaChange;
++  await onContentResize;
+ 
+   ok(ResponsiveUIManager.isActiveForTab(tab),
+     "Responsive mode should be active.");
+   conditions = sidebar.querySelectorAll(".media-rule-condition");
+   ok(!conditions[itemIndex].classList.contains("media-condition-unmatched"),
+      "media rule should now be matched after responsive mode is active");
+ 
+-  let dimension = (yield getSizing(rdmUI))[type];
++  let dimension = (await getSizing(rdmUI))[type];
+   is(dimension, value, `${type} should be properly set.`);
+ }
+ 
+-function* closeRDM(tab, ui) {
++async function closeRDM(tab, ui) {
+   info("Closing responsive mode");
+   ResponsiveUIManager.toggle(window, tab);
+   let onMediaChange = waitForNEvents(ui, "media-list-changed", 2);
+-  yield once(ResponsiveUIManager, "off");
+-  yield onMediaChange;
++  await once(ResponsiveUIManager, "off");
++  await onMediaChange;
+   ok(!ResponsiveUIManager.isActiveForTab(tab),
+      "Responsive mode should no longer be active.");
+ }
+ 
+ function doFinalChecks(editor) {
+   let sidebar = editor.details.querySelector(".stylesheet-sidebar");
+   let conditions = sidebar.querySelectorAll(".media-rule-condition");
+   conditions = sidebar.querySelectorAll(".media-rule-condition");
+@@ -109,19 +109,19 @@ function waitForResizeTo(rdmUI, type, va
+       info(`Got content-resize to a ${type} of ${value}`);
+       resolve();
+     };
+     info(`Waiting for content-resize to a ${type} of ${value}`);
+     rdmUI.on("content-resize", onResize);
+   });
+ }
+ 
+-function* getSizing(rdmUI) {
++async function getSizing(rdmUI) {
+   let browser = rdmUI.getViewportBrowser();
+-  let sizing = yield ContentTask.spawn(browser, {}, function* () {
++  let sizing = await ContentTask.spawn(browser, {}, async function() {
+     return {
+       width: content.innerWidth,
+       height: content.innerHeight
+     };
+   });
+   return sizing;
+ }
+ 
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_sourcemaps.js b/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_sourcemaps.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_sourcemaps.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_sourcemaps.js
+@@ -9,26 +9,26 @@ const TESTCASE_URI = TEST_BASE_HTTPS + "
+ const MAP_PREF = "devtools.source-map.client-service.enabled";
+ 
+ const LABELS = ["screen and (max-width: 320px)",
+                 "screen and (min-width: 1200px)"];
+ const LINE_NOS = [5, 8];
+ 
+ waitForExplicitFinish();
+ 
+-add_task(function* () {
++add_task(async function() {
+   Services.prefs.setBoolPref(MAP_PREF, true);
+ 
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, 1, "correct number of editors");
+ 
+   // Test editor with @media rules
+   let mediaEditor = ui.editors[0];
+-  yield openEditor(mediaEditor);
++  await openEditor(mediaEditor);
+   testMediaEditor(mediaEditor);
+ 
+   Services.prefs.clearUserPref(MAP_PREF);
+ });
+ 
+ function testMediaEditor(editor) {
+   let sidebar = editor.details.querySelector(".stylesheet-sidebar");
+   is(sidebar.hidden, false, "sidebar is showing on editor with @media");
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_missing_stylesheet.js b/devtools/client/styleeditor/test/browser_styleeditor_missing_stylesheet.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_missing_stylesheet.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_missing_stylesheet.js
+@@ -3,18 +3,18 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ // Checks that the style editor manages to finalize its stylesheet loading phase
+ // even if one stylesheet is missing, and that an error message is displayed.
+ 
+ const TESTCASE_URI = TEST_BASE + "missing.html";
+ 
+-add_task(function* () {
+-  let { ui, toolbox, panel } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui, toolbox, panel } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   // Note that we're not testing for a specific number of stylesheet editors
+   // below because the test-page is loaded with chrome:// URL and, right now,
+   // that means UA stylesheets are shown. So we avoid hardcoding the number of
+   // stylesheets here.
+   ok(ui.editors.length, "The UI contains style sheets.");
+ 
+   let rootEl = panel.panelWindow.document.getElementById("style-editor-chrome");
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_navigate.js b/devtools/client/styleeditor/test/browser_styleeditor_navigate.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_navigate.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_navigate.js
+@@ -6,27 +6,27 @@
+ // Test that selected sheet and cursor position is reset during navigation.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
+ const NEW_URI = TEST_BASE_HTTPS + "media.html";
+ 
+ const LINE_NO = 5;
+ const COL_NO = 3;
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, 2, "Two sheets present after load.");
+ 
+   info("Selecting the second editor");
+-  yield ui.selectStyleSheet(ui.editors[1].styleSheet, LINE_NO, COL_NO);
++  await ui.selectStyleSheet(ui.editors[1].styleSheet, LINE_NO, COL_NO);
+ 
+-  yield navigateToAndWaitForStyleSheets(NEW_URI, ui);
++  await navigateToAndWaitForStyleSheets(NEW_URI, ui);
+ 
+   info("Waiting for source editor to be ready.");
+-  yield ui.editors[0].getSourceEditor();
++  await ui.editors[0].getSourceEditor();
+ 
+   is(ui.selectedEditor, ui.editors[0], "first editor is selected");
+ 
+   let {line, ch} = ui.selectedEditor.sourceEditor.getCursor();
+   is(line, 0, "first line is selected");
+   is(ch, 0, "first column is selected");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_new.js b/devtools/client/styleeditor/test/browser_styleeditor_new.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_new.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_new.js
+@@ -4,28 +4,28 @@
+ "use strict";
+ 
+ // Test that new sheets can be added and edited.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
+ 
+ const TESTCASE_CSS_SOURCE = "body{background-color:red;";
+ 
+-add_task(function* () {
+-  let { panel, ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { panel, ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+-  let editor = yield createNew(ui, panel.panelWindow);
+-  yield testInitialState(editor);
++  let editor = await createNew(ui, panel.panelWindow);
++  await testInitialState(editor);
+ 
+   let originalHref = editor.styleSheet.href;
+   let waitForPropertyChange = onPropertyChange(editor);
+ 
+-  yield typeInEditor(editor, panel.panelWindow);
++  await typeInEditor(editor, panel.panelWindow);
+ 
+-  yield waitForPropertyChange;
++  await waitForPropertyChange;
+ 
+   testUpdated(editor, originalHref);
+ });
+ 
+ function createNew(ui, panelWindow) {
+   info("Creating a new stylesheet now");
+ 
+   return new Promise(resolve => {
+@@ -52,31 +52,31 @@ function onPropertyChange(editor) {
+       if (property == "ruleCount" && text == TESTCASE_CSS_SOURCE + "}") {
+         editor.styleSheet.off("property-change", onProp);
+         resolve();
+       }
+     });
+   });
+ }
+ 
+-function* testInitialState(editor) {
++async function testInitialState(editor) {
+   info("Testing the initial state of the new editor");
+ 
+   let summary = editor.summary;
+ 
+   ok(editor.sourceLoaded, "new editor is loaded when attached");
+   ok(editor.isNew, "new editor has isNew flag");
+ 
+   ok(editor.sourceEditor.hasFocus(), "new editor has focus");
+ 
+   summary = editor.summary;
+   let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
+   is(parseInt(ruleCount, 10), 0, "new editor initially shows 0 rules");
+ 
+-  let color = yield getComputedStyleProperty({
++  let color = await getComputedStyleProperty({
+     selector: "body",
+     name: "background-color"
+   });
+   is(color, "rgb(255, 255, 255)",
+      "content's background color is initially white");
+ }
+ 
+ function typeInEditor(editor, panelWindow) {
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_nostyle.js b/devtools/client/styleeditor/test/browser_styleeditor_nostyle.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_nostyle.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_nostyle.js
+@@ -3,18 +3,18 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ // Test that 'no styles' indicator is shown if a page doesn't contain any style
+ // sheets.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTP + "nostyle.html";
+ 
+-add_task(function* () {
+-  let { panel } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { panel } = await openStyleEditorForURL(TESTCASE_URI);
+   let { panelWindow } = panel;
+ 
+   let root = panelWindow.document.querySelector(".splitview-root");
+   ok(!root.classList.contains("loading"),
+      "style editor root element does not have 'loading' class name anymore");
+ 
+   ok(root.querySelector(".empty.placeholder"), "showing 'no style' indicator");
+ 
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_opentab.js b/devtools/client/styleeditor/test/browser_styleeditor_opentab.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_opentab.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_opentab.js
+@@ -3,20 +3,20 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // A test to check the 'Open Link in new tab' functionality in the
+ // context menu item for stylesheets (bug 992947).
+ const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+-  yield rightClickStyleSheet(ui, ui.editors[0]);
++  await rightClickStyleSheet(ui, ui.editors[0]);
+   is(ui._openLinkNewTabItem.getAttribute("disabled"), "false",
+     "The menu item is not disabled");
+   is(ui._openLinkNewTabItem.getAttribute("hidden"), "false",
+     "The menu item is not hidden");
+ 
+   let url = "https://example.com/browser/devtools/client/styleeditor/test/" +
+     "simple.css";
+   is(ui._contextMenuStyleSheet.href, url, "Correct URL for sheet");
+@@ -30,25 +30,25 @@ add_task(function* () {
+       is(newUrl, url, "The correct tab has been opened");
+       resolve();
+     };
+   });
+ 
+   ui._openLinkNewTabItem.click();
+ 
+   info(`Waiting for a tab to open - ${url}`);
+-  yield tabOpenedDefer;
++  await tabOpenedDefer;
+ 
+-  yield rightClickInlineStyleSheet(ui, ui.editors[1]);
++  await rightClickInlineStyleSheet(ui, ui.editors[1]);
+   is(ui._openLinkNewTabItem.getAttribute("disabled"), "true",
+     "The menu item is disabled");
+   is(ui._openLinkNewTabItem.getAttribute("hidden"), "false",
+     "The menu item is not hidden");
+ 
+-  yield rightClickNoStyleSheet(ui);
++  await rightClickNoStyleSheet(ui);
+   is(ui._openLinkNewTabItem.getAttribute("hidden"), "true",
+     "The menu item is not hidden");
+ });
+ 
+ function onPopupShow(contextMenu) {
+   return new Promise(resolve => {
+     contextMenu.addEventListener("popupshown", function() {
+       resolve();
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_pretty.js b/devtools/client/styleeditor/test/browser_styleeditor_pretty.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_pretty.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_pretty.js
+@@ -43,34 +43,34 @@ const ORIGINAL_SOURCE = "" +
+ "body \{ background\: red; \}\r?\n" +
+ "div \{\r?\n" +
+   "font\-size\: 5em;\r?\n" +
+   "color\: red\r?\n" +
+ "\}";
+ 
+ const EXPAND_TAB = "devtools.editor.expandtab";
+ 
+-add_task(function* () {
++add_task(async function() {
+   let oldExpandTabPref = SpecialPowers.getBoolPref(EXPAND_TAB);
+   // The 'EXPAND_TAB' preference has to be set to false because
+   // the constant 'PRETTIFIED_SOURCE' uses tabs for indentation.
+   SpecialPowers.setBoolPref(EXPAND_TAB, false);
+ 
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+   is(ui.editors.length, 2, "Two sheets present.");
+ 
+   info("Testing minified style sheet.");
+-  let editor = yield ui.editors[0].getSourceEditor();
++  let editor = await ui.editors[0].getSourceEditor();
+ 
+   let prettifiedSourceRE = new RegExp(PRETTIFIED_SOURCE);
+   ok(prettifiedSourceRE.test(editor.sourceEditor.getText()),
+      "minified source has been prettified automatically");
+ 
+   info("Selecting second, non-minified style sheet.");
+-  yield ui.selectStyleSheet(ui.editors[1].styleSheet);
++  await ui.selectStyleSheet(ui.editors[1].styleSheet);
+ 
+   editor = ui.editors[1];
+ 
+   let originalSourceRE = new RegExp(ORIGINAL_SOURCE);
+   ok(originalSourceRE.test(editor.sourceEditor.getText()),
+      "non-minified source has been left untouched");
+ 
+   SpecialPowers.setBoolPref(EXPAND_TAB, oldExpandTabPref);
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_private_perwindowpb.js b/devtools/client/styleeditor/test/browser_styleeditor_private_perwindowpb.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_private_perwindowpb.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_private_perwindowpb.js
+@@ -5,44 +5,44 @@
+ "use strict";
+ 
+ // This test makes sure that the style editor does not store any
+ // content CSS files in the permanent cache when opened from PB mode.
+ 
+ const TEST_URL = "http://" + TEST_HOST + "/browser/devtools/client/" +
+   "styleeditor/test/test_private.html";
+ 
+-add_task(function* () {
++add_task(async function() {
+   info("Opening a new private window");
+   let win = OpenBrowserWindow({private: true});
+-  yield waitForDelayedStartupFinished(win);
++  await waitForDelayedStartupFinished(win);
+ 
+   info("Clearing the browser cache");
+   Services.cache2.clear();
+ 
+-  let { toolbox, ui } = yield openStyleEditorForURL(TEST_URL, win);
++  let { toolbox, ui } = await openStyleEditorForURL(TEST_URL, win);
+ 
+   is(ui.editors.length, 1, "The style editor contains one sheet.");
+   let editor = ui.editors[0];
+ 
+-  yield editor.getSourceEditor();
+-  yield checkDiskCacheFor(TEST_HOST);
++  await editor.getSourceEditor();
++  await checkDiskCacheFor(TEST_HOST);
+ 
+-  yield toolbox.destroy();
++  await toolbox.destroy();
+ 
+   let onUnload = new Promise(done => {
+     win.addEventListener("unload", function listener(event) {
+       if (event.target == win.document) {
+         win.removeEventListener("unload", listener);
+         done();
+       }
+     });
+   });
+   win.close();
+-  yield onUnload;
++  await onUnload;
+ });
+ 
+ function checkDiskCacheFor(host) {
+   let foundPrivateData = false;
+ 
+   return new Promise(resolve => {
+     Visitor.prototype = {
+       onCacheStorageInfo: function(num) {
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_reload.js b/devtools/client/styleeditor/test/browser_styleeditor_reload.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_reload.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_reload.js
+@@ -5,30 +5,30 @@
+ 
+ // Test that selected sheet and cursor position persists during reload.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
+ 
+ const LINE_NO = 5;
+ const COL_NO = 3;
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, 2, "Two sheets present after load.");
+ 
+   info("Selecting the second editor");
+-  yield ui.selectStyleSheet(ui.editors[1].styleSheet, LINE_NO, COL_NO);
++  await ui.selectStyleSheet(ui.editors[1].styleSheet, LINE_NO, COL_NO);
+ 
+-  yield reloadPageAndWaitForStyleSheets(ui);
++  await reloadPageAndWaitForStyleSheets(ui);
+ 
+   is(ui.editors.length, 2, "Two sheets present after reload.");
+ 
+   info("Waiting for source editor to be ready.");
+-  yield ui.editors[1].getSourceEditor();
++  await ui.editors[1].getSourceEditor();
+ 
+   is(ui.selectedEditor, ui.editors[1],
+     "second editor is selected after reload");
+ 
+   let {line, ch} = ui.selectedEditor.sourceEditor.getCursor();
+   is(line, LINE_NO, "correct line selected");
+   is(ch, COL_NO, "correct column selected");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_scroll.js b/devtools/client/styleeditor/test/browser_styleeditor_scroll.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_scroll.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_scroll.js
+@@ -19,73 +19,73 @@ const DOCUMENT_WITH_LONG_SHEET = "data:t
+            '  <link rel="stylesheet" type="text/css" href="' + SIMPLE + '">',
+            '  <link rel="stylesheet" type="text/css" href="' + LONG + '">',
+            " </head>",
+            " <body>Editor scroll test page</body>",
+            "</html>"
+           ].join("\n"));
+ const LINE_TO_SELECT = 201;
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(DOCUMENT_WITH_LONG_SHEET);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(DOCUMENT_WITH_LONG_SHEET);
+ 
+   is(ui.editors.length, 2, "Two editors present.");
+ 
+   let simpleEditor = ui.editors[0];
+   let longEditor = ui.editors[1];
+ 
+   info(`Selecting doc_long.css and scrolling to line ${LINE_TO_SELECT}`);
+ 
+   // We need to wait for editor-selected if we want to check the scroll
+   // position as scrolling occurs after selectStyleSheet resolves but before the
+   // event is emitted.
+   let selectEventPromise = waitForEditorToBeSelected(longEditor, ui);
+-  yield ui.selectStyleSheet(longEditor.styleSheet, LINE_TO_SELECT);
+-  yield selectEventPromise;
++  await ui.selectStyleSheet(longEditor.styleSheet, LINE_TO_SELECT);
++  await selectEventPromise;
+ 
+   info("Checking that the correct line is visible after initial load");
+ 
+   let { from, to } = longEditor.sourceEditor.getViewport();
+   info(`Lines ${from}-${to} are visible (expected ${LINE_TO_SELECT}).`);
+ 
+   ok(from <= LINE_TO_SELECT, "The editor scrolled too much.");
+   ok(to >= LINE_TO_SELECT, "The editor scrolled too little.");
+ 
+   let initialScrollTop = longEditor.sourceEditor.getScrollInfo().top;
+   info(`Storing scrollTop = ${initialScrollTop} for later comparison.`);
+ 
+   info("Selecting the first editor (simple.css)");
+-  yield ui.selectStyleSheet(simpleEditor.styleSheet);
++  await ui.selectStyleSheet(simpleEditor.styleSheet);
+ 
+   info("Selecting doc_long.css again.");
+   selectEventPromise = waitForEditorToBeSelected(longEditor, ui);
+ 
+   // Can't use ui.selectStyleSheet here as it will scroll the editor back to top
+   // and we want to check that the previous scroll position is restored.
+-  let summary = yield ui.getEditorSummary(longEditor);
++  let summary = await ui.getEditorSummary(longEditor);
+   ui._view.activeSummary = summary;
+ 
+   info("Waiting for doc_long.css to be selected.");
+-  yield selectEventPromise;
++  await selectEventPromise;
+ 
+   let scrollTop = longEditor.sourceEditor.getScrollInfo().top;
+   is(scrollTop, initialScrollTop,
+     "Scroll top was restored after the sheet was selected again.");
+ });
+ 
+ /**
+  * A helper that waits "editor-selected" event for given editor.
+  *
+  * @param {StyleSheetEditor} editor
+  *        The editor to wait for.
+  * @param {StyleEditorUI} ui
+  *        The StyleEditorUI the editor belongs to.
+  */
+-var waitForEditorToBeSelected = Task.async(function* (editor, ui) {
++var waitForEditorToBeSelected = async function(editor, ui) {
+   info(`Waiting for ${editor.friendlyName} to be selected.`);
+-  let selected = yield ui.once("editor-selected");
++  let selected = await ui.once("editor-selected");
+   while (selected != editor) {
+     info(`Ignored editor-selected for editor ${editor.friendlyName}.`);
+-    selected = yield ui.once("editor-selected");
++    selected = await ui.once("editor-selected");
+   }
+ 
+   info(`Got editor-selected for ${editor.friendlyName}.`);
+-});
++};
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_selectstylesheet.js b/devtools/client/styleeditor/test/browser_styleeditor_selectstylesheet.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_selectstylesheet.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_selectstylesheet.js
+@@ -6,21 +6,21 @@
+ // Test that StyleEditorUI.selectStyleSheet selects the correct sheet, line and
+ // column.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
+ 
+ const LINE_NO = 5;
+ const COL_NO = 0;
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+   let editor = ui.editors[1];
+ 
+   info("Selecting style sheet #1.");
+-  yield ui.selectStyleSheet(editor.styleSheet.href, LINE_NO);
++  await ui.selectStyleSheet(editor.styleSheet.href, LINE_NO);
+ 
+   is(ui.selectedEditor, ui.editors[1], "Second editor is selected.");
+   let {line, ch} = ui.selectedEditor.sourceEditor.getCursor();
+ 
+   is(line, LINE_NO, "correct line selected");
+   is(ch, COL_NO, "correct column selected");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_sourcemap_large.js b/devtools/client/styleeditor/test/browser_styleeditor_sourcemap_large.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_sourcemap_large.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_sourcemap_large.js
+@@ -6,20 +6,20 @@
+ 
+ // Covers the case from Bug 1128747, where loading a sourcemapped
+ // file prevents the correct editor from being selected on load,
+ // and causes a second iframe to be appended when the user clicks
+ // editor in the list.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTPS + "sourcemaps-large.html";
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+-  yield openEditor(ui.editors[0]);
++  await openEditor(ui.editors[0]);
+   let iframes = ui.selectedEditor.details.querySelectorAll("iframe");
+ 
+   is(iframes.length, 1, "There is only one editor iframe");
+   ok(ui.selectedEditor.summary.classList.contains("splitview-active"),
+     "The editor is selected");
+ });
+ 
+ function openEditor(editor) {
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_sourcemap_watching.js b/devtools/client/styleeditor/test/browser_styleeditor_sourcemap_watching.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_sourcemap_watching.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_sourcemap_watching.js
+@@ -14,69 +14,69 @@ const TESTCASE_SCSS_NAME = "sourcemaps.s
+ 
+ const TRANSITIONS_PREF = "devtools.styleeditor.transitions";
+ 
+ const CSS_TEXT = "* { color: blue }";
+ 
+ const {FileUtils} = ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {});
+ const {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", {});
+ 
+-add_task(function* () {
+-  yield new Promise(resolve => {
++add_task(async function() {
++  await new Promise(resolve => {
+     SpecialPowers.pushPrefEnv({"set": [
+       [TRANSITIONS_PREF, false]
+     ]}, resolve);
+   });
+ 
+   // copy all our files over so we don't screw them up for other tests
+-  let HTMLFile = yield copy(TESTCASE_URI_HTML, ["sourcemaps.html"]);
+-  let CSSFile = yield copy(TESTCASE_URI_CSS,
++  let HTMLFile = await copy(TESTCASE_URI_HTML, ["sourcemaps.html"]);
++  let CSSFile = await copy(TESTCASE_URI_CSS,
+     ["sourcemap-css", "sourcemaps.css"]);
+-  yield copy(TESTCASE_URI_SCSS, ["sourcemap-sass", "sourcemaps.scss"]);
+-  yield copy(TESTCASE_URI_MAP, ["sourcemap-css", "sourcemaps.css.map"]);
+-  yield copy(TESTCASE_URI_REG_CSS, ["simple.css"]);
++  await copy(TESTCASE_URI_SCSS, ["sourcemap-sass", "sourcemaps.scss"]);
++  await copy(TESTCASE_URI_MAP, ["sourcemap-css", "sourcemaps.css.map"]);
++  await copy(TESTCASE_URI_REG_CSS, ["simple.css"]);
+ 
+   let uri = Services.io.newFileURI(HTMLFile);
+   let testcaseURI = uri.resolve("");
+ 
+-  let { ui } = yield openStyleEditorForURL(testcaseURI);
++  let { ui } = await openStyleEditorForURL(testcaseURI);
+ 
+   let editor = ui.editors[1];
+   if (getStylesheetNameFor(editor) != TESTCASE_SCSS_NAME) {
+     editor = ui.editors[2];
+   }
+ 
+   is(getStylesheetNameFor(editor), TESTCASE_SCSS_NAME, "found scss editor");
+ 
+   let link = getLinkFor(editor);
+   link.click();
+ 
+-  yield editor.getSourceEditor();
++  await editor.getSourceEditor();
+ 
+-  let color = yield getComputedStyleProperty({selector: "div", name: "color"});
++  let color = await getComputedStyleProperty({selector: "div", name: "color"});
+   is(color, "rgb(255, 0, 102)", "div is red before saving file");
+ 
+   // let styleApplied = defer();
+   let styleApplied = editor.once("style-applied");
+ 
+-  yield pauseForTimeChange();
++  await pauseForTimeChange();
+ 
+   // Edit and save Sass in the editor. This will start off a file-watching
+   // process waiting for the CSS file to change.
+-  yield editSCSS(editor);
++  await editSCSS(editor);
+ 
+   // We can't run Sass or another compiler, so we fake it by just
+   // directly changing the CSS file.
+-  yield editCSSFile(CSSFile);
++  await editCSSFile(CSSFile);
+ 
+   info("wrote to CSS file, waiting for style-applied event");
+ 
+-  yield styleApplied;
++  await styleApplied;
+ 
+-  color = yield getComputedStyleProperty({selector: "div", name: "color"});
++  color = await getComputedStyleProperty({selector: "div", name: "color"});
+   is(color, "rgb(0, 0, 255)", "div is blue after saving file");
+ 
+   // Ensure that the editor didn't revert.  Bug 1346662.
+   is(editor.sourceEditor.getText(), CSS_TEXT, "edits remain applied");
+ });
+ 
+ function editSCSS(editor) {
+   return new Promise(resolve => {
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_sourcemaps.js b/devtools/client/styleeditor/test/browser_styleeditor_sourcemaps.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_sourcemaps.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_sourcemaps.js
+@@ -72,39 +72,39 @@ const contents = {
+   ].join("\n")
+ };
+ 
+ const cssNames = ["sourcemaps.css", "contained.css", "test-stylus.css"];
+ const origNames = ["sourcemaps.scss", "contained.scss", "test-stylus.styl"];
+ 
+ waitForExplicitFinish();
+ 
+-add_task(function* () {
+-  let {ui} = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let {ui} = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, 4,
+     "correct number of editors with source maps enabled");
+ 
+   // Test first plain css editor
+   testFirstEditor(ui.editors[0]);
+ 
+   // Test Scss editors
+-  yield testEditor(ui.editors[1], origNames);
+-  yield testEditor(ui.editors[2], origNames);
+-  yield testEditor(ui.editors[3], origNames);
++  await testEditor(ui.editors[1], origNames);
++  await testEditor(ui.editors[2], origNames);
++  await testEditor(ui.editors[3], origNames);
+ 
+   // Test disabling original sources
+-  yield togglePref(ui);
++  await togglePref(ui);
+ 
+   is(ui.editors.length, 4, "correct number of editors after pref toggled");
+ 
+   // Test CSS editors
+-  yield testEditor(ui.editors[1], cssNames);
+-  yield testEditor(ui.editors[2], cssNames);
+-  yield testEditor(ui.editors[3], cssNames);
++  await testEditor(ui.editors[1], cssNames);
++  await testEditor(ui.editors[2], cssNames);
++  await testEditor(ui.editors[3], cssNames);
+ 
+   Services.prefs.clearUserPref(PREF);
+ });
+ 
+ function testFirstEditor(editor) {
+   let name = getStylesheetNameFor(editor);
+   is(name, "simple.css", "First style sheet display name is correct");
+ }
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_sourcemaps_inline.js b/devtools/client/styleeditor/test/browser_styleeditor_sourcemaps_inline.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_sourcemaps_inline.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_sourcemaps_inline.js
+@@ -25,40 +25,40 @@ body > h1 {
+ ` +
+ "/*# sourceMappingURL=data:application/json;base64,ewoidmVyc2lvbiI6IDMsCiJtY" +
+ "XBwaW5ncyI6ICJBQUFBLElBQUs7RUFDSCxnQkFBZ0IsRUFBRSxLQUFLO0VBQ3ZCLFNBQU87SUFD" +
+ "TCxLQUFLLEVBQUUsS0FBSyIsCiJzb3VyY2VzIjogWyJ0ZXN0LnNjc3MiXSwKInNvdXJjZXNDb25" +
+ "0ZW50IjogWyJib2R5IHtcbiAgYmFja2dyb3VuZC1jb2xvcjogYmxhY2s7XG4gICYgPiBoMSB7XG" +
+ "4gICAgY29sb3I6IHdoaXRlO1xuICB9XG59XG4iXSwKIm5hbWVzIjogW10sCiJmaWxlIjogInRlc" +
+ "3QuY3NzIgp9Cg== */";
+ 
+-add_task(function* () {
+-  let {ui} = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let {ui} = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, 1,
+     "correct number of editors with source maps enabled");
+ 
+-  yield testEditor(ui.editors[0], "test.scss", sassContent);
++  await testEditor(ui.editors[0], "test.scss", sassContent);
+ 
+   // Test disabling original sources
+-  yield togglePref(ui);
++  await togglePref(ui);
+ 
+   is(ui.editors.length, 1, "correct number of editors after pref toggled");
+ 
+   // Test CSS editors
+-  yield testEditor(ui.editors[0], "<inline style sheet #1>", cssContent);
++  await testEditor(ui.editors[0], "<inline style sheet #1>", cssContent);
+ 
+   Services.prefs.clearUserPref(PREF);
+ });
+ 
+-function* testEditor(editor, expectedName, expectedText) {
++async function testEditor(editor, expectedName, expectedText) {
+   let name = getStylesheetNameFor(editor);
+   is(expectedName, name, name + " editor name is correct");
+ 
+-  yield openEditor(editor);
++  await openEditor(editor);
+   let text = editor.sourceEditor.getText();
+   is(text, expectedText, name + " editor contains expected text");
+ }
+ 
+ /* Helpers */
+ 
+ function togglePref(UI) {
+   let editorsPromise = UI.once("stylesheets-reset");
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_sv_keynav.js b/devtools/client/styleeditor/test/browser_styleeditor_sv_keynav.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_sv_keynav.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_sv_keynav.js
+@@ -2,29 +2,29 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ // Test that the style sheet list can be navigated with keyboard.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTP + "four.html";
+ 
+-add_task(function* () {
+-  let { panel, ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { panel, ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   info("Waiting for source editor to load.");
+-  yield ui.editors[0].getSourceEditor();
++  await ui.editors[0].getSourceEditor();
+ 
+   let selected = ui.once("editor-selected");
+ 
+   info("Testing keyboard navigation on the sheet list.");
+   testKeyboardNavigation(ui.editors[0], panel);
+ 
+   info("Waiting for editor #2 to be selected due to keyboard navigation.");
+-  yield selected;
++  await selected;
+ 
+   ok(ui.editors[2].sourceEditor.hasFocus(), "Editor #2 has focus.");
+ });
+ 
+ function getStylesheetNameLinkFor(editor) {
+   return editor.summary.querySelector(".stylesheet-name");
+ }
+ 
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_sv_resize.js b/devtools/client/styleeditor/test/browser_styleeditor_sv_resize.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_sv_resize.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_sv_resize.js
+@@ -5,25 +5,25 @@
+ "use strict";
+ 
+ // Test that resizing the source editor container doesn't move the caret.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
+ 
+ const {Toolbox} = require("devtools/client/framework/toolbox");
+ 
+-add_task(function* () {
+-  let { toolbox, ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { toolbox, ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, 2, "There are 2 style sheets initially");
+ 
+   info("Changing toolbox host to a window.");
+-  yield toolbox.switchHost(Toolbox.HostType.WINDOW);
++  await toolbox.switchHost(Toolbox.HostType.WINDOW);
+ 
+-  let editor = yield ui.editors[0].getSourceEditor();
++  let editor = await ui.editors[0].getSourceEditor();
+   let originalSourceEditor = editor.sourceEditor;
+ 
+   let hostWindow = toolbox.win.parent;
+   let originalWidth = hostWindow.outerWidth;
+   let originalHeight = hostWindow.outerHeight;
+ 
+   // to check the caret is preserved
+   originalSourceEditor.setCursor(originalSourceEditor.getPosition(4));
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_sync.js b/devtools/client/styleeditor/test/browser_styleeditor_sync.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_sync.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_sync.js
+@@ -14,59 +14,59 @@ const expectedText = `
+     /*! color: red; */
+   }
+ 
+   #testid {
+     /*! font-size: 4em; */
+   }
+   `;
+ 
+-function* closeAndReopenToolbox() {
++async function closeAndReopenToolbox() {
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  yield gDevTools.closeToolbox(target);
+-  let { ui: newui } = yield openStyleEditor();
++  await gDevTools.closeToolbox(target);
++  let { ui: newui } = await openStyleEditor();
+   return newui;
+ }
+ 
+-add_task(function* () {
+-  yield addTab(TESTCASE_URI);
+-  let { inspector, view } = yield openRuleView();
+-  yield selectNode("#testid", inspector);
++add_task(async function() {
++  await addTab(TESTCASE_URI);
++  let { inspector, view } = await openRuleView();
++  await selectNode("#testid", inspector);
+   let ruleEditor = getRuleViewRuleEditor(view, 1);
+ 
+   // Disable the "font-size" property.
+   let propEditor = ruleEditor.rule.textProps[0].editor;
+   let onModification = view.once("ruleview-changed");
+   propEditor.enable.click();
+-  yield onModification;
++  await onModification;
+ 
+   // Disable the "color" property.  Note that this property is in a
+   // rule that also contains a non-inherited property -- so this test
+   // is also testing that property editing works properly in this
+   // situation.
+   ruleEditor = getRuleViewRuleEditor(view, 3);
+   propEditor = ruleEditor.rule.textProps[1].editor;
+   onModification = view.once("ruleview-changed");
+   propEditor.enable.click();
+-  yield onModification;
++  await onModification;
+ 
+-  let { ui } = yield openStyleEditor();
++  let { ui } = await openStyleEditor();
+ 
+-  let editor = yield ui.editors[0].getSourceEditor();
++  let editor = await ui.editors[0].getSourceEditor();
+   let text = editor.sourceEditor.getText();
+   is(text, expectedText, "style inspector changes are synced");
+ 
+   // Close and reopen the toolbox, to see that the edited text remains
+   // available.
+-  ui = yield closeAndReopenToolbox();
+-  editor = yield ui.editors[0].getSourceEditor();
++  ui = await closeAndReopenToolbox();
++  editor = await ui.editors[0].getSourceEditor();
+   text = editor.sourceEditor.getText();
+   is(text, expectedText, "changes remain after close and reopen");
+ 
+   // For the time being, the actor does not update the style's owning
+   // node's textContent.  See bug 1205380.
+-  let textContent = yield ContentTask.spawn(gBrowser.selectedBrowser, null,
+-    function* () {
++  let textContent = await ContentTask.spawn(gBrowser.selectedBrowser, null,
++    async function() {
+       return content.document.querySelector("style").textContent;
+     });
+ 
+   isnot(textContent, expectedText, "changes not written back to style node");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_syncAddProperty.js b/devtools/client/styleeditor/test/browser_styleeditor_syncAddProperty.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_syncAddProperty.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_syncAddProperty.js
+@@ -14,32 +14,32 @@ const expectedText = `
+   }
+ 
+   #testid {
+     font-size: 4em;
+     /*! background-color: yellow; */
+   }
+   `;
+ 
+-add_task(function* () {
+-  yield addTab(TESTCASE_URI);
+-  let { inspector, view } = yield openRuleView();
+-  yield selectNode("#testid", inspector);
++add_task(async function() {
++  await addTab(TESTCASE_URI);
++  let { inspector, view } = await openRuleView();
++  await selectNode("#testid", inspector);
+ 
+   info("Focusing a new property name in the rule-view");
+   let ruleEditor = getRuleViewRuleEditor(view, 1);
+-  let editor = yield focusEditableField(view, ruleEditor.closeBrace);
++  let editor = await focusEditableField(view, ruleEditor.closeBrace);
+   is(inplaceEditor(ruleEditor.newPropSpan), editor,
+     "The new property editor has focus");
+ 
+   let input = editor.input;
+   input.value = "/* background-color: yellow; */";
+ 
+   info("Pressing return to commit and focus the new value field");
+   let onModifications = view.once("ruleview-changed");
+   EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
+-  yield onModifications;
++  await onModifications;
+ 
+-  let { ui } = yield openStyleEditor();
+-  let sourceEditor = yield ui.editors[0].getSourceEditor();
++  let { ui } = await openStyleEditor();
++  let sourceEditor = await ui.editors[0].getSourceEditor();
+   let text = sourceEditor.sourceEditor.getText();
+   is(text, expectedText, "selector edits are synced");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_syncAddRule.js b/devtools/client/styleeditor/test/browser_styleeditor_syncAddRule.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_syncAddRule.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_syncAddRule.js
+@@ -6,26 +6,26 @@
+ // Test that adding a new rule is synced to the style editor.
+ 
+ const TESTCASE_URI = TEST_BASE_HTTP + "sync.html";
+ 
+ const expectedText = `
+ #testid {
+ }`;
+ 
+-add_task(function* () {
+-  yield addTab(TESTCASE_URI);
+-  let { inspector, view } = yield openRuleView();
+-  yield selectNode("#testid", inspector);
++add_task(async function() {
++  await addTab(TESTCASE_URI);
++  let { inspector, view } = await openRuleView();
++  await selectNode("#testid", inspector);
+ 
+   let onRuleViewChanged = once(view, "ruleview-changed");
+   view.addRuleButton.click();
+-  yield onRuleViewChanged;
++  await onRuleViewChanged;
+ 
+-  let { ui } = yield openStyleEditor();
++  let { ui } = await openStyleEditor();
+ 
+   info("Selecting the second editor");
+-  yield ui.selectStyleSheet(ui.editors[1].styleSheet);
++  await ui.selectStyleSheet(ui.editors[1].styleSheet);
+ 
+   let editor = ui.editors[1];
+   let text = editor.sourceEditor.getText();
+   is(text, expectedText, "selector edits are synced");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_syncAlreadyOpen.js b/devtools/client/styleeditor/test/browser_styleeditor_syncAlreadyOpen.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_syncAlreadyOpen.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_syncAlreadyOpen.js
+@@ -14,38 +14,38 @@ const expectedText = `
+     color: red;
+   }
+ 
+   #testid {
+     /*! font-size: 4em; */
+   }
+   `;
+ 
+-add_task(function* () {
+-  yield addTab(TESTCASE_URI);
++add_task(async function() {
++  await addTab(TESTCASE_URI);
+ 
+-  let { inspector, view, toolbox } = yield openRuleView();
++  let { inspector, view, toolbox } = await openRuleView();
+ 
+   // In this test, make sure the style editor is open before making
+   // changes in the inspector.
+-  let { ui } = yield openStyleEditor();
+-  let editor = yield ui.editors[0].getSourceEditor();
++  let { ui } = await openStyleEditor();
++  let editor = await ui.editors[0].getSourceEditor();
+ 
+   let onEditorChange = new Promise(resolve => {
+     editor.sourceEditor.on("change", resolve);
+   });
+ 
+-  yield toolbox.getPanel("inspector");
+-  yield selectNode("#testid", inspector);
++  await toolbox.getPanel("inspector");
++  await selectNode("#testid", inspector);
+   let ruleEditor = getRuleViewRuleEditor(view, 1);
+ 
+   // Disable the "font-size" property.
+   let propEditor = ruleEditor.rule.textProps[0].editor;
+   let onModification = view.once("ruleview-changed");
+   propEditor.enable.click();
+-  yield onModification;
++  await onModification;
+ 
+-  yield openStyleEditor();
+-  yield onEditorChange;
++  await openStyleEditor();
++  await onEditorChange;
+ 
+   let text = editor.sourceEditor.getText();
+   is(text, expectedText, "style inspector changes are synced");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_syncEditSelector.js b/devtools/client/styleeditor/test/browser_styleeditor_syncEditSelector.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_syncEditSelector.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_syncEditSelector.js
+@@ -14,26 +14,26 @@ const expectedText = `
+     color: red;
+   }
+ 
+   #testid, span {
+     font-size: 4em;
+   }
+   `;
+ 
+-add_task(function* () {
+-  yield addTab(TESTCASE_URI);
+-  let { inspector, view } = yield openRuleView();
+-  yield selectNode("#testid", inspector);
++add_task(async function() {
++  await addTab(TESTCASE_URI);
++  let { inspector, view } = await openRuleView();
++  await selectNode("#testid", inspector);
+   let ruleEditor = getRuleViewRuleEditor(view, 1);
+ 
+-  let editor = yield focusEditableField(view, ruleEditor.selectorText);
++  let editor = await focusEditableField(view, ruleEditor.selectorText);
+   editor.input.value = "#testid, span";
+   let onRuleViewChanged = once(view, "ruleview-changed");
+   EventUtils.synthesizeKey("KEY_Enter");
+-  yield onRuleViewChanged;
++  await onRuleViewChanged;
+ 
+-  let { ui } = yield openStyleEditor();
++  let { ui } = await openStyleEditor();
+ 
+-  editor = yield ui.editors[0].getSourceEditor();
++  editor = await ui.editors[0].getSourceEditor();
+   let text = editor.sourceEditor.getText();
+   is(text, expectedText, "selector edits are synced");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_syncIntoRuleView.js b/devtools/client/styleeditor/test/browser_styleeditor_syncIntoRuleView.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_syncIntoRuleView.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_syncIntoRuleView.js
+@@ -9,29 +9,29 @@
+ const TEST_URI = `
+   <style type='text/css'>
+   </style>
+   <div id='testid' class='testclass'>Styled Node</div>
+ `;
+ 
+ const TESTCASE_CSS_SOURCE = "#testid { color: chartreuse; }";
+ 
+-add_task(function* () {
+-  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
++add_task(async function() {
++  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+ 
+-  let {inspector, view} = yield openRuleView();
+-  yield selectNode("#testid", inspector);
++  let {inspector, view} = await openRuleView();
++  await selectNode("#testid", inspector);
+ 
+-  let { panel, ui } = yield openStyleEditor();
++  let { panel, ui } = await openStyleEditor();
+ 
+-  let editor = yield ui.editors[0].getSourceEditor();
++  let editor = await ui.editors[0].getSourceEditor();
+ 
+   let waitForRuleView = view.once("ruleview-refreshed");
+-  yield typeInEditor(editor, panel.panelWindow);
+-  yield waitForRuleView;
++  await typeInEditor(editor, panel.panelWindow);
++  await waitForRuleView;
+ 
+   let value = getRuleViewPropertyValue(view, "#testid", "color");
+   is(value, "chartreuse", "check that edits were synced to rule view");
+ });
+ 
+ function typeInEditor(editor, panelWindow) {
+   return new Promise(resolve => {
+     waitForFocus(function() {
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_transition_rule.js b/devtools/client/styleeditor/test/browser_styleeditor_transition_rule.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_transition_rule.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_transition_rule.js
+@@ -3,34 +3,34 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
+ 
+ const NEW_RULE = "body { background-color: purple; }";
+ 
+-add_task(function* () {
+-  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
++add_task(async function() {
++  let { ui } = await openStyleEditorForURL(TESTCASE_URI);
+ 
+   is(ui.editors.length, 2, "correct number of editors");
+ 
+   let editor = ui.editors[0];
+-  yield openEditor(editor);
++  await openEditor(editor);
+ 
+   // Set text twice in a row
+   let styleChanges = listenForStyleChange(editor.styleSheet);
+ 
+   editor.sourceEditor.setText(NEW_RULE);
+   editor.sourceEditor.setText(NEW_RULE + " ");
+ 
+-  yield styleChanges;
++  await styleChanges;
+ 
+-  let rules = yield ContentTask.spawn(gBrowser.selectedBrowser, 0,
+-  function* (index) {
++  let rules = await ContentTask.spawn(gBrowser.selectedBrowser, 0,
++  async function(index) {
+     let sheet = content.document.styleSheets[index];
+     return [...sheet.cssRules].map(rule => rule.cssText);
+   });
+ 
+   // Test that we removed the transition rule, but kept the rule we added
+   is(rules.length, 1, "only one rule in stylesheet");
+   is(rules[0], NEW_RULE, "stylesheet only contains rule we added");
+ });
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_xul.js b/devtools/client/styleeditor/test/browser_styleeditor_xul.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_xul.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_xul.js
+@@ -5,18 +5,18 @@
+ // Test that the style-editor initializes correctly for XUL windows.
+ 
+ "use strict";
+ 
+ waitForExplicitFinish();
+ 
+ const TEST_URL = TEST_BASE + "doc_xulpage.xul";
+ 
+-add_task(function* () {
+-  let tab = yield addTab(TEST_URL);
++add_task(async function() {
++  let tab = await addTab(TEST_URL);
+   let target = TargetFactory.forTab(tab);
+ 
+-  let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
++  let toolbox = await gDevTools.showToolbox(target, "styleeditor");
+   let panel = toolbox.getCurrentPanel();
+ 
+   ok(panel,
+      "The style-editor panel did initialize correctly for the XUL window");
+ });
+diff --git a/devtools/client/styleeditor/test/head.js b/devtools/client/styleeditor/test/head.js
+--- a/devtools/client/styleeditor/test/head.js
++++ b/devtools/client/styleeditor/test/head.js
+@@ -50,73 +50,73 @@ var navigateTo = function(url) {
+     browser.addEventListener("load", function() {
+       resolve();
+     }, {capture: true, once: true});
+ 
+     browser.loadURI(url);
+   });
+ };
+ 
+-var navigateToAndWaitForStyleSheets = Task.async(function* (url, ui) {
++var navigateToAndWaitForStyleSheets = async function(url, ui) {
+   let onReset = ui.once("stylesheets-reset");
+-  yield navigateTo(url);
+-  yield onReset;
+-});
++  await navigateTo(url);
++  await onReset;
++};
+ 
+-var reloadPageAndWaitForStyleSheets = Task.async(function* (ui) {
++var reloadPageAndWaitForStyleSheets = async function(ui) {
+   info("Reloading the page.");
+ 
+   let onReset = ui.once("stylesheets-reset");
+   let browser = gBrowser.selectedBrowser;
+-  yield ContentTask.spawn(browser, null, "() => content.location.reload()");
+-  yield onReset;
+-});
++  await ContentTask.spawn(browser, null, "() => content.location.reload()");
++  await onReset;
++};
+ 
+ /**
+  * Open the style editor for the current tab.
+  */
+-var openStyleEditor = Task.async(function* (tab) {
++var openStyleEditor = async function(tab) {
+   if (!tab) {
+     tab = gBrowser.selectedTab;
+   }
+   let target = TargetFactory.forTab(tab);
+-  let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
++  let toolbox = await gDevTools.showToolbox(target, "styleeditor");
+   let panel = toolbox.getPanel("styleeditor");
+   let ui = panel.UI;
+ 
+   // The stylesheet list appears with an animation. Let this animation finish.
+   let animations = ui._root.getAnimations({subtree: true});
+-  yield Promise.all(animations.map(a => a.finished));
++  await Promise.all(animations.map(a => a.finished));
+ 
+   return { toolbox, panel, ui };
+-});
++};
+ 
+ /**
+  * Creates a new tab in specified window navigates it to the given URL and
+  * opens style editor in it.
+  */
+-var openStyleEditorForURL = Task.async(function* (url, win) {
+-  let tab = yield addTab(url, win);
+-  let result = yield openStyleEditor(tab);
++var openStyleEditorForURL = async function(url, win) {
++  let tab = await addTab(url, win);
++  let result = await openStyleEditor(tab);
+   result.tab = tab;
+   return result;
+-});
++};
+ 
+ /**
+  * Send an async message to the frame script and get back the requested
+  * computed style property.
+  *
+  * @param {String} selector
+  *        The selector used to obtain the element.
+  * @param {String} pseudo
+  *        pseudo id to query, or null.
+  * @param {String} name
+  *        name of the property.
+  */
+-var getComputedStyleProperty = function* (args) {
+-  return yield ContentTask.spawn(gBrowser.selectedBrowser, args,
++var getComputedStyleProperty = async function(args) {
++  return ContentTask.spawn(gBrowser.selectedBrowser, args,
+     function({selector, pseudo, name}) {
+       let element = content.document.querySelector(selector);
+       let style = content.getComputedStyle(element, pseudo);
+       return style.getPropertyValue(name);
+     }
+   );
+ };

+ 3160 - 0
frg/work-js/mozilla-release/patches/1440321-1q-webaudioeditor-61a1.patch

@@ -0,0 +1,3160 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  a508a805b68fff9f2a83815d2eab472377eb15bd
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1q. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/webaudioeditor/controller.js b/devtools/client/webaudioeditor/controller.js
+--- a/devtools/client/webaudioeditor/controller.js
++++ b/devtools/client/webaudioeditor/controller.js
+@@ -38,17 +38,17 @@ function shutdownWebAudioEditor() {
+ 
+ /**
+  * Functions handling target-related lifetime events.
+  */
+ var WebAudioEditorController = {
+   /**
+    * Listen for events emitted by the current tab target.
+    */
+-  initialize: Task.async(function* () {
++  async initialize() {
+     this._onTabNavigated = this._onTabNavigated.bind(this);
+     this._onThemeChange = this._onThemeChange.bind(this);
+ 
+     gTarget.on("will-navigate", this._onTabNavigated);
+     gTarget.on("navigate", this._onTabNavigated);
+     gFront.on("start-context", this._onStartContext);
+     gFront.on("create-node", this._onCreateNode);
+     gFront.on("connect-node", this._onConnectNode);
+@@ -62,29 +62,29 @@ var WebAudioEditorController = {
+     // with CSS
+ 
+     this._prefObserver = new PrefObserver("");
+     this._prefObserver.on("devtools.theme", this._onThemeChange);
+ 
+     // Store the AudioNode definitions from the WebAudioFront, if the method exists.
+     // If not, get the JSON directly. Using the actor method is preferable so the client
+     // knows exactly what methods are supported on the server.
+-    let actorHasDefinition = yield gTarget.actorHasMethod("webaudio", "getDefinition");
++    let actorHasDefinition = await gTarget.actorHasMethod("webaudio", "getDefinition");
+     if (actorHasDefinition) {
+-      AUDIO_NODE_DEFINITION = yield gFront.getDefinition();
++      AUDIO_NODE_DEFINITION = await gFront.getDefinition();
+     } else {
+       AUDIO_NODE_DEFINITION = require("devtools/server/actors/utils/audionodes.json");
+     }
+ 
+     // Make sure the backend is prepared to handle audio contexts.
+     // Since actors are created lazily on the first request to them, we need to send an
+     // early request to ensure the CallWatcherActor is running and watching for new window
+     // globals.
+     gFront.setup({ reload: false });
+-  }),
++  },
+ 
+   /**
+    * Remove events emitted by the current tab target.
+    */
+   destroy: function () {
+     gTarget.off("will-navigate", this._onTabNavigated);
+     gTarget.off("navigate", this._onTabNavigated);
+     gFront.off("start-context", this._onStartContext);
+@@ -107,29 +107,29 @@ var WebAudioEditorController = {
+     ContextView.resetUI();
+     InspectorView.resetUI();
+     PropertiesView.resetUI();
+   },
+ 
+   // Since node events (create, disconnect, connect) are all async,
+   // we have to make sure to wait that the node has finished creating
+   // before performing an operation on it.
+-  getNode: function* (nodeActor) {
++  getNode: async function (nodeActor) {
+     let id = nodeActor.actorID;
+     let node = gAudioNodes.get(id);
+ 
+     if (!node) {
+       let { resolve, promise } = defer();
+       gAudioNodes.on("add", function createNodeListener(createdNode) {
+         if (createdNode.id === id) {
+           gAudioNodes.off("add", createNodeListener);
+           resolve(createdNode);
+         }
+       });
+-      node = yield promise;
++      node = await promise;
+     }
+     return node;
+   },
+ 
+   /**
+    * Fired when the devtools theme changes (light, dark, etc.)
+    * so that the graph can update marker styling, as that
+    * cannot currently be done with CSS.
+@@ -137,17 +137,17 @@ var WebAudioEditorController = {
+   _onThemeChange: function () {
+     let newValue = Services.prefs.getCharPref("devtools.theme");
+     window.emit(EVENTS.THEME_CHANGE, newValue);
+   },
+ 
+   /**
+    * Called for each location change in the debugged tab.
+    */
+-  _onTabNavigated: Task.async(function* (event, {isFrameSwitching}) {
++  async _onTabNavigated(event, {isFrameSwitching}) {
+     switch (event) {
+       case "will-navigate": {
+         // Clear out current UI.
+         this.reset();
+ 
+         // When switching to an iframe, ensure displaying the reload button.
+         // As the document has already been loaded without being hooked.
+         if (isFrameSwitching) {
+@@ -168,17 +168,17 @@ var WebAudioEditorController = {
+         break;
+       }
+       case "navigate": {
+         // TODO Case of bfcache, needs investigating
+         // bug 994250
+         break;
+       }
+     }
+-  }),
++  },
+ 
+   /**
+    * Called after the first audio node is created in an audio context,
+    * signaling that the audio context is being used.
+    */
+   _onStartContext: function () {
+     $("#reload-notice").hidden = true;
+     $("#waiting-notice").hidden = true;
+@@ -200,39 +200,39 @@ var WebAudioEditorController = {
+    */
+   _onDestroyNode: function (nodeActor) {
+     gAudioNodes.remove(gAudioNodes.get(nodeActor.actorID));
+   },
+ 
+   /**
+    * Called when a node is connected to another node.
+    */
+-  _onConnectNode: Task.async(function* ({ source: sourceActor, dest: destActor }) {
+-    let source = yield WebAudioEditorController.getNode(sourceActor);
+-    let dest = yield WebAudioEditorController.getNode(destActor);
++  async _onConnectNode({ source: sourceActor, dest: destActor }) {
++    let source = await WebAudioEditorController.getNode(sourceActor);
++    let dest = await WebAudioEditorController.getNode(destActor);
+     source.connect(dest);
+-  }),
++  },
+ 
+   /**
+    * Called when a node is conneceted to another node's AudioParam.
+    */
+-  _onConnectParam: Task.async(function* ({ source: sourceActor, dest: destActor, param }) {
+-    let source = yield WebAudioEditorController.getNode(sourceActor);
+-    let dest = yield WebAudioEditorController.getNode(destActor);
++  async _onConnectParam({ source: sourceActor, dest: destActor, param }) {
++    let source = await WebAudioEditorController.getNode(sourceActor);
++    let dest = await WebAudioEditorController.getNode(destActor);
+     source.connect(dest, param);
+-  }),
++  },
+ 
+   /**
+    * Called when a node is disconnected.
+    */
+-  _onDisconnectNode: Task.async(function* (nodeActor) {
+-    let node = yield WebAudioEditorController.getNode(nodeActor);
++  async _onDisconnectNode(nodeActor) {
++    let node = await WebAudioEditorController.getNode(nodeActor);
+     node.disconnect();
+-  }),
++  },
+ 
+   /**
+    * Called when a node param is changed.
+    */
+-  _onChangeParam: Task.async(function* ({ actor, param, value }) {
+-    let node = yield WebAudioEditorController.getNode(actor);
++  async _onChangeParam({ actor, param, value }) {
++    let node = await WebAudioEditorController.getNode(actor);
+     window.emit(EVENTS.CHANGE_PARAM, node, param, value);
+-  })
++  }
+ };
+diff --git a/devtools/client/webaudioeditor/includes.js b/devtools/client/webaudioeditor/includes.js
+--- a/devtools/client/webaudioeditor/includes.js
++++ b/devtools/client/webaudioeditor/includes.js
+@@ -1,16 +1,15 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+  * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ const { loader, require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+-const { Task } = require("devtools/shared/task");
+ const EventEmitter = require("devtools/shared/event-emitter");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ const Services = require("Services");
+ const { gDevTools } = require("devtools/client/framework/devtools");
+ const { LocalizationHelper } = require("devtools/shared/l10n");
+ const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");
+ 
+ // Use privileged promise in panel documents to prevent having them to freeze
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-add-automation-event.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-add-automation-event.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-add-automation-event.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-add-automation-event.js
+@@ -1,52 +1,52 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test AudioNode#addAutomationEvent();
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
+-  let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
++  let [_, [destNode, oscNode, gainNode]] = await Promise.all([
+     front.setup({ reload: true }),
+     get3(front, "create-node")
+   ]);
+   let count = 0;
+   let counter = () => count++;
+   front.on("automation-event", counter);
+ 
+   let t0 = 0, t1 = 0.1, t2 = 0.2, t3 = 0.3, t4 = 0.4, t5 = 0.6, t6 = 0.7, t7 = 1;
+   let curve = [-1, 0, 1];
+-  yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.2, t0]);
+-  yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.3, t1]);
+-  yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.4, t2]);
+-  yield oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [1, t3]);
+-  yield oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [0.15, t4]);
+-  yield oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [0.75, t5]);
+-  yield oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [0.5, t6]);
+-  yield oscNode.addAutomationEvent("frequency", "setValueCurveAtTime", [curve, t7, t7 - t6]);
+-  yield oscNode.addAutomationEvent("frequency", "setTargetAtTime", [20, 2, 5]);
++  await oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.2, t0]);
++  await oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.3, t1]);
++  await oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.4, t2]);
++  await oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [1, t3]);
++  await oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [0.15, t4]);
++  await oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [0.75, t5]);
++  await oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [0.5, t6]);
++  await oscNode.addAutomationEvent("frequency", "setValueCurveAtTime", [curve, t7, t7 - t6]);
++  await oscNode.addAutomationEvent("frequency", "setTargetAtTime", [20, 2, 5]);
+ 
+   ok(true, "successfully set automation events for valid automation events");
+ 
+   try {
+-    yield oscNode.addAutomationEvent("frequency", "notAMethod", 20, 2, 5);
++    await oscNode.addAutomationEvent("frequency", "notAMethod", 20, 2, 5);
+     ok(false, "non-automation methods should not be successful");
+   } catch (e) {
+     ok(/invalid/.test(e.message), "AudioNode:addAutomationEvent fails for invalid automation methods");
+   }
+ 
+   try {
+-    yield oscNode.addAutomationEvent("invalidparam", "setValueAtTime", 0.2, t0);
++    await oscNode.addAutomationEvent("invalidparam", "setValueAtTime", 0.2, t0);
+     ok(false, "automating non-AudioParams should not be successful");
+   } catch (e) {
+     ok(/invalid/.test(e.message), "AudioNode:addAutomationEvent fails for a non AudioParam");
+   }
+ 
+   front.off("automation-event", counter);
+ 
+   is(count, 9,
+     "when calling `addAutomationEvent`, the WebAudioActor should still fire `automation-event`.");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-bypass.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-bypass.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-bypass.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-bypass.js
+@@ -1,36 +1,36 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test AudioNode#bypass(), AudioNode#isBypassed()
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
+-  let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
++  let [_, [destNode, oscNode, gainNode]] = await Promise.all([
+     front.setup({ reload: true }),
+     get3(front, "create-node")
+   ]);
+ 
+-  is((yield gainNode.isBypassed()), false, "Nodes start off unbypassed.");
++  is((await gainNode.isBypassed()), false, "Nodes start off unbypassed.");
+ 
+   info("Calling node#bypass(true)");
+-  let isBypassed = yield gainNode.bypass(true);
++  let isBypassed = await gainNode.bypass(true);
+ 
+   is(isBypassed, true, "node.bypass(true) resolves to true");
+-  is((yield gainNode.isBypassed()), true, "Node is now bypassed.");
++  is((await gainNode.isBypassed()), true, "Node is now bypassed.");
+ 
+   info("Calling node#bypass(false)");
+-  isBypassed = yield gainNode.bypass(false);
++  isBypassed = await gainNode.bypass(false);
+ 
+   is(isBypassed, false, "node.bypass(false) resolves to false");
+-  is((yield gainNode.isBypassed()), false, "Node back to being unbypassed.");
++  is((await gainNode.isBypassed()), false, "Node back to being unbypassed.");
+ 
+   info("Calling node#bypass(true) on unbypassable node");
+-  isBypassed = yield destNode.bypass(true);
++  isBypassed = await destNode.bypass(true);
+ 
+   is(isBypassed, false, "node.bypass(true) resolves to false for unbypassable node");
+-  is((yield gainNode.isBypassed()), false, "Unbypassable node is unaffect");
++  is((await gainNode.isBypassed()), false, "Unbypassable node is unaffect");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-bypassable.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-bypassable.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-bypassable.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-bypassable.js
+@@ -1,18 +1,18 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test AudioNode#bypassable
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_NODES_URL);
+-  let [_, nodes] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_NODES_URL);
++  let [_, nodes] = await Promise.all([
+     front.setup({ reload: true }),
+     getN(front, "create-node", 14)
+   ]);
+ 
+   let actualBypassability = nodes.map(node => node.bypassable);
+   let expectedBypassability = [
+     false, // AudioDestinationNode
+     true, // AudioBufferSourceNode
+@@ -29,10 +29,10 @@ add_task(function* () {
+     true, // DynamicsCompressNode
+     true, // OscillatorNode
+   ];
+ 
+   expectedBypassability.forEach((bypassable, i) => {
+     is(actualBypassability[i], bypassable, `${nodes[i].type} has correct ".bypassable" status`);
+   });
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
+@@ -1,39 +1,39 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that AudioNodeActor#connectNode() and AudioNodeActor#disconnect() work.
+  * Uses the editor front as the actors do not retain connect state.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let [dest, osc, gain] = actors;
+ 
+   info("Disconnecting oscillator...");
+   osc.disconnect();
+-  yield Promise.all([
++  await Promise.all([
+     waitForGraphRendered(panelWin, 3, 1),
+     once(gAudioNodes, "disconnect")
+   ]);
+   ok(true, "Oscillator disconnected, event emitted.");
+ 
+   info("Reconnecting oscillator...");
+   osc.connectNode(gain);
+-  yield Promise.all([
++  await Promise.all([
+     waitForGraphRendered(panelWin, 3, 2),
+     once(gAudioNodes, "connect")
+   ]);
+   ok(true, "Oscillator reconnected.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-connectparam.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-connectparam.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-connectparam.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-connectparam.js
+@@ -1,32 +1,32 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that AudioNodeActor#connectParam() work.
+  * Uses the editor front as the actors do not retain connect state.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let [dest, osc, gain] = actors;
+ 
+-  yield osc.disconnect();
++  await osc.disconnect();
+ 
+   osc.connectParam(gain, "gain");
+-  yield Promise.all([
++  await Promise.all([
+     waitForGraphRendered(panelWin, 3, 1, 1),
+     once(gAudioNodes, "connect")
+   ]);
+   ok(true, "Oscillator connect to Gain's Gain AudioParam, event emitted.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-01.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-01.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-01.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-01.js
+@@ -1,37 +1,37 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test AudioNode#addAutomationEvent() checking automation values, also using
+  * a curve as the last event to check duration spread.
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
+-  let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
++  let [_, [destNode, oscNode, gainNode]] = await Promise.all([
+     front.setup({ reload: true }),
+     get3(front, "create-node")
+   ]);
+ 
+   let t0 = 0, t1 = 0.1, t2 = 0.2, t3 = 0.3, t4 = 0.4, t5 = 0.6, t6 = 0.7, t7 = 1;
+   let curve = [-1, 0, 1];
+-  yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.2, t0]);
+-  yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.3, t1]);
+-  yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.4, t2]);
+-  yield oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [1, t3]);
+-  yield oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [0.15, t4]);
+-  yield oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [0.75, t5]);
+-  yield oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [0.05, t6]);
++  await oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.2, t0]);
++  await oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.3, t1]);
++  await oscNode.addAutomationEvent("frequency", "setValueAtTime", [0.4, t2]);
++  await oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [1, t3]);
++  await oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [0.15, t4]);
++  await oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [0.75, t5]);
++  await oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [0.05, t6]);
+   // End with a curve here so we can get proper results on the last event (which takes into account
+   // duration)
+-  yield oscNode.addAutomationEvent("frequency", "setValueCurveAtTime", [curve, t6, t7 - t6]);
++  await oscNode.addAutomationEvent("frequency", "setValueCurveAtTime", [curve, t6, t7 - t6]);
+ 
+-  let { events, values } = yield oscNode.getAutomationData("frequency");
++  let { events, values } = await oscNode.getAutomationData("frequency");
+ 
+   is(events.length, 8, "8 recorded events returned.");
+   is(values.length, 2000, "2000 value points returned.");
+ 
+   checkAutomationValue(values, 0.05, 0.2);
+   checkAutomationValue(values, 0.1, 0.3);
+   checkAutomationValue(values, 0.15, 0.3);
+   checkAutomationValue(values, 0.2, 0.4);
+@@ -44,10 +44,10 @@ add_task(function* () {
+   checkAutomationValue(values, 0.55, 0.15 * Math.pow(0.75 / 0.15, 0.15 / 0.2));
+   checkAutomationValue(values, 0.6, 0.75);
+   checkAutomationValue(values, 0.65, 0.75 * Math.pow(0.05 / 0.75, 0.5));
+   checkAutomationValue(values, 0.705, -1); // Increase this time a bit to prevent off by the previous exponential amount
+   checkAutomationValue(values, 0.8, 0);
+   checkAutomationValue(values, 0.9, 1);
+   checkAutomationValue(values, 1, 1);
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-02.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-02.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-02.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-02.js
+@@ -1,42 +1,42 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test AudioNode#addAutomationEvent() when automation series ends with
+  * `setTargetAtTime`, which approaches its target to infinity.
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
+-  let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
++  let [_, [destNode, oscNode, gainNode]] = await Promise.all([
+     front.setup({ reload: true }),
+     get3(front, "create-node")
+   ]);
+ 
+-  yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [300, 0.1]);
+-  yield oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [500, 0.4]);
+-  yield oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [200, 0.6]);
++  await oscNode.addAutomationEvent("frequency", "setValueAtTime", [300, 0.1]);
++  await oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [500, 0.4]);
++  await oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [200, 0.6]);
+   // End with a setTargetAtTime event, as the target approaches infinity, which will
+   // give us more points to render than the default 2000
+-  yield oscNode.addAutomationEvent("frequency", "setTargetAtTime", [1000, 2, 0.5]);
++  await oscNode.addAutomationEvent("frequency", "setTargetAtTime", [1000, 2, 0.5]);
+ 
+-  var { events, values } = yield oscNode.getAutomationData("frequency");
++  var { events, values } = await oscNode.getAutomationData("frequency");
+ 
+   is(events.length, 4, "4 recorded events returned.");
+   is(values.length, 4000, "4000 value points returned when ending with exponentiall approaching automator.");
+ 
+   checkAutomationValue(values, 2.01, 215.055);
+   checkAutomationValue(values, 2.1, 345.930);
+   checkAutomationValue(values, 3, 891.601);
+   checkAutomationValue(values, 5, 998.01);
+ 
+   // Refetch the automation data to ensure it recalculates correctly (bug 1118071)
+-  var { events, values } = yield oscNode.getAutomationData("frequency");
++  var { events, values } = await oscNode.getAutomationData("frequency");
+ 
+   checkAutomationValue(values, 2.01, 215.055);
+   checkAutomationValue(values, 2.1, 345.930);
+   checkAutomationValue(values, 3, 891.601);
+   checkAutomationValue(values, 5, 998.01);
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-03.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-03.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-03.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-03.js
+@@ -1,34 +1,34 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test that `cancelScheduledEvents` clears out events on and after
+  * its argument.
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
+-  let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
++  let [_, [destNode, oscNode, gainNode]] = await Promise.all([
+     front.setup({ reload: true }),
+     get3(front, "create-node")
+   ]);
+ 
+-  yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [300, 0]);
+-  yield oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [500, 0.9]);
+-  yield oscNode.addAutomationEvent("frequency", "setValueAtTime", [700, 1]);
+-  yield oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [1000, 2]);
+-  yield oscNode.addAutomationEvent("frequency", "cancelScheduledValues", [1]);
++  await oscNode.addAutomationEvent("frequency", "setValueAtTime", [300, 0]);
++  await oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [500, 0.9]);
++  await oscNode.addAutomationEvent("frequency", "setValueAtTime", [700, 1]);
++  await oscNode.addAutomationEvent("frequency", "exponentialRampToValueAtTime", [1000, 2]);
++  await oscNode.addAutomationEvent("frequency", "cancelScheduledValues", [1]);
+ 
+-  var { events, values } = yield oscNode.getAutomationData("frequency");
++  var { events, values } = await oscNode.getAutomationData("frequency");
+ 
+   is(events.length, 2, "2 recorded events returned.");
+   is(values.length, 2000, "2000 value points returned");
+ 
+   checkAutomationValue(values, 0, 300);
+   checkAutomationValue(values, 0.5, 411.15);
+   checkAutomationValue(values, 0.9, 499.9);
+   checkAutomationValue(values, 1, 499.9);
+   checkAutomationValue(values, 2, 499.9);
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-param-flags.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
+@@ -1,47 +1,47 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test AudioNode#getParamFlags()
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_NODES_URL);
+-  let [_, nodes] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_NODES_URL);
++  let [_, nodes] = await Promise.all([
+     front.setup({ reload: true }),
+     getN(front, "create-node", 15)
+   ]);
+ 
+-  let allNodeParams = yield Promise.all(nodes.map(node => node.getParams()));
++  let allNodeParams = await Promise.all(nodes.map(node => node.getParams()));
+   let nodeTypes = [
+     "AudioDestinationNode",
+     "AudioBufferSourceNode", "ScriptProcessorNode", "AnalyserNode", "GainNode",
+     "DelayNode", "BiquadFilterNode", "WaveShaperNode", "PannerNode", "ConvolverNode",
+     "ChannelSplitterNode", "ChannelMergerNode", "DynamicsCompressorNode", "OscillatorNode",
+     "StereoPannerNode"
+   ];
+ 
+   // For some reason nodeTypes.forEach and params.forEach fail here so we use
+   // simple for loops.
+   for (let i = 0; i < nodeTypes.length; i++) {
+     let type = nodeTypes[i];
+     let params = allNodeParams[i];
+ 
+     for (let {param, value, flags} of params) {
+-      let testFlags = yield nodes[i].getParamFlags(param);
++      let testFlags = await nodes[i].getParamFlags(param);
+       ok(typeof testFlags === "object", type + " has flags from #getParamFlags(" + param + ")");
+ 
+       if (param === "buffer") {
+         is(flags.Buffer, true, "`buffer` params have Buffer flag");
+       }
+       else if (param === "bufferSize" || param === "frequencyBinCount") {
+         is(flags.readonly, true, param + " is readonly");
+       }
+       else if (param === "curve") {
+         is(flags["Float32Array"], true, "`curve` param has Float32Array flag");
+       }
+     }
+   }
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-01.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-01.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-01.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-01.js
+@@ -1,34 +1,34 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test AudioNode#getParams()
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_NODES_URL);
+-  let [_, nodes] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_NODES_URL);
++  let [_, nodes] = await Promise.all([
+     front.setup({ reload: true }),
+     getN(front, "create-node", 15)
+   ]);
+ 
+-  yield loadFrameScriptUtils();
++  await loadFrameScriptUtils();
+ 
+-  let allNodeParams = yield Promise.all(nodes.map(node => node.getParams()));
++  let allNodeParams = await Promise.all(nodes.map(node => node.getParams()));
+   let nodeTypes = [
+     "AudioDestinationNode",
+     "AudioBufferSourceNode", "ScriptProcessorNode", "AnalyserNode", "GainNode",
+     "DelayNode", "BiquadFilterNode", "WaveShaperNode", "PannerNode", "ConvolverNode",
+     "ChannelSplitterNode", "ChannelMergerNode", "DynamicsCompressorNode", "OscillatorNode",
+     "StereoPannerNode"
+   ];
+ 
+-  let defaults = yield Promise.all(nodeTypes.map(type => nodeDefaultValues(type)));
++  let defaults = await Promise.all(nodeTypes.map(type => nodeDefaultValues(type)));
+ 
+   nodeTypes.map((type, i) => {
+     let params = allNodeParams[i];
+ 
+     params.forEach(({param, value, flags}) => {
+       ok(param in defaults[i], "expected parameter for " + type);
+ 
+       ok(typeof flags === "object", type + " has a flags object");
+@@ -40,10 +40,10 @@ add_task(function* () {
+         is(flags.readonly, true, param + " is readonly");
+       }
+       else if (param === "curve") {
+         is(flags["Float32Array"], true, "`curve` param has Float32Array flag");
+       }
+     });
+   });
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-02.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-02.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-02.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-02.js
+@@ -1,42 +1,42 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that default properties are returned with the correct type
+  * from the AudioNode actors.
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_NODES_URL);
+-  let [_, nodes] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_NODES_URL);
++  let [_, nodes] = await Promise.all([
+     front.setup({ reload: true }),
+     getN(front, "create-node", 15)
+   ]);
+ 
+-  yield loadFrameScriptUtils();
++  await loadFrameScriptUtils();
+ 
+-  let allParams = yield Promise.all(nodes.map(node => node.getParams()));
++  let allParams = await Promise.all(nodes.map(node => node.getParams()));
+   let types = [
+     "AudioDestinationNode", "AudioBufferSourceNode", "ScriptProcessorNode",
+     "AnalyserNode", "GainNode", "DelayNode", "BiquadFilterNode", "WaveShaperNode",
+     "PannerNode", "ConvolverNode", "ChannelSplitterNode", "ChannelMergerNode",
+     "DynamicsCompressorNode", "OscillatorNode", "StereoPannerNode"
+   ];
+ 
+-  let defaults = yield Promise.all(types.map(type => nodeDefaultValues(type)));
++  let defaults = await Promise.all(types.map(type => nodeDefaultValues(type)));
+ 
+   info(JSON.stringify(defaults));
+ 
+   allParams.forEach((params, i) => {
+     compare(params, defaults[i], types[i]);
+   });
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+ 
+ function compare(actual, expected, type) {
+   actual.forEach(({ value, param }) => {
+     value = getGripValue(value);
+     if (typeof expected[param] === "function") {
+       ok(expected[param](value), type + " has a passing value for " + param);
+     }
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-set-param.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-set-param.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-set-param.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-set-param.js
+@@ -1,47 +1,47 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test AudioNode#getParam() / AudioNode#setParam()
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
+-  let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
++  let [_, [destNode, oscNode, gainNode]] = await Promise.all([
+     front.setup({ reload: true }),
+     get3(front, "create-node")
+   ]);
+ 
+-  let freq = yield oscNode.getParam("frequency");
++  let freq = await oscNode.getParam("frequency");
+   info(typeof freq);
+   is(freq, 440, "AudioNode:getParam correctly fetches AudioParam");
+ 
+-  let type = yield oscNode.getParam("type");
++  let type = await oscNode.getParam("type");
+   is(type, "sine", "AudioNode:getParam correctly fetches non-AudioParam");
+ 
+-  type = yield oscNode.getParam("not-a-valid-param");
++  type = await oscNode.getParam("not-a-valid-param");
+   ok(type.type === "undefined",
+     "AudioNode:getParam correctly returns a grip value for `undefined` for an invalid param.");
+ 
+-  let resSuccess = yield oscNode.setParam("frequency", 220);
+-  freq = yield oscNode.getParam("frequency");
++  let resSuccess = await oscNode.setParam("frequency", 220);
++  freq = await oscNode.getParam("frequency");
+   is(freq, 220, "AudioNode:setParam correctly sets a `number` AudioParam");
+   is(resSuccess, undefined, "AudioNode:setParam returns undefined for correctly set AudioParam");
+ 
+-  resSuccess = yield oscNode.setParam("type", "square");
+-  type = yield oscNode.getParam("type");
++  resSuccess = await oscNode.setParam("type", "square");
++  type = await oscNode.getParam("type");
+   is(type, "square", "AudioNode:setParam correctly sets a `string` non-AudioParam");
+   is(resSuccess, undefined, "AudioNode:setParam returns undefined for correctly set AudioParam");
+ 
+   try {
+-    yield oscNode.setParam("frequency", "hello");
++    await oscNode.setParam("frequency", "hello");
+     ok(false, "setParam with invalid types should throw");
+   } catch (e) {
+     ok(/is not a finite floating-point/.test(e.message), "AudioNode:setParam returns error with correct message when attempting an invalid assignment");
+     is(e.type, "TypeError", "AudioNode:setParam returns error with correct type when attempting an invalid assignment");
+-    freq = yield oscNode.getParam("frequency");
++    freq = await oscNode.getParam("frequency");
+     is(freq, 220, "AudioNode:setParam does not modify value when an error occurs");
+   }
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-source.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-source.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-source.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-source.js
+@@ -1,27 +1,27 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test AudioNode#source
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_NODES_URL);
+-  let [_, nodes] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_NODES_URL);
++  let [_, nodes] = await Promise.all([
+     front.setup({ reload: true }),
+     getN(front, "create-node", 14)
+   ]);
+ 
+   let actualTypes = nodes.map(node => node.type);
+   let isSourceResult = nodes.map(node => node.source);
+ 
+   actualTypes.forEach((type, i) => {
+     let shouldBeSource = type === "AudioBufferSourceNode" || type === "OscillatorNode";
+     if (shouldBeSource)
+       is(isSourceResult[i], true, type + "'s `source` is `true`");
+     else
+       is(isSourceResult[i], false, type + "'s `source` is `false`");
+   });
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_audionode-actor-type.js b/devtools/client/webaudioeditor/test/browser_audionode-actor-type.js
+--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-type.js
++++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-type.js
+@@ -1,28 +1,28 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test AudioNode#type
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_NODES_URL);
+-  let [_, nodes] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_NODES_URL);
++  let [_, nodes] = await Promise.all([
+     front.setup({ reload: true }),
+     getN(front, "create-node", 14)
+   ]);
+ 
+   let actualTypes = nodes.map(node => node.type);
+   let expectedTypes = [
+     "AudioDestinationNode",
+     "AudioBufferSourceNode", "ScriptProcessorNode", "AnalyserNode", "GainNode",
+     "DelayNode", "BiquadFilterNode", "WaveShaperNode", "PannerNode", "ConvolverNode",
+     "ChannelSplitterNode", "ChannelMergerNode", "DynamicsCompressorNode", "OscillatorNode"
+   ];
+ 
+   expectedTypes.forEach((type, i) => {
+     is(actualTypes[i], type, type + " successfully created with correct type");
+   });
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_callwatcher-01.js b/devtools/client/webaudioeditor/test/browser_callwatcher-01.js
+--- a/devtools/client/webaudioeditor/test/browser_callwatcher-01.js
++++ b/devtools/client/webaudioeditor/test/browser_callwatcher-01.js
+@@ -5,22 +5,22 @@
+  * Bug 1130901
+  * Tests to ensure that calling call/apply on methods wrapped
+  * via CallWatcher do not throw a security permissions error:
+  * "Error: Permission denied to access property 'call'"
+  */
+ 
+ const BUG_1130901_URL = EXAMPLE_URL + "doc_bug_1130901.html";
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(BUG_1130901_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(BUG_1130901_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
+ 
+   let rendered = waitForGraphRendered(panelWin, 3, 0);
+   reload(target);
+-  yield rendered;
++  await rendered;
+ 
+   ok(true, "Successfully created a node from AudioContext via `call`.");
+   ok(true, "Successfully created a node from AudioContext via `apply`.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_callwatcher-02.js b/devtools/client/webaudioeditor/test/browser_callwatcher-02.js
+--- a/devtools/client/webaudioeditor/test/browser_callwatcher-02.js
++++ b/devtools/client/webaudioeditor/test/browser_callwatcher-02.js
+@@ -4,41 +4,41 @@
+ /**
+  * Bug 1112378
+  * Tests to ensure that errors called on wrapped functions via call-watcher
+  * correctly looks like the error comes from the content, not from within the devtools.
+  */
+ 
+ const BUG_1112378_URL = EXAMPLE_URL + "doc_bug_1112378.html";
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(BUG_1112378_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(BUG_1112378_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
+ 
+   loadFrameScriptUtils();
+ 
+   let rendered = waitForGraphRendered(panelWin, 2, 0);
+   reload(target);
+-  yield rendered;
++  await rendered;
+ 
+-  let error = yield evalInDebuggee("throwError()");
++  let error = await evalInDebuggee("throwError()");
+   is(error.lineNumber, 21, "error has correct lineNumber");
+   is(error.columnNumber, 11, "error has correct columnNumber");
+   is(error.name, "TypeError", "error has correct name");
+   is(error.message, "Argument 1 is not valid for any of the 2-argument overloads of AudioNode.connect.", "error has correct message");
+   is(error.stringified, "TypeError: Argument 1 is not valid for any of the 2-argument overloads of AudioNode.connect.", "error is stringified correctly");
+   is(error.instanceof, true, "error is correctly an instanceof TypeError");
+   is(error.fileName, "http://example.com/browser/devtools/client/webaudioeditor/test/doc_bug_1112378.html", "error has correct fileName");
+ 
+-  error = yield evalInDebuggee("throwDOMException()");
++  error = await evalInDebuggee("throwDOMException()");
+   is(error.lineNumber, 37, "exception has correct lineNumber");
+   is(error.columnNumber, 0, "exception has correct columnNumber");
+   is(error.code, 9, "exception has correct code");
+   is(error.result, 2152923145, "exception has correct result");
+   is(error.name, "NotSupportedError", "exception has correct name");
+   is(error.message, "Operation is not supported", "exception has correct message");
+   is(error.stringified, "NotSupportedError: Operation is not supported", "exception is stringified correctly");
+   is(error.instanceof, true, "exception is correctly an instance of DOMException");
+   is(error.filename, "http://example.com/browser/devtools/client/webaudioeditor/test/doc_bug_1112378.html", "exception has correct filename");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_automation-view-01.js b/devtools/client/webaudioeditor/test/browser_wa_automation-view-01.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_automation-view-01.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_automation-view-01.js
+@@ -1,57 +1,57 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that automation view shows the correct view depending on if events
+  * or params exist.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(AUTOMATION_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(AUTOMATION_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS } = panelWin;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   let $tabbox = $("#web-audio-editor-tabs");
+ 
+   // Oscillator node
+   click(panelWin, findGraphNode(panelWin, nodeIds[1]));
+-  yield waitForInspectorRender(panelWin, EVENTS);
++  await waitForInspectorRender(panelWin, EVENTS);
+   $tabbox.selectedIndex = 1;
+ 
+   ok(isVisible($("#automation-graph-container")), "graph container should be visible");
+   ok(isVisible($("#automation-content")), "automation content should be visible");
+   ok(!isVisible($("#automation-no-events")), "no-events panel should not be visible");
+   ok(!isVisible($("#automation-empty")), "empty panel should not be visible");
+ 
+   // Gain node
+   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
+-  yield waitForInspectorRender(panelWin, EVENTS);
++  await waitForInspectorRender(panelWin, EVENTS);
+   $tabbox.selectedIndex = 1;
+ 
+   ok(!isVisible($("#automation-graph-container")), "graph container should not be visible");
+   ok(isVisible($("#automation-content")), "automation content should be visible");
+   ok(isVisible($("#automation-no-events")), "no-events panel should be visible");
+   ok(!isVisible($("#automation-empty")), "empty panel should not be visible");
+ 
+   // destination node
+   click(panelWin, findGraphNode(panelWin, nodeIds[0]));
+-  yield waitForInspectorRender(panelWin, EVENTS);
++  await waitForInspectorRender(panelWin, EVENTS);
+   $tabbox.selectedIndex = 1;
+ 
+   ok(!isVisible($("#automation-graph-container")), "graph container should not be visible");
+   ok(!isVisible($("#automation-content")), "automation content should not be visible");
+   ok(!isVisible($("#automation-no-events")), "no-events panel should not be visible");
+   ok(isVisible($("#automation-empty")), "empty panel should be visible");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_automation-view-02.js b/devtools/client/webaudioeditor/test/browser_wa_automation-view-02.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_automation-view-02.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_automation-view-02.js
+@@ -1,55 +1,55 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that automation view selects the first parameter by default and
+  * switching between AudioParam rerenders the graph.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(AUTOMATION_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(AUTOMATION_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, AutomationView } = panelWin;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   // Oscillator node
+   click(panelWin, findGraphNode(panelWin, nodeIds[1]));
+-  yield waitForInspectorRender(panelWin, EVENTS);
++  await waitForInspectorRender(panelWin, EVENTS);
+   click(panelWin, $("#automation-tab"));
+ 
+   ok(AutomationView._selectedParamName, "frequency",
+     "AutomatioView is set on 'frequency'");
+   ok($(".automation-param-button[data-param='frequency']").getAttribute("selected"),
+     "frequency param should be selected on load");
+   ok(!$(".automation-param-button[data-param='detune']").getAttribute("selected"),
+     "detune param should not be selected on load");
+   ok(isVisible($("#automation-content")), "automation content should be visible");
+   ok(isVisible($("#automation-graph-container")), "graph container should be visible");
+   ok(!isVisible($("#automation-no-events")), "no-events panel should not be visible");
+ 
+   click(panelWin, $(".automation-param-button[data-param='detune']"));
+-  yield once(panelWin, EVENTS.UI_AUTOMATION_TAB_RENDERED);
++  await once(panelWin, EVENTS.UI_AUTOMATION_TAB_RENDERED);
+ 
+   ok(true, "automation tab rerendered");
+ 
+   ok(AutomationView._selectedParamName, "detune",
+     "AutomatioView is set on 'detune'");
+   ok(!$(".automation-param-button[data-param='frequency']").getAttribute("selected"),
+     "frequency param should not be selected after clicking detune");
+   ok($(".automation-param-button[data-param='detune']").getAttribute("selected"),
+     "detune param should be selected after clicking detune");
+   ok(isVisible($("#automation-content")), "automation content should be visible");
+   ok(!isVisible($("#automation-graph-container")), "graph container should not be visible");
+   ok(isVisible($("#automation-no-events")), "no-events panel should be visible");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_controller-01.js b/devtools/client/webaudioeditor/test/browser_wa_controller-01.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_controller-01.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_controller-01.js
+@@ -4,25 +4,25 @@
+ /**
+  * Bug 1125817
+  * Tests to ensure that disconnecting a node immediately
+  * after creating it does not fail.
+  */
+ 
+ const BUG_1125817_URL = EXAMPLE_URL + "doc_bug_1125817.html";
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(BUG_1125817_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(BUG_1125817_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
+ 
+   let events = Promise.all([
+     once(gAudioNodes, "add", 2),
+     once(gAudioNodes, "disconnect"),
+     waitForGraphRendered(panelWin, 2, 0)
+   ]);
+   reload(target);
+-  yield events;
++  await events;
+ 
+   ok(true, "Successfully disconnected a just-created node.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_destroy-node-01.js b/devtools/client/webaudioeditor/test/browser_wa_destroy-node-01.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_destroy-node-01.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_destroy-node-01.js
+@@ -4,44 +4,44 @@
+ /**
+  * Tests that the destruction node event is fired and that the nodes are no
+  * longer stored internally in the tool, that the graph is updated properly, and
+  * that selecting a soon-to-be dead node clears the inspector.
+  *
+  * All done in one test since this test takes a few seconds to clear GC.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(DESTROY_NODES_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(DESTROY_NODES_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, gAudioNodes } = panelWin;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     getNSpread(gAudioNodes, "add", 13),
+     waitForGraphRendered(panelWin, 13, 2)
+   ]);
+   reload(target);
+-  let [created] = yield events;
++  let [created] = await events;
+ 
+   // Flatten arrays of event arguments and take the first (AudioNodeModel)
+   // and get its ID.
+   let actorIDs = created.map(ev => ev[0].id);
+ 
+   // Click a soon-to-be dead buffer node
+-  yield clickGraphNode(panelWin, actorIDs[5]);
++  await clickGraphNode(panelWin, actorIDs[5]);
+ 
+   let destroyed = getN(gAudioNodes, "remove", 10);
+ 
+   // Force a CC in the child process to collect the orphaned nodes.
+   forceNodeCollection();
+ 
+   // Wait for destruction and graph to re-render
+-  yield Promise.all([destroyed, waitForGraphRendered(panelWin, 3, 2)]);
++  await Promise.all([destroyed, waitForGraphRendered(panelWin, 3, 2)]);
+ 
+   // Test internal storage
+   is(panelWin.gAudioNodes.length, 3, "All nodes should be GC'd except one gain, osc and dest node.");
+ 
+   // Test graph rendering
+   ok(findGraphNode(panelWin, actorIDs[0]), "dest should be in graph");
+   ok(findGraphNode(panelWin, actorIDs[1]), "osc should be in graph");
+   ok(findGraphNode(panelWin, actorIDs[2]), "gain should be in graph");
+@@ -50,10 +50,10 @@ add_task(function* () {
+ 
+   is(nodes, 3, "Only 3 nodes rendered in graph.");
+   is(edges, 2, "Only 2 edges rendered in graph.");
+ 
+   // Test that the inspector reset to no node selected
+   ok(isVisible($("#web-audio-editor-details-pane-empty")),
+     "InspectorView empty message should show if the currently selected node gets collected.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_first-run.js b/devtools/client/webaudioeditor/test/browser_wa_first-run.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_first-run.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_first-run.js
+@@ -1,43 +1,43 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the reloading/onContentLoaded hooks work.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { gFront, $ } = panel.panelWin;
+ 
+   is($("#reload-notice").hidden, false,
+     "The 'reload this page' notice should initially be visible.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for an audio context' notice should initially be hidden.");
+   is($("#content").hidden, true,
+     "The tool's content should initially be hidden.");
+ 
+   let navigating = once(target, "will-navigate");
+   let started = once(gFront, "start-context");
+ 
+   reload(target);
+ 
+-  yield navigating;
++  await navigating;
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should be hidden when navigating.");
+   is($("#waiting-notice").hidden, false,
+     "The 'waiting for an audio context' notice should be visible when navigating.");
+   is($("#content").hidden, true,
+     "The tool's content should still be hidden.");
+ 
+-  yield started;
++  await started;
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should be hidden after context found.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for an audio context' notice should be hidden after context found.");
+   is($("#content").hidden, false,
+     "The tool's content should not be hidden anymore.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_graph-click.js b/devtools/client/webaudioeditor/test/browser_wa_graph-click.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_graph-click.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_graph-click.js
+@@ -1,49 +1,49 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the clicking on a node in the GraphView opens and sets
+  * the correct node in the InspectorView
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(COMPLEX_CONTEXT_URL);
+   let panelWin = panel.panelWin;
+   let { gFront, $, $$, InspectorView } = panelWin;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     getN(gFront, "create-node", 8),
+     waitForGraphRendered(panel.panelWin, 8, 8)
+   ]);
+   reload(target);
+-  let [actors, _] = yield events;
++  let [actors, _] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   ok(!InspectorView.isVisible(), "InspectorView hidden on start.");
+ 
+-  yield clickGraphNode(panelWin, nodeIds[1], true);
++  await clickGraphNode(panelWin, nodeIds[1], true);
+ 
+   ok(InspectorView.isVisible(), "InspectorView visible after selecting a node.");
+   is(InspectorView.getCurrentAudioNode().id, nodeIds[1], "InspectorView has correct node set.");
+ 
+-  yield clickGraphNode(panelWin, nodeIds[2]);
++  await clickGraphNode(panelWin, nodeIds[2]);
+ 
+   ok(InspectorView.isVisible(), "InspectorView still visible after selecting another node.");
+   is(InspectorView.getCurrentAudioNode().id, nodeIds[2], "InspectorView has correct node set on second node.");
+ 
+-  yield clickGraphNode(panelWin, nodeIds[2]);
++  await clickGraphNode(panelWin, nodeIds[2]);
+   is(InspectorView.getCurrentAudioNode().id, nodeIds[2], "Clicking the same node again works (idempotent).");
+ 
+-  yield clickGraphNode(panelWin, $("rect", findGraphNode(panelWin, nodeIds[3])));
++  await clickGraphNode(panelWin, $("rect", findGraphNode(panelWin, nodeIds[3])));
+   is(InspectorView.getCurrentAudioNode().id, nodeIds[3], "Clicking on a <rect> works as expected.");
+ 
+-  yield clickGraphNode(panelWin, $("tspan", findGraphNode(panelWin, nodeIds[4])));
++  await clickGraphNode(panelWin, $("tspan", findGraphNode(panelWin, nodeIds[4])));
+   is(InspectorView.getCurrentAudioNode().id, nodeIds[4], "Clicking on a <tspan> works as expected.");
+ 
+   ok(InspectorView.isVisible(),
+     "InspectorView still visible after several nodes have been clicked.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_graph-markers.js b/devtools/client/webaudioeditor/test/browser_wa_graph-markers.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_graph-markers.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_graph-markers.js
+@@ -2,34 +2,34 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the SVG marker styling is updated when devtools theme changes.
+  */
+ 
+ const { setTheme } = require("devtools/client/shared/theme");
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, MARKER_STYLING } = panelWin;
+ 
+   let currentTheme = Services.prefs.getCharPref("devtools.theme");
+ 
+   ok(MARKER_STYLING.light, "Marker styling exists for light theme.");
+   ok(MARKER_STYLING.dark, "Marker styling exists for dark theme.");
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+ 
+   is(getFill($("#arrowhead")), MARKER_STYLING[currentTheme],
+     "marker initially matches theme.");
+ 
+   // Switch to light
+   setTheme("light");
+   is(getFill($("#arrowhead")), MARKER_STYLING.light,
+     "marker styling matches light theme on change.");
+@@ -44,17 +44,17 @@ add_task(function* () {
+   is(getFill($("#arrowhead")), MARKER_STYLING.dark,
+     "marker styling remains dark.");
+ 
+   // Switch to back to light again
+   setTheme("light");
+   is(getFill($("#arrowhead")), MARKER_STYLING.light,
+     "marker styling switches back to light once again.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+ 
+ /**
+  * Returns a hex value found in styling for an element. So parses
+  * <marker style="fill: #abcdef"> and returns "#abcdef"
+  */
+ function getFill(el) {
+   return el.getAttribute("style").match(/(#.*)$/)[1];
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_graph-render-01.js b/devtools/client/webaudioeditor/test/browser_wa_graph-render-01.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-01.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-01.js
+@@ -2,43 +2,43 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that SVG nodes and edges were created for the Graph View.
+  */
+ 
+ var connectCount = 0;
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
+ 
+   let started = once(gFront, "start-context");
+ 
+   gAudioNodes.on("connect", onConnectNode);
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let [destId, oscId, gainId] = actors.map(actor => actor.actorID);
+ 
+   ok(findGraphNode(panelWin, oscId).classList.contains("type-OscillatorNode"), "found OscillatorNode with class");
+   ok(findGraphNode(panelWin, gainId).classList.contains("type-GainNode"), "found GainNode with class");
+   ok(findGraphNode(panelWin, destId).classList.contains("type-AudioDestinationNode"), "found AudioDestinationNode with class");
+   is(findGraphEdge(panelWin, oscId, gainId).toString(), "[object SVGGElement]", "found edge for osc -> gain");
+   is(findGraphEdge(panelWin, gainId, destId).toString(), "[object SVGGElement]", "found edge for gain -> dest");
+ 
+-  yield wait(1000);
++  await wait(1000);
+ 
+   is(connectCount, 2, "Only two node connect events should be fired.");
+ 
+   gAudioNodes.off("connect", onConnectNode);
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+ 
+ function onConnectNode() {
+   ++connectCount;
+ }
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_graph-render-02.js b/devtools/client/webaudioeditor/test/browser_wa_graph-render-02.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-02.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-02.js
+@@ -1,28 +1,28 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests more edge rendering for complex graphs.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(COMPLEX_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$ } = panelWin;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     getN(gFront, "create-node", 8),
+     waitForGraphRendered(panelWin, 8, 8)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIDs = actors.map(actor => actor.actorID);
+ 
+   let types = ["AudioDestinationNode", "OscillatorNode", "GainNode", "ScriptProcessorNode",
+                "OscillatorNode", "GainNode", "AudioBufferSourceNode", "BiquadFilterNode"];
+ 
+ 
+   types.forEach((type, i) => {
+     ok(findGraphNode(panelWin, nodeIDs[i]).classList.contains("type-" + type), "found " + type + " with class");
+@@ -39,10 +39,10 @@ add_task(function* () {
+     [7, 0, "filter -> dest"],
+   ];
+ 
+   edges.forEach(([source, target, msg], i) => {
+     is(findGraphEdge(panelWin, nodeIDs[source], nodeIDs[target]).toString(), "[object SVGGElement]",
+       "found edge for " + msg);
+   });
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_graph-render-03.js b/devtools/client/webaudioeditor/test/browser_wa_graph-render-03.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-03.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-03.js
+@@ -1,34 +1,34 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests to ensure that selected nodes stay selected on graph redraw.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS } = panelWin;
+ 
+   let events = Promise.all([
+     getN(gFront, "create-node", 3),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let [dest, osc, gain] = actors;
+ 
+-  yield clickGraphNode(panelWin, gain.actorID);
++  await clickGraphNode(panelWin, gain.actorID);
+   ok(findGraphNode(panelWin, gain.actorID).classList.contains("selected"),
+     "Node selected once.");
+ 
+   // Disconnect a node to trigger a rerender
+   osc.disconnect();
+ 
+-  yield once(panelWin, EVENTS.UI_GRAPH_RENDERED);
++  await once(panelWin, EVENTS.UI_GRAPH_RENDERED);
+ 
+   ok(findGraphNode(panelWin, gain.actorID).classList.contains("selected"),
+     "Node still selected after rerender.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_graph-render-04.js b/devtools/client/webaudioeditor/test/browser_wa_graph-render-04.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-04.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-04.js
+@@ -1,37 +1,37 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests audio param connection rendering.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(CONNECT_MULTI_PARAM_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(CONNECT_MULTI_PARAM_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS } = panelWin;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     getN(gFront, "create-node", 5),
+     waitForGraphRendered(panelWin, 5, 2, 3)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIDs = actors.map(actor => actor.actorID);
+ 
+   let [, carrier, gain, mod1, mod2] = nodeIDs;
+ 
+   let edges = [
+     [mod1, gain, "gain", "mod1 -> gain[gain]"],
+     [mod2, carrier, "frequency", "mod2 -> carrier[frequency]"],
+     [mod2, carrier, "detune", "mod2 -> carrier[detune]"]
+   ];
+ 
+   edges.forEach(([source, target, param, msg], i) => {
+     let edge = findGraphEdge(panelWin, source, target, param);
+     ok(edge.classList.contains("param-connection"), "edge is classified as a param-connection");
+   });
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_graph-render-05.js b/devtools/client/webaudioeditor/test/browser_wa_graph-render-05.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-05.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-05.js
+@@ -1,28 +1,28 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests to ensure that param connections trigger graph redraws
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS } = panelWin;
+ 
+   let events = Promise.all([
+     getN(gFront, "create-node", 3),
+     waitForGraphRendered(panelWin, 3, 2, 0)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let [dest, osc, gain] = actors;
+ 
+-  yield osc.disconnect();
++  await osc.disconnect();
+ 
+   osc.connectParam(gain, "gain");
+-  yield waitForGraphRendered(panelWin, 3, 1, 1);
++  await waitForGraphRendered(panelWin, 3, 1, 1);
+   ok(true, "Graph re-rendered upon param connection");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_graph-render-06.js b/devtools/client/webaudioeditor/test/browser_wa_graph-render-06.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-06.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-06.js
+@@ -2,24 +2,24 @@
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests to ensure that param connections trigger graph redraws
+  */
+ 
+ const BUG_1141261_URL = EXAMPLE_URL + "doc_bug_1141261.html";
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(BUG_1141261_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(BUG_1141261_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS } = panelWin;
+ 
+   let events = Promise.all([
+     getN(gFront, "create-node", 3),
+     waitForGraphRendered(panelWin, 3, 1, 0)
+   ]);
+   reload(target);
+-  yield events;
++  await events;
+ 
+   ok(true, "Graph correctly shows gain node as disconnected");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_graph-selected.js b/devtools/client/webaudioeditor/test/browser_wa_graph-selected.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_graph-selected.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_graph-selected.js
+@@ -1,49 +1,49 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that SVG nodes and edges were created for the Graph View.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS } = panelWin;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let [destId, oscId, gainId] = actors.map(actor => actor.actorID);
+ 
+   ok(!findGraphNode(panelWin, destId).classList.contains("selected"),
+     "No nodes selected on start. (destination)");
+   ok(!findGraphNode(panelWin, oscId).classList.contains("selected"),
+     "No nodes selected on start. (oscillator)");
+   ok(!findGraphNode(panelWin, gainId).classList.contains("selected"),
+     "No nodes selected on start. (gain)");
+ 
+-  yield clickGraphNode(panelWin, oscId);
++  await clickGraphNode(panelWin, oscId);
+ 
+   ok(findGraphNode(panelWin, oscId).classList.contains("selected"),
+     "Selected node has class 'selected'.");
+   ok(!findGraphNode(panelWin, destId).classList.contains("selected"),
+     "Non-selected nodes do not have class 'selected'.");
+   ok(!findGraphNode(panelWin, gainId).classList.contains("selected"),
+     "Non-selected nodes do not have class 'selected'.");
+ 
+-  yield clickGraphNode(panelWin, gainId);
++  await clickGraphNode(panelWin, gainId);
+ 
+   ok(!findGraphNode(panelWin, oscId).classList.contains("selected"),
+     "Previously selected node no longer has class 'selected'.");
+   ok(!findGraphNode(panelWin, destId).classList.contains("selected"),
+     "Non-selected nodes do not have class 'selected'.");
+   ok(findGraphNode(panelWin, gainId).classList.contains("selected"),
+     "Newly selected node now has class 'selected'.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_graph-zoom.js b/devtools/client/webaudioeditor/test/browser_wa_graph-zoom.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_graph-zoom.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_graph-zoom.js
+@@ -1,23 +1,23 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the graph's scale and position is reset on a page reload.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, ContextView } = panelWin;
+ 
+   let started = once(gFront, "start-context");
+ 
+-  yield Promise.all([
++  await Promise.all([
+     waitForGraphRendered(panelWin, 3, 2),
+     reload(target),
+   ]);
+ 
+   is(ContextView.getCurrentScale(), 1, "Default graph scale is 1.");
+   is(ContextView.getCurrentTranslation()[0], 20, "Default x-translation is 20.");
+   is(ContextView.getCurrentTranslation()[1], 20, "Default y-translation is 20.");
+ 
+@@ -25,19 +25,19 @@ add_task(function* () {
+   panelWin.d3.select("#graph-target").attr("transform", "translate([100, 400]) scale(10)");
+   ContextView._zoomBinding.scale(10);
+   ContextView._zoomBinding.translate([100, 400]);
+ 
+   is(ContextView.getCurrentScale(), 10, "After zoom, scale is 10.");
+   is(ContextView.getCurrentTranslation()[0], 100, "After zoom, x-translation is 100.");
+   is(ContextView.getCurrentTranslation()[1], 400, "After zoom, y-translation is 400.");
+ 
+-  yield Promise.all([
++  await Promise.all([
+     waitForGraphRendered(panelWin, 3, 2),
+     reload(target),
+   ]);
+ 
+   is(ContextView.getCurrentScale(), 1, "After refresh, graph scale is 1.");
+   is(ContextView.getCurrentTranslation()[0], 20, "After refresh, x-translation is 20.");
+   is(ContextView.getCurrentTranslation()[1], 20, "After refresh, y-translation is 20.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_inspector-bypass-01.js b/devtools/client/webaudioeditor/test/browser_wa_inspector-bypass-01.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-bypass-01.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-bypass-01.js
+@@ -1,61 +1,61 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that nodes are correctly bypassed when bypassing.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   // Wait for the node to be set as well as the inspector to come fully into the view
+-  yield clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[1]), true);
++  await clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[1]), true);
+ 
+   let $bypass = $("toolbarbutton.bypass");
+ 
+-  is((yield actors[1].isBypassed()), false, "AudioNodeActor is not bypassed by default.");
++  is((await actors[1].isBypassed()), false, "AudioNodeActor is not bypassed by default.");
+   is($bypass.checked, true, "Button is 'on' for normal nodes");
+   is($bypass.disabled, false, "Bypass button is not disabled for normal nodes");
+ 
+   command($bypass);
+-  yield once(gAudioNodes, "bypass");
++  await once(gAudioNodes, "bypass");
+ 
+-  is((yield actors[1].isBypassed()), true, "AudioNodeActor is bypassed.");
++  is((await actors[1].isBypassed()), true, "AudioNodeActor is bypassed.");
+   is($bypass.checked, false, "Button is 'off' when clicked");
+   is($bypass.disabled, false, "Bypass button is not disabled after click");
+   ok(findGraphNode(panelWin, nodeIds[1]).classList.contains("bypassed"),
+     "AudioNode has 'bypassed' class.");
+ 
+   command($bypass);
+-  yield once(gAudioNodes, "bypass");
++  await once(gAudioNodes, "bypass");
+ 
+-  is((yield actors[1].isBypassed()), false, "AudioNodeActor is no longer bypassed.");
++  is((await actors[1].isBypassed()), false, "AudioNodeActor is no longer bypassed.");
+   is($bypass.checked, true, "Button is back on when clicked");
+   is($bypass.disabled, false, "Bypass button is not disabled after click");
+   ok(!findGraphNode(panelWin, nodeIds[1]).classList.contains("bypassed"),
+     "AudioNode no longer has 'bypassed' class.");
+ 
+-  yield clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[0]));
++  await clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[0]));
+ 
+-  is((yield actors[0].isBypassed()), false, "Unbypassable AudioNodeActor is not bypassed.");
++  is((await actors[0].isBypassed()), false, "Unbypassable AudioNodeActor is not bypassed.");
+   is($bypass.checked, false, "Button is 'off' for unbypassable nodes");
+   is($bypass.disabled, true, "Bypass button is disabled for unbypassable nodes");
+ 
+   command($bypass);
+-  is((yield actors[0].isBypassed()), false,
++  is((await actors[0].isBypassed()), false,
+     "Clicking button on unbypassable node does not change bypass state on actor.");
+   is($bypass.checked, false, "Button is still 'off' for unbypassable nodes");
+   is($bypass.disabled, true, "Bypass button is still disabled for unbypassable nodes");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_inspector-toggle.js b/devtools/client/webaudioeditor/test/browser_wa_inspector-toggle.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-toggle.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-toggle.js
+@@ -1,60 +1,60 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that the inspector toggle button shows and hides
+  * the inspector panel as intended.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
+   let gVars = InspectorView._propsView;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   ok(!InspectorView.isVisible(), "InspectorView hidden on start.");
+ 
+   // Open inspector pane
+   $("#inspector-pane-toggle").click();
+-  yield once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
++  await once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
+ 
+   ok(InspectorView.isVisible(), "InspectorView shown after toggling.");
+ 
+   ok(isVisible($("#web-audio-editor-details-pane-empty")),
+     "InspectorView empty message should still be visible.");
+   ok(!isVisible($("#web-audio-editor-tabs")),
+     "InspectorView tabs view should still be hidden.");
+ 
+   // Close inspector pane
+   $("#inspector-pane-toggle").click();
+-  yield once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
++  await once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
+ 
+   ok(!InspectorView.isVisible(), "InspectorView back to being hidden.");
+ 
+   // Open again to test node loading while open
+   $("#inspector-pane-toggle").click();
+-  yield once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
++  await once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
+ 
+   ok(InspectorView.isVisible(), "InspectorView being shown.");
+   ok(!isVisible($("#web-audio-editor-tabs")),
+     "InspectorView tabs are still hidden.");
+ 
+-  yield clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[1]));
++  await clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[1]));
+ 
+   ok(!isVisible($("#web-audio-editor-details-pane-empty")),
+     "Empty message hides even when loading node while open.");
+   ok(isVisible($("#web-audio-editor-tabs")),
+     "Switches to tab view when loading node while open.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_inspector-width.js b/devtools/client/webaudioeditor/test/browser_wa_inspector-width.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-width.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-width.js
+@@ -1,57 +1,57 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test that the WebAudioInspector's Width is saved as
+  * a preference
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
+   let gVars = InspectorView._propsView;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   ok(!InspectorView.isVisible(), "InspectorView hidden on start.");
+ 
+   // Open inspector pane
+   $("#inspector-pane-toggle").click();
+-  yield once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
++  await once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
+ 
+   let newInspectorWidth = 500;
+ 
+   // Setting width to new_inspector_width
+   $("#web-audio-inspector").setAttribute("width", newInspectorWidth);
+ 
+   // Width should be 500 after reloading
+   events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  [actors] = yield events;
++  [actors] = await events;
+   nodeIds = actors.map(actor => actor.actorID);
+ 
+   // Open inspector pane
+   $("#inspector-pane-toggle").click();
+-  yield once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
++  await once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED);
+ 
+-  yield clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[1]));
++  await clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[1]));
+ 
+   // Getting the width of the audio inspector
+   let width = $("#web-audio-inspector").getAttribute("width");
+ 
+   is(width, newInspectorWidth, "WebAudioEditor's Inspector width should be saved as a preference");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_inspector.js b/devtools/client/webaudioeditor/test/browser_wa_inspector.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_inspector.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_inspector.js
+@@ -1,46 +1,46 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that inspector view opens on graph node click, and
+  * loads the correct node inside the inspector.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
+   let gVars = InspectorView._propsView;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   ok(!InspectorView.isVisible(), "InspectorView hidden on start.");
+   ok(isVisible($("#web-audio-editor-details-pane-empty")),
+     "InspectorView empty message should show when no node's selected.");
+   ok(!isVisible($("#web-audio-editor-tabs")),
+     "InspectorView tabs view should be hidden when no node's selected.");
+ 
+   // Wait for the node to be set as well as the inspector to come fully into the view
+-  yield clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[1]), true);
++  await clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[1]), true);
+ 
+   ok(InspectorView.isVisible(), "InspectorView shown once node selected.");
+   ok(!isVisible($("#web-audio-editor-details-pane-empty")),
+     "InspectorView empty message hidden when node selected.");
+   ok(isVisible($("#web-audio-editor-tabs")),
+     "InspectorView tabs view visible when node selected.");
+ 
+   is($("#web-audio-editor-tabs").selectedIndex, 0,
+     "default tab selected should be the parameters tab.");
+ 
+-  yield clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[2]));
++  await clickGraphNode(panelWin, findGraphNode(panelWin, nodeIds[2]));
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_navigate.js b/devtools/client/webaudioeditor/test/browser_wa_navigate.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_navigate.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_navigate.js
+@@ -1,44 +1,44 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests naviating from a page to another will repopulate
+  * the audio graph if both pages have an AudioContext.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $ } = panelWin;
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  yield events;
++  await events;
+ 
+   var { nodes, edges } = countGraphObjects(panelWin);
+   is(nodes, 3, "should only be 3 nodes.");
+   is(edges, 2, "should only be 2 edges.");
+ 
+   events = Promise.all([
+     getN(gFront, "create-node", 15),
+     waitForGraphRendered(panelWin, 15, 0)
+   ]);
+   navigate(target, SIMPLE_NODES_URL);
+-  yield events;
++  await events;
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should be hidden after context found after navigation.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for an audio context' notice should be hidden after context found after navigation.");
+   is($("#content").hidden, false,
+     "The tool's content should reappear without closing and reopening the toolbox.");
+ 
+   var { nodes, edges } = countGraphObjects(panelWin);
+   is(nodes, 15, "after navigation, should have 15 nodes");
+   is(edges, 0, "after navigation, should have 0 edges.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-01.js b/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-01.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-01.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-01.js
+@@ -1,65 +1,65 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that properties are updated when modifying the VariablesView.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
+   let gVars = PropertiesView._propsView;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   click(panelWin, findGraphNode(panelWin, nodeIds[1]));
+   // Wait for the node to be set as well as the inspector to come fully into the view
+-  yield Promise.all([
++  await Promise.all([
+     waitForInspectorRender(panelWin, EVENTS),
+     once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED)
+   ]);
+ 
+   let setAndCheck = setAndCheckVariable(panelWin, gVars);
+ 
+   checkVariableView(gVars, 0, {
+     "type": "sine",
+     "frequency": 440,
+     "detune": 0
+   }, "default loaded string");
+ 
+   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
+-  yield waitForInspectorRender(panelWin, EVENTS),
++  await waitForInspectorRender(panelWin, EVENTS),
+   checkVariableView(gVars, 0, {
+     "gain": 0
+   }, "default loaded number");
+ 
+   click(panelWin, findGraphNode(panelWin, nodeIds[1]));
+-  yield waitForInspectorRender(panelWin, EVENTS),
+-  yield setAndCheck(0, "type", "square", "square", "sets string as string");
++  await waitForInspectorRender(panelWin, EVENTS),
++  await setAndCheck(0, "type", "square", "square", "sets string as string");
+ 
+   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
+-  yield waitForInspectorRender(panelWin, EVENTS),
+-  yield setAndCheck(0, "gain", "0.005", 0.005, "sets number as number");
+-  yield setAndCheck(0, "gain", "0.1", 0.1, "sets float as float");
+-  yield setAndCheck(0, "gain", ".2", 0.2, "sets float without leading zero as float");
++  await waitForInspectorRender(panelWin, EVENTS),
++  await setAndCheck(0, "gain", "0.005", 0.005, "sets number as number");
++  await setAndCheck(0, "gain", "0.1", 0.1, "sets float as float");
++  await setAndCheck(0, "gain", ".2", 0.2, "sets float without leading zero as float");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+ 
+ function setAndCheckVariable(panelWin, gVars) {
+-  return Task.async(function* (varNum, prop, value, expected, desc) {
+-    yield modifyVariableView(panelWin, gVars, varNum, prop, value);
++  return async function (varNum, prop, value, expected, desc) {
++    await modifyVariableView(panelWin, gVars, varNum, prop, value);
+     var props = {};
+     props[prop] = expected;
+     checkVariableView(gVars, varNum, props, desc);
+-  });
++  };
+ }
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-02.js b/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-02.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-02.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-02.js
+@@ -1,44 +1,44 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that properties are not updated when modifying the VariablesView.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(COMPLEX_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
+   let gVars = PropertiesView._propsView;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     getN(gFront, "create-node", 8),
+     waitForGraphRendered(panelWin, 8, 8)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   click(panelWin, findGraphNode(panelWin, nodeIds[3]));
+   // Wait for the node to be set as well as the inspector to come fully into the view
+-  yield Promise.all([
++  await Promise.all([
+     waitForInspectorRender(panelWin, EVENTS),
+     once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED),
+   ]);
+ 
+   let errorEvent = once(panelWin, EVENTS.UI_SET_PARAM_ERROR);
+ 
+   try {
+-    yield modifyVariableView(panelWin, gVars, 0, "bufferSize", 2048);
++    await modifyVariableView(panelWin, gVars, 0, "bufferSize", 2048);
+   } catch (e) {
+     // we except modifyVariableView to fail here, because bufferSize is not writable
+   }
+ 
+-  yield errorEvent;
++  await errorEvent;
+ 
+   checkVariableView(gVars, 0, {bufferSize: 4096}, "check that unwritable variable is not updated");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js b/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
+@@ -27,48 +27,48 @@ function waitForDeviceClosed() {
+       if (!aMessage.data.showGlobalIndicator) {
+         ppmm.removeMessageListener(message, listener);
+         resolve();
+       }
+     });
+   });
+ }
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(MEDIA_NODES_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(MEDIA_NODES_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
+   let gVars = PropertiesView._propsView;
+ 
+   // Auto enable getUserMedia
+   let mediaPermissionPref = Services.prefs.getBoolPref(MEDIA_PERMISSION);
+   Services.prefs.setBoolPref(MEDIA_PERMISSION, true);
+ 
+-  yield loadFrameScriptUtils();
++  await loadFrameScriptUtils();
+ 
+   let events = Promise.all([
+     getN(gFront, "create-node", 4),
+     waitForGraphRendered(panelWin, 4, 0)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   let types = [
+     "AudioDestinationNode", "MediaElementAudioSourceNode",
+     "MediaStreamAudioSourceNode", "MediaStreamAudioDestinationNode"
+   ];
+ 
+-  let defaults = yield Promise.all(types.map(type => nodeDefaultValues(type)));
++  let defaults = await Promise.all(types.map(type => nodeDefaultValues(type)));
+ 
+   for (let i = 0; i < types.length; i++) {
+     click(panelWin, findGraphNode(panelWin, nodeIds[i]));
+-    yield waitForInspectorRender(panelWin, EVENTS);
++    await waitForInspectorRender(panelWin, EVENTS);
+     checkVariableView(gVars, 0, defaults[i], types[i]);
+   }
+ 
+   // Reset permissions on getUserMedia
+   Services.prefs.setBoolPref(MEDIA_PERMISSION, mediaPermissionPref);
+ 
+-  yield teardown(target);
++  await teardown(target);
+ 
+-  yield waitForDeviceClosed();
++  await waitForDeviceClosed();
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_properties-view-params-objects.js b/devtools/client/webaudioeditor/test/browser_wa_properties-view-params-objects.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-params-objects.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-params-objects.js
+@@ -1,46 +1,46 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that params view correctly displays non-primitive properties
+  * like AudioBuffer and Float32Array in properties of AudioNodes.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(BUFFER_AND_ARRAY_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(BUFFER_AND_ARRAY_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
+   let gVars = PropertiesView._propsView;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     getN(gFront, "create-node", 3),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
+-  yield waitForInspectorRender(panelWin, EVENTS);
++  await waitForInspectorRender(panelWin, EVENTS);
+   checkVariableView(gVars, 0, {
+     "curve": "Float32Array"
+   }, "WaveShaper's `curve` is listed as an `Float32Array`.");
+ 
+   let aVar = gVars.getScopeAtIndex(0).get("curve");
+   let state = aVar.target.querySelector(".theme-twisty").hasAttribute("invisible");
+   ok(state, "Float32Array property should not have a dropdown.");
+ 
+   click(panelWin, findGraphNode(panelWin, nodeIds[1]));
+-  yield waitForInspectorRender(panelWin, EVENTS);
++  await waitForInspectorRender(panelWin, EVENTS);
+   checkVariableView(gVars, 0, {
+     "buffer": "AudioBuffer"
+   }, "AudioBufferSourceNode's `buffer` is listed as an `AudioBuffer`.");
+ 
+   aVar = gVars.getScopeAtIndex(0).get("buffer");
+   state = aVar.target.querySelector(".theme-twisty").hasAttribute("invisible");
+   ok(state, "AudioBuffer property should not have a dropdown.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_properties-view-params.js b/devtools/client/webaudioeditor/test/browser_wa_properties-view-params.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-params.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-params.js
+@@ -1,43 +1,43 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that params view correctly displays all properties for nodes
+  * correctly, with default values and correct types.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_NODES_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_NODES_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
+   let gVars = PropertiesView._propsView;
+ 
+   let started = once(gFront, "start-context");
+ 
+-  yield loadFrameScriptUtils();
++  await loadFrameScriptUtils();
+ 
+   let events = Promise.all([
+     getN(gFront, "create-node", 15),
+     waitForGraphRendered(panelWin, 15, 0)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   let types = [
+     "AudioDestinationNode", "AudioBufferSourceNode", "ScriptProcessorNode",
+     "AnalyserNode", "GainNode", "DelayNode", "BiquadFilterNode", "WaveShaperNode",
+     "PannerNode", "ConvolverNode", "ChannelSplitterNode", "ChannelMergerNode",
+     "DynamicsCompressorNode", "OscillatorNode"
+   ];
+ 
+-  let defaults = yield Promise.all(types.map(type => nodeDefaultValues(type)));
++  let defaults = await Promise.all(types.map(type => nodeDefaultValues(type)));
+ 
+   for (let i = 0; i < types.length; i++) {
+     click(panelWin, findGraphNode(panelWin, nodeIds[i]));
+-    yield waitForInspectorRender(panelWin, EVENTS);
++    await waitForInspectorRender(panelWin, EVENTS);
+     checkVariableView(gVars, 0, defaults[i], types[i]);
+   }
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_properties-view.js b/devtools/client/webaudioeditor/test/browser_wa_properties-view.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view.js
+@@ -1,42 +1,42 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that params view shows params when they exist, and are hidden otherwise.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
+   let gVars = PropertiesView._propsView;
+ 
+   let started = once(gFront, "start-context");
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+   // Gain node
+   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
+-  yield waitForInspectorRender(panelWin, EVENTS);
++  await waitForInspectorRender(panelWin, EVENTS);
+ 
+   ok(isVisible($("#properties-content")), "Parameters shown when they exist.");
+   ok(!isVisible($("#properties-empty")),
+     "Empty message hidden when AudioParams exist.");
+ 
+   // Destination node
+   click(panelWin, findGraphNode(panelWin, nodeIds[0]));
+-  yield waitForInspectorRender(panelWin, EVENTS);
++  await waitForInspectorRender(panelWin, EVENTS);
+ 
+   ok(!isVisible($("#properties-content")),
+     "Parameters hidden when they don't exist.");
+   ok(isVisible($("#properties-empty")),
+     "Empty message shown when no AudioParams exist.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_reset-01.js b/devtools/client/webaudioeditor/test/browser_wa_reset-01.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_reset-01.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_reset-01.js
+@@ -1,61 +1,61 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that reloading a tab will properly listen for the `start-context`
+  * event and reshow the tools after reloading.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { gFront, $ } = panel.panelWin;
+ 
+   is($("#reload-notice").hidden, false,
+     "The 'reload this page' notice should initially be visible.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for an audio context' notice should initially be hidden.");
+   is($("#content").hidden, true,
+     "The tool's content should initially be hidden.");
+ 
+   let navigating = once(target, "will-navigate");
+   let started = once(gFront, "start-context");
+ 
+   reload(target);
+ 
+-  yield navigating;
++  await navigating;
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should be hidden when navigating.");
+   is($("#waiting-notice").hidden, false,
+     "The 'waiting for an audio context' notice should be visible when navigating.");
+   is($("#content").hidden, true,
+     "The tool's content should still be hidden.");
+ 
+-  yield started;
++  await started;
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should be hidden after context found.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for an audio context' notice should be hidden after context found.");
+   is($("#content").hidden, false,
+     "The tool's content should not be hidden anymore.");
+ 
+   navigating = once(target, "will-navigate");
+   started = once(gFront, "start-context");
+ 
+   reload(target);
+ 
+-  yield Promise.all([navigating, started]);
++  await Promise.all([navigating, started]);
+   let rendered = waitForGraphRendered(panel.panelWin, 3, 2);
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should be hidden after context found after reload.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for an audio context' notice should be hidden after context found after reload.");
+   is($("#content").hidden, false,
+     "The tool's content should reappear without closing and reopening the toolbox.");
+ 
+-  yield rendered;
++  await rendered;
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_reset-02.js b/devtools/client/webaudioeditor/test/browser_wa_reset-02.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_reset-02.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_reset-02.js
+@@ -1,37 +1,37 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests reloading a tab with the tools open properly cleans up
+  * the graph.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $ } = panelWin;
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  yield events;
++  await events;
+ 
+   let { nodes, edges } = countGraphObjects(panelWin);
+   is(nodes, 3, "should only be 3 nodes.");
+   is(edges, 2, "should only be 2 edges.");
+ 
+   events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  yield events;
++  await events;
+ 
+   ({ nodes, edges } = countGraphObjects(panelWin));
+   is(nodes, 3, "after reload, should only be 3 nodes.");
+   is(edges, 2, "after reload, should only be 2 edges.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_reset-03.js b/devtools/client/webaudioeditor/test/browser_wa_reset-03.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_reset-03.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_reset-03.js
+@@ -1,48 +1,48 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests reloading a tab with the tools open properly cleans up
+  * the inspector and selected node.
+  */
+ 
+-add_task(function* () {
+-  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
++add_task(async function () {
++  let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
+   let { panelWin } = panel;
+   let { gFront, $, InspectorView } = panelWin;
+ 
+   let events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  let [actors] = yield events;
++  let [actors] = await events;
+   let nodeIds = actors.map(actor => actor.actorID);
+ 
+-  yield clickGraphNode(panelWin, nodeIds[1], true);
++  await clickGraphNode(panelWin, nodeIds[1], true);
+   ok(InspectorView.isVisible(), "InspectorView visible after selecting a node.");
+   is(InspectorView.getCurrentAudioNode().id, nodeIds[1], "InspectorView has correct node set.");
+ 
+   /**
+    * Reload
+    */
+ 
+   events = Promise.all([
+     get3(gFront, "create-node"),
+     waitForGraphRendered(panelWin, 3, 2)
+   ]);
+   reload(target);
+-  [actors] = yield events;
++  [actors] = await events;
+   nodeIds = actors.map(actor => actor.actorID);
+ 
+   ok(!InspectorView.isVisible(), "InspectorView hidden on start.");
+   is(InspectorView.getCurrentAudioNode(), null,
+     "InspectorView has no current node set on reset.");
+ 
+-  yield clickGraphNode(panelWin, nodeIds[2], true);
++  await clickGraphNode(panelWin, nodeIds[2], true);
+   ok(InspectorView.isVisible(),
+     "InspectorView visible after selecting a node after a reset.");
+   is(InspectorView.getCurrentAudioNode().id, nodeIds[2], "InspectorView has correct node set upon clicking graph node after a reset.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_wa_reset-04.js b/devtools/client/webaudioeditor/test/browser_wa_reset-04.js
+--- a/devtools/client/webaudioeditor/test/browser_wa_reset-04.js
++++ b/devtools/client/webaudioeditor/test/browser_wa_reset-04.js
+@@ -1,60 +1,60 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Tests that switching to an iframe works fine.
+  */
+ 
+-add_task(function* () {
++add_task(async function () {
+   Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
+ 
+-  let { target, panel, toolbox } = yield initWebAudioEditor(IFRAME_CONTEXT_URL);
++  let { target, panel, toolbox } = await initWebAudioEditor(IFRAME_CONTEXT_URL);
+   let { gFront, $ } = panel.panelWin;
+ 
+   is($("#reload-notice").hidden, false,
+     "The 'reload this page' notice should initially be visible.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for an audio context' notice should initially be hidden.");
+   is($("#content").hidden, true,
+     "The tool's content should initially be hidden.");
+ 
+   let btn = toolbox.doc.getElementById("command-button-frames");
+   ok(!btn.firstChild, "The frame list button has no children");
+ 
+   // Open frame menu and wait till it's available on the screen.
+-  let menu = yield toolbox.showFramesMenu({target: btn});
+-  yield once(menu, "open");
++  let menu = await toolbox.showFramesMenu({target: btn});
++  await once(menu, "open");
+ 
+   let frames = menu.items;
+   is(frames.length, 2, "We have both frames in the list");
+ 
+   // Select the iframe
+   frames[1].click();
+ 
+   let navigating = once(target, "will-navigate");
+ 
+-  yield navigating;
++  await navigating;
+ 
+   is($("#reload-notice").hidden, false,
+     "The 'reload this page' notice should still be visible when switching to a frame.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for an audio context' notice should be kept hidden when switching to a frame.");
+   is($("#content").hidden, true,
+     "The tool's content should still be hidden.");
+ 
+   navigating = once(target, "will-navigate");
+   let started = once(gFront, "start-context");
+ 
+   reload(target);
+ 
+-  yield Promise.all([navigating, started]);
++  await Promise.all([navigating, started]);
+ 
+   is($("#reload-notice").hidden, true,
+     "The 'reload this page' notice should be hidden after reloading the frame.");
+   is($("#waiting-notice").hidden, true,
+     "The 'waiting for an audio context' notice should be hidden after reloading the frame.");
+   is($("#content").hidden, false,
+     "The tool's content should appear after reload.");
+ 
+-  yield teardown(target);
++  await teardown(target);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_webaudio-actor-automation-event.js b/devtools/client/webaudioeditor/test/browser_webaudio-actor-automation-event.js
+--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-automation-event.js
++++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-automation-event.js
+@@ -1,30 +1,30 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test that the WebAudioActor receives and emits the `automation-event` events
+  * with correct arguments from the content.
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(AUTOMATION_URL);
++add_task(async function () {
++  let { target, front } = await initBackend(AUTOMATION_URL);
+   let events = [];
+ 
+   let expected = [
+     ["setValueAtTime", 0.2, 0],
+     ["linearRampToValueAtTime", 1, 0.3],
+     ["exponentialRampToValueAtTime", 0.75, 0.6],
+     ["setValueCurveAtTime", [-1, 0, 1], 0.7, 0.3],
+   ];
+ 
+   front.on("automation-event", onAutomationEvent);
+ 
+-  let [_, __, [destNode, oscNode, gainNode], [connect1, connect2]] = yield Promise.all([
++  let [_, __, [destNode, oscNode, gainNode], [connect1, connect2]] = await Promise.all([
+     front.setup({ reload: true }),
+     once(front, "start-context"),
+     get3(front, "create-node"),
+     get2(front, "connect-node")
+   ]);
+ 
+   is(events.length, 4, "correct number of events fired");
+ 
+@@ -43,10 +43,10 @@ add_task(function* () {
+       } else {
+         is(a, exp[i + 1], `correct ${i + 1}th argument in args: ${a}`);
+       }
+     });
+     events.push([eventName].concat(args));
+   }
+ 
+   front.off("automation-event", onAutomationEvent);
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_webaudio-actor-connect-param.js b/devtools/client/webaudioeditor/test/browser_webaudio-actor-connect-param.js
+--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-connect-param.js
++++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-connect-param.js
+@@ -1,25 +1,25 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test the `connect-param` event on the web audio actor.
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(CONNECT_PARAM_URL);
+-  let [, , [destNode, carrierNode, modNode, gainNode], , connectParam] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(CONNECT_PARAM_URL);
++  let [, , [destNode, carrierNode, modNode, gainNode], , connectParam] = await Promise.all([
+     front.setup({ reload: true }),
+     once(front, "start-context"),
+     getN(front, "create-node", 4),
+     get2(front, "connect-node"),
+     once(front, "connect-param")
+   ]);
+ 
+   info(connectParam);
+ 
+   is(connectParam.source.actorID, modNode.actorID, "`connect-param` has correct actor for `source`");
+   is(connectParam.dest.actorID, gainNode.actorID, "`connect-param` has correct actor for `dest`");
+   is(connectParam.param, "gain", "`connect-param` has correct parameter name for `param`");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/browser_webaudio-actor-destroy-node.js b/devtools/client/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
+--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
++++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
+@@ -1,40 +1,40 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test `destroy-node` event on WebAudioActor.
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(DESTROY_NODES_URL);
++add_task(async function () {
++  let { target, front } = await initBackend(DESTROY_NODES_URL);
+ 
+-  let [, , created] = yield Promise.all([
++  let [, , created] = await Promise.all([
+     front.setup({ reload: true }),
+     once(front, "start-context"),
+     // Should create dest, gain, and oscillator node and 10
+     // disposable buffer nodes
+     getN(front, "create-node", 13)
+   ]);
+ 
+   let waitUntilDestroyed = getN(front, "destroy-node", 10);
+ 
+   // Force CC so we can ensure it's run to clear out dead AudioNodes
+   forceNodeCollection();
+ 
+-  let destroyed = yield waitUntilDestroyed;
++  let destroyed = await waitUntilDestroyed;
+ 
+   destroyed.forEach((node, i) => {
+     ok(node.type, "AudioBufferSourceNode", "Only buffer nodes are destroyed");
+     ok(actorIsInList(created, destroyed[i]),
+       "`destroy-node` called only on AudioNodes in current document.");
+   });
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+ 
+ function actorIsInList(list, actor) {
+   for (let i = 0; i < list.length; i++) {
+     if (list[i].actorID === actor.actorID)
+       return list[i];
+   }
+   return null;
+diff --git a/devtools/client/webaudioeditor/test/browser_webaudio-actor-simple.js b/devtools/client/webaudioeditor/test/browser_webaudio-actor-simple.js
+--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-simple.js
++++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-simple.js
+@@ -1,18 +1,18 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /**
+  * Test basic communication of Web Audio actor
+  */
+ 
+-add_task(function* () {
+-  let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
+-  let [_, __, [destNode, oscNode, gainNode], [connect1, connect2]] = yield Promise.all([
++add_task(async function () {
++  let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
++  let [_, __, [destNode, oscNode, gainNode], [connect1, connect2]] = await Promise.all([
+     front.setup({ reload: true }),
+     once(front, "start-context"),
+     get3(front, "create-node"),
+     get2(front, "connect-node")
+   ]);
+ 
+   is(destNode.type, "AudioDestinationNode", "WebAudioActor:create-node returns AudioNodeActor for AudioDestination");
+   is(oscNode.type, "OscillatorNode", "WebAudioActor:create-node returns AudioNodeActor");
+@@ -21,10 +21,10 @@ add_task(function* () {
+   let { source, dest } = connect1;
+   is(source.actorID, oscNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on source (osc->gain)");
+   is(dest.actorID, gainNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on dest (osc->gain)");
+ 
+   ({ source, dest } = connect2);
+   is(source.actorID, gainNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on source (gain->dest)");
+   is(dest.actorID, destNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on dest (gain->dest)");
+ 
+-  yield removeTab(target.tab);
++  await removeTab(target.tab);
+ });
+diff --git a/devtools/client/webaudioeditor/test/head.js b/devtools/client/webaudioeditor/test/head.js
+--- a/devtools/client/webaudioeditor/test/head.js
++++ b/devtools/client/webaudioeditor/test/head.js
+@@ -57,46 +57,46 @@ function navigate(aTarget, aUrl, aWaitFo
+  * This requires calling removeTab before the test ends.
+  */
+ function initBackend(aUrl) {
+   info("Initializing a web audio editor front.");
+ 
+   DebuggerServer.init();
+   DebuggerServer.registerAllActors();
+ 
+-  return Task.spawn(function* () {
+-    let tab = yield addTab(aUrl);
++  return (async function () {
++    let tab = await addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+-    yield target.makeRemote();
++    await target.makeRemote();
+ 
+     let front = new WebAudioFront(target.client, target.form);
+     return { target, front };
+-  });
++  })();
+ }
+ 
+ /**
+  * Adds a new tab, and open the toolbox for that tab, selecting the audio editor
+  * panel.
+  * This requires calling teardown before the test ends.
+  */
+ function initWebAudioEditor(aUrl) {
+   info("Initializing a web audio editor pane.");
+ 
+-  return Task.spawn(function* () {
+-    let tab = yield addTab(aUrl);
++  return (async function () {
++    let tab = await addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+-    yield target.makeRemote();
++    await target.makeRemote();
+ 
+     Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true);
+-    let toolbox = yield gDevTools.showToolbox(target, "webaudioeditor");
++    let toolbox = await gDevTools.showToolbox(target, "webaudioeditor");
+     let panel = toolbox.getCurrentPanel();
+     return { target, panel, toolbox };
+-  });
++  })();
+ }
+ 
+ /**
+  * Close the toolbox, destroying all panels, and remove the added test tabs.
+  */
+ function teardown(aTarget) {
+   info("Destroying the web audio editor.");
+ 
+@@ -320,17 +320,17 @@ function countGraphObjects(win) {
+     edges: win.document.querySelectorAll(".edgePaths > .edgePath").length
+   };
+ }
+ 
+ /**
+ * Forces cycle collection and GC, used in AudioNode destruction tests.
+ */
+ function forceNodeCollection() {
+-  ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
++  ContentTask.spawn(gBrowser.selectedBrowser, {}, async function () {
+     // Kill the reference keeping stuff alive.
+     content.wrappedJSObject.keepAlive = null;
+ 
+     // Collect the now-deceased nodes.
+     Cu.forceGC();
+     Cu.forceCC();
+     Cu.forceGC();
+     Cu.forceCC();
+diff --git a/devtools/client/webaudioeditor/views/automation.js b/devtools/client/webaudioeditor/views/automation.js
+--- a/devtools/client/webaudioeditor/views/automation.js
++++ b/devtools/client/webaudioeditor/views/automation.js
+@@ -41,48 +41,48 @@ var AutomationView = {
+   resetUI: function () {
+     this._currentNode = null;
+   },
+ 
+   /**
+    * On a new node selection, create the Automation panel for
+    * that specific node.
+    */
+-  build: Task.async(function* () {
++  async build() {
+     let node = this._currentNode;
+ 
+-    let props = yield node.getParams();
++    let props = await node.getParams();
+     let params = props.filter(({ flags }) => flags && flags.param);
+ 
+     this._createParamButtons(params);
+ 
+     this._selectedParamName = params[0] ? params[0].param : null;
+     this.render();
+-  }),
++  },
+ 
+   /**
+    * Renders the graph for specified `paramName`. Called when
+    * the parameter view is changed, or when new param data events
+    * are fired for the currently specified param.
+    */
+-  render: Task.async(function* () {
++  async render() {
+     let node = this._currentNode;
+     let paramName = this._selectedParamName;
+     // Escape if either node or parameter name does not exist.
+     if (!node || !paramName) {
+       this._setState("no-params");
+       window.emit(EVENTS.UI_AUTOMATION_TAB_RENDERED, null);
+       return;
+     }
+ 
+-    let { values, events } = yield node.getAutomationData(paramName);
++    let { values, events } = await node.getAutomationData(paramName);
+     this._setState(events.length ? "show" : "no-events");
+-    yield this.graph.setDataWhenReady(values);
++    await this.graph.setDataWhenReady(values);
+     window.emit(EVENTS.UI_AUTOMATION_TAB_RENDERED, node.id);
+-  }),
++  },
+ 
+   /**
+    * Create the buttons for each AudioParam, that when clicked,
+    * render the graph for that AudioParam.
+    */
+   _createParamButtons: function (params) {
+     this._buttons.innerHTML = "";
+     params.forEach((param, i) => {
+diff --git a/devtools/client/webaudioeditor/views/inspector.js b/devtools/client/webaudioeditor/views/inspector.js
+--- a/devtools/client/webaudioeditor/views/inspector.js
++++ b/devtools/client/webaudioeditor/views/inspector.js
+@@ -71,34 +71,34 @@ var InspectorView = {
+     this.button = null;
+     this.splitter = null;
+   },
+ 
+   /**
+    * Takes a AudioNodeView `node` and sets it as the current
+    * node and scaffolds the inspector view based off of the new node.
+    */
+-  setCurrentAudioNode: Task.async(function* (node) {
++  async setCurrentAudioNode(node) {
+     this._currentNode = node || null;
+ 
+     // If no node selected, set the inspector back to "no AudioNode selected"
+     // view.
+     if (!node) {
+       $("#web-audio-editor-details-pane-empty").removeAttribute("hidden");
+       $("#web-audio-editor-tabs").setAttribute("hidden", "true");
+       window.emit(EVENTS.UI_INSPECTOR_NODE_SET, null);
+     }
+     // Otherwise load up the tabs view and hide the empty placeholder
+     else {
+       $("#web-audio-editor-details-pane-empty").setAttribute("hidden", "true");
+       $("#web-audio-editor-tabs").removeAttribute("hidden");
+       this._buildToolbar();
+       window.emit(EVENTS.UI_INSPECTOR_NODE_SET, this._currentNode.id);
+     }
+-  }),
++  },
+ 
+   /**
+    * Returns the current AudioNodeView.
+    */
+   getCurrentAudioNode: function () {
+     return this._currentNode;
+   },
+ 
+diff --git a/devtools/client/webaudioeditor/views/properties.js b/devtools/client/webaudioeditor/views/properties.js
+--- a/devtools/client/webaudioeditor/views/properties.js
++++ b/devtools/client/webaudioeditor/views/properties.js
+@@ -58,23 +58,23 @@ var PropertiesView = {
+       this._buildPropertiesView();
+     }
+   },
+ 
+   /**
+    * Reconstructs the `Properties` tab in the inspector
+    * with the `this._currentNode` as it's source.
+    */
+-  _buildPropertiesView: Task.async(function* () {
++  async _buildPropertiesView() {
+     let propsView = this._propsView;
+     let node = this._currentNode;
+     propsView.empty();
+ 
+     let audioParamsScope = propsView.addScope("AudioParams");
+-    let props = yield node.getParams();
++    let props = await node.getParams();
+ 
+     // Disable AudioParams VariableView expansion
+     // when there are no props i.e. AudioDestinationNode
+     this._togglePropertiesView(!!props.length);
+ 
+     props.forEach(({ param, value, flags }) => {
+       let descriptor = {
+         value: value,
+@@ -84,17 +84,17 @@ var PropertiesView = {
+ 
+       // No items should currently display a dropdown
+       item.twisty = false;
+     });
+ 
+     audioParamsScope.expanded = true;
+ 
+     window.emit(EVENTS.UI_PROPERTIES_TAB_RENDERED, node.id);
+-  }),
++  },
+ 
+   /**
+    * Toggles the display of the "empty" properties view when
+    * node has no properties to display.
+    */
+   _togglePropertiesView: function (show) {
+     let propsView = $("#properties-content");
+     let emptyView = $("#properties-empty");
+@@ -121,43 +121,43 @@ var PropertiesView = {
+    */
+   _onNodeSet: function (id) {
+     this._setAudioNode(gAudioNodes.get(id));
+   },
+ 
+   /**
+    * Executed when an audio prop is changed in the UI.
+    */
+-  _onEval: Task.async(function* (variable, value) {
++  async _onEval(variable, value) {
+     let ownerScope = variable.ownerView;
+     let node = this._currentNode;
+     let propName = variable.name;
+     let error;
+ 
+     if (!variable._initialDescriptor.writable) {
+       error = new Error("Variable " + propName + " is not writable.");
+     } else {
+       // Cast value to proper type
+       try {
+         let number = parseFloat(value);
+         if (!isNaN(number)) {
+           value = number;
+         } else {
+           value = JSON.parse(value);
+         }
+-        error = yield node.actor.setParam(propName, value);
++        error = await node.actor.setParam(propName, value);
+       }
+       catch (e) {
+         error = e;
+       }
+     }
+ 
+     // TODO figure out how to handle and display set prop errors
+     // and enable `test/brorwser_wa_properties-view-edit.js`
+     // Bug 994258
+     if (!error) {
+       ownerScope.get(propName).setGrip(value);
+       window.emit(EVENTS.UI_SET_PARAM, node.id, propName, value);
+     } else {
+       window.emit(EVENTS.UI_SET_PARAM_ERROR, node.id, propName, value);
+     }
+-  })
++  }
+ };

+ 1772 - 0
frg/work-js/mozilla-release/patches/1440321-1r-webconsole-61a1.patch

@@ -0,0 +1,1772 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520762700 -7200
+# Node ID 8e5e21375673e0be523fb43205ba07dfba937617
+# Parent  526e9e16cdb449f465c83223e2fcd5889ee4f8d6
+Bug 1440321 - Convert Task.jsm to async/await in devtools/client - part 1r. r=jryans
+
+MozReview-Commit-ID: HaGOC5cn3JD
+
+diff --git a/devtools/client/webconsole/console-output.js b/devtools/client/webconsole/console-output.js
+--- a/devtools/client/webconsole/console-output.js
++++ b/devtools/client/webconsole/console-output.js
+@@ -14,17 +14,16 @@ loader.lazyImporter(this, "escapeHTML", 
+ loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
+ loader.lazyRequireGetter(this, "TableWidget", "devtools/client/shared/widgets/TableWidget", true);
+ loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
+ 
+ const XHTML_NS = "http://www.w3.org/1999/xhtml";
+ 
+ const WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
+ const { getSourceNames } = require("devtools/client/shared/source-utils");
+-const {Task} = require("devtools/shared/task");
+ const l10n = require("devtools/client/webconsole/webconsole-l10n");
+ const nodeConstants = require("devtools/shared/dom-node-constants");
+ const {PluralForm} = require("devtools/shared/plural-form");
+ const {extend} = require("devtools/shared/extend");
+ const defer = require("devtools/shared/defer");
+ 
+ const MAX_STRING_GRIP_LENGTH = 36;
+ const {ELLIPSIS} = require("devtools/shared/l10n");
+@@ -3080,17 +3079,17 @@ Widgets.ObjectRenderers.add({
+    * will attach mouseover/out event listeners to do so, and the inspector icon
+    * to open the node in the inspector.
+    * @return a promise that resolves when the node has been linked to the
+    * inspector, or rejects if it wasn't (either if no toolbox could be found to
+    * access the inspector, or if the node isn't present in the inspector, i.e.
+    * if the node is in a DocumentFragment or not part of the tree, or not of
+    * type nodeConstants.ELEMENT_NODE).
+    */
+-  linkToInspector: Task.async(function* () {
++  async linkToInspector() {
+     if (this._linkedToInspector) {
+       return;
+     }
+ 
+     // Checking the node type
+     if (this.objectActor.preview.nodeType !== nodeConstants.ELEMENT_NODE) {
+       throw new Error("The object cannot be linked to the inspector as it " +
+         "isn't an element node");
+@@ -3100,18 +3099,18 @@ Widgets.ObjectRenderers.add({
+     let target = this.message.output.toolboxTarget;
+     this.toolbox = gDevTools.getToolbox(target);
+     if (!this.toolbox) {
+       // In cases like the browser console, there is no toolbox.
+       return;
+     }
+ 
+     // Checking that the inspector supports the node
+-    yield this.toolbox.initInspector();
+-    this._nodeFront = yield this.toolbox.walker.getNodeActorFromObjectActor(
++    await this.toolbox.initInspector();
++    this._nodeFront = await this.toolbox.walker.getNodeActorFromObjectActor(
+       this.objectActor.actor);
+     if (!this._nodeFront) {
+       throw new Error("The object cannot be linked to the inspector, the " +
+         "corresponding nodeFront could not be found");
+     }
+ 
+     // At this stage, the message may have been cleared already
+     if (!this.document) {
+@@ -3130,32 +3129,32 @@ Widgets.ObjectRenderers.add({
+     this.unhighlightDomNode = this.unhighlightDomNode.bind(this);
+     this.element.addEventListener("mouseout", this.unhighlightDomNode);
+ 
+     this._openInspectorNode = this._anchor("", {
+       className: "open-inspector",
+       onClick: this.openNodeInInspector.bind(this)
+     });
+     this._openInspectorNode.title = l10n.getStr("openNodeInInspector");
+-  }),
++  },
+ 
+   /**
+    * Highlight the DOMNode corresponding to the ObjectActor in the page.
+    * @return a promise that resolves when the node has been highlighted, or
+    * rejects if the node cannot be highlighted (detached from the DOM)
+    */
+-  highlightDomNode: Task.async(function* () {
+-    yield this.linkToInspector();
+-    let isAttached = yield this.toolbox.walker.isInDOMTree(this._nodeFront);
++  async highlightDomNode() {
++    await this.linkToInspector();
++    let isAttached = await this.toolbox.walker.isInDOMTree(this._nodeFront);
+     if (isAttached) {
+-      yield this.toolbox.highlighterUtils.highlightNodeFront(this._nodeFront);
++      await this.toolbox.highlighterUtils.highlightNodeFront(this._nodeFront);
+     } else {
+       throw new Error("Node is not attached.");
+     }
+-  }),
++  },
+ 
+   /**
+    * Unhighlight a previously highlit node
+    * @see highlightDomNode
+    * @return a promise that resolves when the highlighter has been hidden
+    */
+   unhighlightDomNode: function() {
+     return this.linkToInspector().then(() => {
+@@ -3165,30 +3164,30 @@ Widgets.ObjectRenderers.add({
+ 
+   /**
+    * Open the DOMNode corresponding to the ObjectActor in the inspector panel
+    * @return a promise that resolves when the inspector has been switched to
+    * and the node has been selected, or rejects if the node cannot be selected
+    * (detached from the DOM). Note that in any case, the inspector panel will
+    * be switched to.
+    */
+-  openNodeInInspector: Task.async(function* () {
+-    yield this.linkToInspector();
+-    yield this.toolbox.selectTool("inspector");
+-
+-    let isAttached = yield this.toolbox.walker.isInDOMTree(this._nodeFront);
++  async openNodeInInspector() {
++    await this.linkToInspector();
++    await this.toolbox.selectTool("inspector");
++
++    let isAttached = await this.toolbox.walker.isInDOMTree(this._nodeFront);
+     if (isAttached) {
+       let onReady = defer();
+       this.toolbox.inspector.once("inspector-updated", onReady.resolve);
+-      yield this.toolbox.selection.setNodeFront(this._nodeFront, "console");
+-      yield onReady.promise;
++      await this.toolbox.selection.setNodeFront(this._nodeFront, "console");
++      await onReady.promise;
+     } else {
+       throw new Error("Node is not attached.");
+     }
+-  }),
++  },
+ 
+   destroy: function() {
+     if (this.toolbox && this._nodeFront) {
+       this.element.removeEventListener("mouseover", this.highlightDomNode);
+       this.element.removeEventListener("mouseout", this.unhighlightDomNode);
+       this._openInspectorNode.removeEventListener("mousedown",
+                                                   this.openNodeInInspector,
+                                                   true);
+diff --git a/devtools/client/webconsole/net/test/mochitest/browser_net_basic.js b/devtools/client/webconsole/net/test/mochitest/browser_net_basic.js
+--- a/devtools/client/webconsole/net/test/mochitest/browser_net_basic.js
++++ b/devtools/client/webconsole/net/test/mochitest/browser_net_basic.js
+@@ -8,22 +8,22 @@
+ const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
+ const JSON_XHR_URL = URL_ROOT + "test.json";
+ 
+ /**
+  * Basic test that generates XHR in the content and
+  * checks the related log in the Console panel can
+  * be expanded.
+  */
+-add_task(function* () {
++add_task(async function () {
+   info("Test XHR Spy basic started");
+ 
+-  let {hud} = yield addTestTab(TEST_PAGE_URL);
++  let {hud} = await addTestTab(TEST_PAGE_URL);
+ 
+-  let netInfoBody = yield executeAndInspectXhr(hud, {
++  let netInfoBody = await executeAndInspectXhr(hud, {
+     method: "GET",
+     url: JSON_XHR_URL
+   });
+ 
+   ok(netInfoBody, "The network details must be available");
+ 
+   // There should be at least two tabs: Headers and Response
+   ok(netInfoBody.querySelector(".tabs .tabs-menu-item.headers"),
+diff --git a/devtools/client/webconsole/net/test/mochitest/browser_net_cookies.js b/devtools/client/webconsole/net/test/mochitest/browser_net_cookies.js
+--- a/devtools/client/webconsole/net/test/mochitest/browser_net_cookies.js
++++ b/devtools/client/webconsole/net/test/mochitest/browser_net_cookies.js
+@@ -8,28 +8,28 @@
+ const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
+ const JSON_XHR_URL = URL_ROOT + "test-cookies.json";
+ 
+ /**
+  * This test generates XHR requests in the page, expands
+  * networks details in the Console panel and checks that
+  * Cookies are properly displayed.
+  */
+-add_task(function* () {
++add_task(async function () {
+   info("Test XHR Spy cookies started");
+ 
+-  let {hud} = yield addTestTab(TEST_PAGE_URL);
++  let {hud} = await addTestTab(TEST_PAGE_URL);
+ 
+-  let netInfoBody = yield executeAndInspectXhr(hud, {
++  let netInfoBody = await executeAndInspectXhr(hud, {
+     method: "GET",
+     url: JSON_XHR_URL
+   });
+ 
+   // Select "Cookies" tab
+-  let tabBody = yield selectNetInfoTab(hud, netInfoBody, "cookies");
++  let tabBody = await selectNetInfoTab(hud, netInfoBody, "cookies");
+ 
+   let requestCookieName = tabBody.querySelector(
+     ".netInfoGroup.requestCookies .netInfoParamName > span[title='bar']");
+ 
+   // Verify request cookies (name and value)
+   ok(requestCookieName, "Request Cookie name must exist");
+   is(requestCookieName.textContent, "bar",
+     "The cookie name must have proper value");
+diff --git a/devtools/client/webconsole/net/test/mochitest/browser_net_headers.js b/devtools/client/webconsole/net/test/mochitest/browser_net_headers.js
+--- a/devtools/client/webconsole/net/test/mochitest/browser_net_headers.js
++++ b/devtools/client/webconsole/net/test/mochitest/browser_net_headers.js
+@@ -8,31 +8,31 @@
+ const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
+ const JSON_XHR_URL = URL_ROOT + "test.json";
+ 
+ /**
+  * This test generates XHR requests in the page, expands
+  * networks details in the Console panel and checks that
+  * HTTP headers are there.
+  */
+-add_task(function* () {
++add_task(async function () {
+   // Disable rcwn to make cache behavior deterministic.
+-  yield pushPref("network.http.rcwn.enabled", false);
++  await pushPref("network.http.rcwn.enabled", false);
+ 
+   info("Test XHR Spy headers started");
+ 
+-  let {hud} = yield addTestTab(TEST_PAGE_URL);
++  let {hud} = await addTestTab(TEST_PAGE_URL);
+ 
+-  let netInfoBody = yield executeAndInspectXhr(hud, {
++  let netInfoBody = await executeAndInspectXhr(hud, {
+     method: "GET",
+     url: JSON_XHR_URL
+   });
+ 
+   // Select "Headers" tab
+-  let tabBody = yield selectNetInfoTab(hud, netInfoBody, "headers");
++  let tabBody = await selectNetInfoTab(hud, netInfoBody, "headers");
+   let paramName = tabBody.querySelector(
+     ".netInfoParamName > span[title='content-type']");
+ 
+   // Verify "Content-Type" header (name and value)
+   ok(paramName, "Header name must exist");
+   is(paramName.textContent, "content-type",
+     "The header name must have proper value");
+ 
+diff --git a/devtools/client/webconsole/net/test/mochitest/browser_net_params.js b/devtools/client/webconsole/net/test/mochitest/browser_net_params.js
+--- a/devtools/client/webconsole/net/test/mochitest/browser_net_params.js
++++ b/devtools/client/webconsole/net/test/mochitest/browser_net_params.js
+@@ -8,29 +8,29 @@
+ const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
+ const JSON_XHR_URL = URL_ROOT + "test.json";
+ 
+ /**
+  * This test generates XHR requests in the page, expands
+  * networks details in the Console panel and checks that
+  * HTTP parameters (query string) are there.
+  */
+-add_task(function* () {
++add_task(async function () {
+   info("Test XHR Spy params started");
+ 
+-  let {hud} = yield addTestTab(TEST_PAGE_URL);
++  let {hud} = await addTestTab(TEST_PAGE_URL);
+ 
+-  let netInfoBody = yield executeAndInspectXhr(hud, {
++  let netInfoBody = await executeAndInspectXhr(hud, {
+     method: "GET",
+     url: JSON_XHR_URL,
+     queryString: "?foo=bar"
+   });
+ 
+   // Check headers
+-  let tabBody = yield selectNetInfoTab(hud, netInfoBody, "params");
++  let tabBody = await selectNetInfoTab(hud, netInfoBody, "params");
+ 
+   let paramName = tabBody.querySelector(
+     ".netInfoParamName > span[title='foo']");
+ 
+   // Verify "Content-Type" header (name and value)
+   ok(paramName, "Header name must exist");
+   is(paramName.textContent, "foo",
+     "The param name must have proper value");
+@@ -39,29 +39,29 @@ add_task(function* () {
+   ok(paramValue, "param value must exist");
+   is(paramValue.textContent, "bar",
+     "The param value must have proper value");
+ });
+ 
+ /**
+  * Test URL parameters with the same name.
+  */
+-add_task(function* () {
++add_task(async function () {
+   info("Test XHR Spy params started");
+ 
+-  let {hud} = yield addTestTab(TEST_PAGE_URL);
++  let {hud} = await addTestTab(TEST_PAGE_URL);
+ 
+-  let netInfoBody = yield executeAndInspectXhr(hud, {
++  let netInfoBody = await executeAndInspectXhr(hud, {
+     method: "GET",
+     url: JSON_XHR_URL,
+     queryString: "?box[]=123&box[]=456"
+   });
+ 
+   // Check headers
+-  let tabBody = yield selectNetInfoTab(hud, netInfoBody, "params");
++  let tabBody = await selectNetInfoTab(hud, netInfoBody, "params");
+ 
+   let params = tabBody.querySelectorAll(
+     ".netInfoParamName > span[title='box[]']");
+   is(params.length, 2, "Two URI parameters must exist");
+ 
+   let values = tabBody.querySelectorAll(
+     ".netInfoParamValue > code");
+   is(values[0].textContent, 123, "First value must match");
+diff --git a/devtools/client/webconsole/net/test/mochitest/browser_net_post.js b/devtools/client/webconsole/net/test/mochitest/browser_net_post.js
+--- a/devtools/client/webconsole/net/test/mochitest/browser_net_post.js
++++ b/devtools/client/webconsole/net/test/mochitest/browser_net_post.js
+@@ -13,76 +13,76 @@ const jsonData = "{\"bar\": \"baz\"}";
+ const jsonRendered = "bar\"baz\"";
+ const xmlPostBody = "<xml><name>John</name></xml>";
+ 
+ /**
+  * This test generates XHR requests in the page, expands
+  * networks details in the Console panel and checks that
+  * Post data are properly rendered.
+  */
+-add_task(function* () {
++add_task(async function () {
+   info("Test XHR Spy post plain body started");
+ 
+-  let {hud} = yield addTestTab(TEST_PAGE_URL);
++  let {hud} = await addTestTab(TEST_PAGE_URL);
+ 
+-  let netInfoBody = yield executeAndInspectXhr(hud, {
++  let netInfoBody = await executeAndInspectXhr(hud, {
+     method: "POST",
+     url: JSON_XHR_URL,
+     body: plainPostBody
+   });
+ 
+   // Check post body data
+-  let tabBody = yield selectNetInfoTab(hud, netInfoBody, "post");
++  let tabBody = await selectNetInfoTab(hud, netInfoBody, "post");
+   let postContent = tabBody.querySelector(
+     ".netInfoGroup.raw.opened .netInfoGroupContent");
+   is(postContent.textContent, plainPostBody,
+     "Post body must be properly rendered");
+ });
+ 
+-add_task(function* () {
++add_task(async function () {
+   info("Test XHR Spy post JSON body started");
+ 
+-  let {hud} = yield addTestTab(TEST_PAGE_URL);
++  let {hud} = await addTestTab(TEST_PAGE_URL);
+ 
+-  let netInfoBody = yield executeAndInspectXhr(hud, {
++  let netInfoBody = await executeAndInspectXhr(hud, {
+     method: "POST",
+     url: JSON_XHR_URL,
+     body: jsonData,
+     requestHeaders: [{
+       name: "Content-Type",
+       value: "application/json"
+     }]
+   });
+ 
+   // Check post body data
+-  let tabBody = yield selectNetInfoTab(hud, netInfoBody, "post");
++  let tabBody = await selectNetInfoTab(hud, netInfoBody, "post");
+   let postContent = tabBody.querySelector(
+     ".netInfoGroup.json.opened .netInfoGroupContent");
+   is(postContent.textContent, jsonRendered,
+     "Post body must be properly rendered");
+ 
+   let rawPostContent = tabBody.querySelector(
+     ".netInfoGroup.raw.opened .netInfoGroupContent");
+   ok(!rawPostContent, "Raw response group must be collapsed");
+ });
+ 
+-add_task(function* () {
++add_task(async function () {
+   info("Test XHR Spy post XML body started");
+ 
+-  let {hud} = yield addTestTab(TEST_PAGE_URL);
++  let {hud} = await addTestTab(TEST_PAGE_URL);
+ 
+-  let netInfoBody = yield executeAndInspectXhr(hud, {
++  let netInfoBody = await executeAndInspectXhr(hud, {
+     method: "POST",
+     url: JSON_XHR_URL,
+     body: xmlPostBody,
+     requestHeaders: [{
+       name: "Content-Type",
+       value: "application/xml"
+     }]
+   });
+ 
+   // Check post body data
+-  let tabBody = yield selectNetInfoTab(hud, netInfoBody, "post");
++  let tabBody = await selectNetInfoTab(hud, netInfoBody, "post");
+   let rawPostContent = tabBody.querySelector(
+     ".netInfoGroup.raw.opened .netInfoGroupContent");
+   is(rawPostContent.textContent, xmlPostBody,
+     "Raw response group must not be collapsed");
+ });
+diff --git a/devtools/client/webconsole/net/test/mochitest/browser_net_response.js b/devtools/client/webconsole/net/test/mochitest/browser_net_response.js
+--- a/devtools/client/webconsole/net/test/mochitest/browser_net_response.js
++++ b/devtools/client/webconsole/net/test/mochitest/browser_net_response.js
+@@ -15,70 +15,70 @@ const jsonResponseBody = "name\"John\"";
+ 
+ // Individual tests below generate XHR request in the page, expand
+ // network details in the Console panel and checks various types
+ // of response bodies.
+ 
+ /**
+  * Validate plain text response
+  */
+-add_task(function* () {
++add_task(async function () {
+   info("Test XHR Spy respone plain body started");
+ 
+-  let {hud} = yield addTestTab(TEST_PAGE_URL);
++  let {hud} = await addTestTab(TEST_PAGE_URL);
+ 
+-  let netInfoBody = yield executeAndInspectXhr(hud, {
++  let netInfoBody = await executeAndInspectXhr(hud, {
+     method: "GET",
+     url: TEXT_XHR_URL,
+   });
+ 
+   // Check response body data
+-  let tabBody = yield selectNetInfoTab(hud, netInfoBody, "response");
++  let tabBody = await selectNetInfoTab(hud, netInfoBody, "response");
+   let responseContent = tabBody.querySelector(
+     ".netInfoGroup.raw.opened .netInfoGroupContent");
+ 
+   ok(responseContent.textContent.indexOf(textResponseBody) > -1,
+     "Response body must be properly rendered");
+ });
+ 
+ /**
+  * Validate XML response
+  */
+-add_task(function* () {
++add_task(async function () {
+   info("Test XHR Spy response XML body started");
+ 
+-  let {hud} = yield addTestTab(TEST_PAGE_URL);
++  let {hud} = await addTestTab(TEST_PAGE_URL);
+ 
+-  let netInfoBody = yield executeAndInspectXhr(hud, {
++  let netInfoBody = await executeAndInspectXhr(hud, {
+     method: "GET",
+     url: XML_XHR_URL,
+   });
+ 
+   // Check response body data
+-  let tabBody = yield selectNetInfoTab(hud, netInfoBody, "response");
++  let tabBody = await selectNetInfoTab(hud, netInfoBody, "response");
+   let rawResponseContent = tabBody.querySelector(
+     ".netInfoGroup.raw.opened .netInfoGroupContent");
+   ok(rawResponseContent, "Raw response group must not be collapsed");
+ });
+ 
+ /**
+  * Validate JSON response
+  */
+-add_task(function* () {
++add_task(async function () {
+   info("Test XHR Spy response JSON body started");
+ 
+-  let {hud} = yield addTestTab(TEST_PAGE_URL);
++  let {hud} = await addTestTab(TEST_PAGE_URL);
+ 
+-  let netInfoBody = yield executeAndInspectXhr(hud, {
++  let netInfoBody = await executeAndInspectXhr(hud, {
+     method: "GET",
+     url: JSON_XHR_URL,
+   });
+ 
+   // Check response body data
+-  let tabBody = yield selectNetInfoTab(hud, netInfoBody, "response");
++  let tabBody = await selectNetInfoTab(hud, netInfoBody, "response");
+   let responseContent = tabBody.querySelector(
+     ".netInfoGroup.json .netInfoGroupContent");
+ 
+   is(responseContent.textContent, jsonResponseBody,
+     "Response body must be properly rendered");
+ 
+   let rawResponseContent = tabBody.querySelector(
+     ".netInfoGroup.raw.opened .netInfoGroupContent");
+diff --git a/devtools/client/webconsole/net/test/mochitest/head.js b/devtools/client/webconsole/net/test/mochitest/head.js
+--- a/devtools/client/webconsole/net/test/mochitest/head.js
++++ b/devtools/client/webconsole/net/test/mochitest/head.js
+@@ -20,43 +20,43 @@ Services.prefs.setBoolPref(NET_XHR_PREF,
+ registerCleanupFunction(() => {
+   Services.prefs.clearUserPref(NET_INFO_PREF);
+   Services.prefs.clearUserPref(NET_XHR_PREF);
+ });
+ 
+ // Use the old webconsole since the new one doesn't yet support
+ // XHR spy. See Bug 1304794.
+ Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
+-registerCleanupFunction(function* () {
++registerCleanupFunction(function() {
+   Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
+ });
+ 
+ /**
+  * Add a new test tab in the browser and load the given url.
+  * @param {String} url The url to be loaded in the new tab
+  * @return a promise that resolves to the tab object when the url is loaded
+  */
+ function addTestTab(url) {
+   info("Adding a new JSON tab with URL: '" + url + "'");
+ 
+-  return Task.spawn(function* () {
+-    let tab = yield addTab(url);
++  return (async function () {
++    let tab = await addTab(url);
+ 
+     // Load devtools/shared/test/frame-script-utils.js
+     loadFrameScriptUtils(tab.linkedBrowser);
+ 
+     // Open the Console panel
+-    let hud = yield openConsole();
++    let hud = await openConsole();
+ 
+     return {
+       tab: tab,
+       browser: tab.linkedBrowser,
+       hud: hud
+     };
+-  });
++  })();
+ }
+ 
+ /**
+  *
+  * @param hud
+  * @param options
+  */
+ function executeAndInspectXhr(hud, options) {
+@@ -68,41 +68,41 @@ function executeAndInspectXhr(hud, optio
+   performRequestsInContent({
+     method: options.method,
+     url: options.url + options.queryString,
+     body: options.body,
+     nocache: options.nocache,
+     requestHeaders: options.requestHeaders
+   });
+ 
+-  return Task.spawn(function* () {
++  return (async function () {
+     // Wait till the appropriate Net log appears in the Console panel.
+-    let rules = yield waitForMessages({
++    let rules = await waitForMessages({
+       webconsole: hud,
+       messages: [{
+         text: options.url,
+         category: CATEGORY_NETWORK,
+         severity: SEVERITY_INFO,
+         isXhr: true,
+       }]
+     });
+ 
+     // The log is here, get its parent element (className: 'message').
+     let msg = [...rules[0].matched][0];
+     let body = msg.querySelector(".message-body");
+ 
+     // Open XHR HTTP details body and wait till the UI fetches
+     // all necessary data from the backend. All RPD requests
+     // needs to be finished before we can continue testing.
+-    yield synthesizeMouseClickSoon(hud, body);
+-    yield waitForBackend(msg);
++    await synthesizeMouseClickSoon(hud, body);
++    await waitForBackend(msg);
+     let netInfoBody = body.querySelector(".netInfoBody");
+     ok(netInfoBody, "Net info body must exist");
+     return netInfoBody;
+-  });
++  })();
+ }
+ 
+ /**
+  * Wait till XHR data are fetched from the backend (i.e. there are
+  * no pending RDP requests.
+  */
+ function waitForBackend(element) {
+   if (!element.hasAttribute("loading")) {
+@@ -123,24 +123,24 @@ function waitForBackend(element) {
+ function selectNetInfoTab(hud, netInfoBody, tabId) {
+   let tab = netInfoBody.querySelector(".tabs-menu-item." + tabId);
+   ok(tab, "Tab must exist " + tabId);
+ 
+   // Click to select specified tab and wait till its
+   // UI is populated with data from the backend.
+   // There must be no pending RDP requests before we can
+   // continue testing the UI.
+-  return Task.spawn(function* () {
+-    yield synthesizeMouseClickSoon(hud, tab);
++  return (async function () {
++    await synthesizeMouseClickSoon(hud, tab);
+     let msg = getAncestorByClass(netInfoBody, "message");
+-    yield waitForBackend(msg);
++    await waitForBackend(msg);
+     let tabBody = netInfoBody.querySelector("." + tabId + "TabBox");
+     ok(tabBody, "Tab body must exist");
+     return tabBody;
+-  });
++  })();
+ }
+ 
+ /**
+  * Return parent node with specified class.
+  *
+  * @param node A child element
+  * @param className Specified class name.
+  *
+diff --git a/devtools/client/webconsole/new-console-output/test/chrome/head.js b/devtools/client/webconsole/new-console-output/test/chrome/head.js
+--- a/devtools/client/webconsole/new-console-output/test/chrome/head.js
++++ b/devtools/client/webconsole/new-console-output/test/chrome/head.js
+@@ -1,14 +1,12 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ /* exported Task, browserRequire */
+ 
+ "use strict";
+ 
+-var { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ var { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
+-var { Task } = require("devtools/shared/task");
+ 
+ var { require: browserRequire } = BrowserLoader({
+   baseURI: "resource://devtools/client/webconsole/",
+   window
+ });
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_console_api.js b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_console_api.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_console_api.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_console_api.js
+@@ -2,21 +2,21 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ 
+-add_task(function* () {
+-  let generatedStubs = yield generateConsoleApiStubs();
++add_task(async function() {
++  let generatedStubs = await generateConsoleApiStubs();
+ 
+   let repoStubFilePath = getTestFilePath("../stubs/consoleApi.js");
+-  let repoStubFileContent = yield OS.File.read(repoStubFilePath, { encoding: "utf-8" });
++  let repoStubFileContent = await OS.File.read(repoStubFilePath, { encoding: "utf-8" });
+ 
+   if (generatedStubs != repoStubFileContent) {
+     ok(false, "The consoleApi stubs file needs to be updated by running " +
+       "`mach test devtools/client/webconsole/new-console-output/test/fixtures/" +
+       "stub-generators/browser_webconsole_update_stubs_console_api.js`");
+   } else {
+     ok(true, "The consoleApi stubs file is up to date");
+   }
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_css_message.js b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_css_message.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_css_message.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_css_message.js
+@@ -2,21 +2,21 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ 
+-add_task(function* () {
+-  let generatedStubs = yield generateCssMessageStubs();
++add_task(async function() {
++  let generatedStubs = await generateCssMessageStubs();
+ 
+   let repoStubFilePath = getTestFilePath("../stubs/cssMessage.js");
+-  let repoStubFileContent = yield OS.File.read(repoStubFilePath, { encoding: "utf-8" });
++  let repoStubFileContent = await OS.File.read(repoStubFilePath, { encoding: "utf-8" });
+ 
+   if (generatedStubs != repoStubFileContent) {
+     ok(false, "The cssMessage stubs file needs to be updated by running " +
+       "`mach test devtools/client/webconsole/new-console-output/test/fixtures/" +
+       "stub-generators/browser_webconsole_update_stubs_css_message.js`");
+   } else {
+     ok(true, "The cssMessage stubs file is up to date");
+   }
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_evaluation_result.js b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_evaluation_result.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_evaluation_result.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_evaluation_result.js
+@@ -2,21 +2,21 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ 
+-add_task(function* () {
+-  let generatedStubs = yield generateEvaluationResultStubs();
++add_task(async function() {
++  let generatedStubs = await generateEvaluationResultStubs();
+ 
+   let repoStubFilePath = getTestFilePath("../stubs/evaluationResult.js");
+-  let repoStubFileContent = yield OS.File.read(repoStubFilePath, { encoding: "utf-8" });
++  let repoStubFileContent = await OS.File.read(repoStubFilePath, { encoding: "utf-8" });
+ 
+   if (generatedStubs != repoStubFileContent) {
+     ok(false, "The evaluationResult stubs file needs to be updated by running " +
+       "`mach test devtools/client/webconsole/new-console-output/test/fixtures/" +
+       "stub-generators/browser_webconsole_update_stubs_evaluation_result.js`");
+   } else {
+     ok(true, "The evaluationResult stubs file is up to date");
+   }
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_network_event.js b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_network_event.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_network_event.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_network_event.js
+@@ -2,21 +2,21 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ 
+-add_task(function* () {
+-  let generatedStubs = yield generateNetworkEventStubs();
++add_task(async function() {
++  let generatedStubs = await generateNetworkEventStubs();
+ 
+   let repoStubFilePath = getTestFilePath("../stubs/networkEvent.js");
+-  let repoStubFileContent = yield OS.File.read(repoStubFilePath, { encoding: "utf-8" });
++  let repoStubFileContent = await OS.File.read(repoStubFilePath, { encoding: "utf-8" });
+ 
+   is(generatedStubs, repoStubFileContent, "Generated stub has the expected content");
+   if (generatedStubs != repoStubFileContent) {
+     ok(false, "The networkEvent stubs file needs to be updated by running " +
+       "`mach test devtools/client/webconsole/new-console-output/test/fixtures/" +
+       "stub-generators/browser_webconsole_update_stubs_network_event.js`");
+   } else {
+     ok(true, "The networkEvent stubs file is up to date");
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_page_error.js b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_page_error.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_page_error.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_page_error.js
+@@ -2,21 +2,21 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ 
+-add_task(function* () {
+-  let generatedStubs = yield generatePageErrorStubs();
++add_task(async function() {
++  let generatedStubs = await generatePageErrorStubs();
+ 
+   let repoStubFilePath = getTestFilePath("../stubs/pageError.js");
+-  let repoStubFileContent = yield OS.File.read(repoStubFilePath, { encoding: "utf-8" });
++  let repoStubFileContent = await OS.File.read(repoStubFilePath, { encoding: "utf-8" });
+ 
+   if (generatedStubs != repoStubFileContent) {
+     ok(false, "The pageError stubs file needs to be updated by running " +
+       "`mach test devtools/client/webconsole/new-console-output/test/fixtures/" +
+       "stub-generators/browser_webconsole_update_stubs_page_error.js`");
+   } else {
+     ok(true, "The pageError stubs file is up to date");
+   }
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_console_api.js b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_console_api.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_console_api.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_console_api.js
+@@ -2,14 +2,14 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ 
+-add_task(function* () {
+-  let fileContent = yield generateConsoleApiStubs();
++add_task(async function() {
++  let fileContent = await generateConsoleApiStubs();
+   let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "consoleApi.js");
+-  yield OS.File.writeAtomic(filePath, fileContent);
++  await OS.File.writeAtomic(filePath, fileContent);
+   ok(true, "Make the test not fail");
+ });
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_css_message.js b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_css_message.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_css_message.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_css_message.js
+@@ -1,13 +1,13 @@
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ 
+-add_task(function* () {
+-  let fileContent = yield generateCssMessageStubs();
++add_task(async function() {
++  let fileContent = await generateCssMessageStubs();
+   let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "cssMessage.js");
+-  yield OS.File.writeAtomic(filePath, fileContent);
++  await OS.File.writeAtomic(filePath, fileContent);
+   ok(true, "Make the test not fail");
+ });
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_evaluation_result.js b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_evaluation_result.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_evaluation_result.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_evaluation_result.js
+@@ -2,14 +2,14 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ 
+-add_task(function* () {
+-  let fileContent = yield generateEvaluationResultStubs();
++add_task(async function() {
++  let fileContent = await generateEvaluationResultStubs();
+   let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "evaluationResult.js");
+-  yield OS.File.writeAtomic(filePath, fileContent);
++  await OS.File.writeAtomic(filePath, fileContent);
+   ok(true, "Make the test not fail");
+ });
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_network_event.js b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_network_event.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_network_event.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_network_event.js
+@@ -2,14 +2,14 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ 
+-add_task(function* () {
+-  let fileContent = yield generateNetworkEventStubs();
++add_task(async function() {
++  let fileContent = await generateNetworkEventStubs();
+   let filePath = OS.Path.join(`${BASE_PATH}/stubs/networkEvent.js`);
+-  yield OS.File.writeAtomic(filePath, fileContent);
++  await OS.File.writeAtomic(filePath, fileContent);
+   ok(true, "Make the test not fail");
+ });
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_page_error.js b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_page_error.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_page_error.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_page_error.js
+@@ -2,14 +2,14 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ 
+-add_task(function* () {
+-  let fileContent = yield generatePageErrorStubs();
++add_task(async function() {
++  let fileContent = await generatePageErrorStubs();
+   let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "pageError.js");
+-  yield OS.File.writeAtomic(filePath, fileContent);
++  await OS.File.writeAtomic(filePath, fileContent);
+   ok(true, "Make the test not fail");
+ });
+diff --git a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js
+--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js
++++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js
+@@ -288,28 +288,28 @@ let stubPackets = new Map();
+ 
+ module.exports = {
+   stubPreparedMessages,
+   stubPackets,
+ };
+ `;
+ }
+ 
+-function* generateConsoleApiStubs() {
++async function generateConsoleApiStubs() {
+   const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html";
+ 
+   // Hiding log messages so we don't get unwanted client/server communication.
+   Services.prefs.setBoolPref(PREFS.FILTER.LOG, false);
+ 
+   let stubs = {
+     preparedMessages: [],
+     packets: [],
+   };
+ 
+-  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
++  let toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
+   const hud = toolbox.getCurrentPanel().hud;
+   let {ui} = hud;
+   ok(ui.jsterm, "jsterm exists");
+   ok(ui.newConsoleOutput, "newConsoleOutput exists");
+ 
+   for (let [key, {keys, code}] of consoleApi) {
+     let received = new Promise(resolve => {
+       let i = 0;
+@@ -320,111 +320,111 @@ function* generateConsoleApiStubs() {
+         if (++i === keys.length) {
+           toolbox.target.client.removeListener("consoleAPICall", listener);
+           resolve();
+         }
+       };
+       toolbox.target.client.addListener("consoleAPICall", listener);
+     });
+ 
+-    yield ContentTask.spawn(
++    await ContentTask.spawn(
+       gBrowser.selectedBrowser,
+       [key, code],
+       function([subKey, subCode]) {
+         let script = content.document.createElement("script");
+         // eslint-disable-next-line no-unsanitized/property
+         script.innerHTML = `function triggerPacket() {${subCode}}`;
+         content.document.body.appendChild(script);
+         content.wrappedJSObject.triggerPacket();
+         script.remove();
+       }
+     );
+ 
+-    yield received;
++    await received;
+   }
+ 
+   Services.prefs.clearUserPref(PREFS.FILTER.LOG);
+ 
+-  yield closeTabAndToolbox();
++  await closeTabAndToolbox();
+   return formatFile(stubs, "ConsoleMessage");
+ }
+ 
+-function* generateCssMessageStubs() {
++async function generateCssMessageStubs() {
+   const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html";
+ 
+   let stubs = {
+     preparedMessages: [],
+     packets: [],
+   };
+ 
+-  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
++  let toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
+ 
+   for (let [key, code] of cssMessage) {
+     let received = new Promise(resolve => {
+       /* CSS errors are considered as pageError on the server */
+       toolbox.target.client.addListener("pageError", function onPacket(e, packet) {
+         toolbox.target.client.removeListener("pageError", onPacket);
+         info("Received css message:" + e + " " + JSON.stringify(packet, null, "\t"));
+ 
+         let message = prepareMessage(packet, {getNextId: () => 1});
+         stubs.packets.push(formatPacket(message.messageText, packet));
+         stubs.preparedMessages.push(formatStub(message.messageText, packet));
+         resolve();
+       });
+     });
+ 
+-    yield ContentTask.spawn(
++    await ContentTask.spawn(
+       gBrowser.selectedBrowser,
+       [key, code],
+       function([subKey, subCode]) {
+         let style = content.document.createElement("style");
+         // eslint-disable-next-line no-unsanitized/property
+         style.innerHTML = subCode;
+         content.document.body.appendChild(style);
+       }
+     );
+ 
+-    yield received;
++    await received;
+   }
+ 
+-  yield closeTabAndToolbox();
++  await closeTabAndToolbox();
+   return formatFile(stubs, "ConsoleMessage");
+ }
+ 
+-function* generateEvaluationResultStubs() {
++async function generateEvaluationResultStubs() {
+   const TEST_URI = "data:text/html;charset=utf-8,stub generation";
+ 
+   let stubs = {
+     preparedMessages: [],
+     packets: [],
+   };
+ 
+-  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
++  let toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
+ 
+   for (let [key, code] of evaluationResult) {
+-    const packet = yield new Promise(resolve => {
++    const packet = await new Promise(resolve => {
+       toolbox.target.activeConsole.evaluateJS(code, resolve);
+     });
+     stubs.packets.push(formatPacket(key, packet));
+     stubs.preparedMessages.push(formatStub(key, packet));
+   }
+ 
+-  yield closeTabAndToolbox();
++  await closeTabAndToolbox();
+   return formatFile(stubs, "ConsoleMessage");
+ }
+ 
+-function* generateNetworkEventStubs() {
++async function generateNetworkEventStubs() {
+   const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html";
+ 
+   let stubs = {
+     preparedMessages: [],
+     packets: [],
+   };
+ 
+-  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
++  let toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
+   let {ui} = toolbox.getCurrentPanel().hud;
+ 
+   for (let [key, {keys, code}] of networkEvent) {
+     let onNetwork = new Promise(resolve => {
+       let i = 0;
+       toolbox.target.activeConsole.on("networkEvent", function onNetworkEvent(type, res) {
+         stubs.packets.push(formatPacket(keys[i], res));
+         stubs.preparedMessages.push(formatNetworkEventStub(keys[i], res));
+@@ -456,45 +456,45 @@ function* generateNetworkEventStubs() {
+         stubs.preparedMessages.push(formatNetworkEventStub(updateKey, res));
+         if (i === keys.length) {
+           ui.jsterm.hud.off("network-message-updated", onNetworkUpdated);
+           resolve();
+         }
+       });
+     });
+ 
+-    yield ContentTask.spawn(
++    await ContentTask.spawn(
+       gBrowser.selectedBrowser,
+       [key, code],
+       function([subKey, subCode]) {
+         let script = content.document.createElement("script");
+         // eslint-disable-next-line no-unsanitized/property
+         script.innerHTML = `function triggerPacket() {${subCode}}`;
+         content.document.body.appendChild(script);
+         content.wrappedJSObject.triggerPacket();
+         script.remove();
+       }
+     );
+ 
+-    yield Promise.all([onNetwork, onNetworkUpdate]);
++    await Promise.all([onNetwork, onNetworkUpdate]);
+   }
+ 
+-  yield closeTabAndToolbox();
++  await closeTabAndToolbox();
+   return formatFile(stubs, "NetworkEventMessage");
+ }
+ 
+-function* generatePageErrorStubs() {
++async function generatePageErrorStubs() {
+   const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html";
+ 
+   let stubs = {
+     preparedMessages: [],
+     packets: [],
+   };
+ 
+-  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
++  let toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
+ 
+   for (let [key, code] of pageError) {
+     let received = new Promise(resolve => {
+       toolbox.target.client.addListener("pageError", function onPacket(e, packet) {
+         toolbox.target.client.removeListener("pageError", onPacket);
+         stubs.packets.push(formatPacket(key, packet));
+         stubs.preparedMessages.push(formatStub(key, packet));
+         resolve();
+@@ -503,26 +503,26 @@ function* generatePageErrorStubs() {
+ 
+     // On e10s, the exception is triggered in child process
+     // and is ignored by test harness
+     // expectUncaughtException should be called for each uncaught exception.
+     if (!Services.appinfo.browserTabsRemoteAutostart) {
+       expectUncaughtException();
+     }
+ 
+-    yield ContentTask.spawn(
++    await ContentTask.spawn(
+       gBrowser.selectedBrowser,
+       [key, code],
+       function([subKey, subCode]) {
+         let script = content.document.createElement("script");
+         // eslint-disable-next-line no-unsanitized/property
+         script.innerHTML = subCode;
+         content.document.body.appendChild(script);
+         script.remove();
+       }
+     );
+ 
+-    yield received;
++    await received;
+   }
+ 
+-  yield closeTabAndToolbox();
++  await closeTabAndToolbox();
+   return formatFile(stubs, "ConsoleMessage");
+ }
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_clear_method.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_clear_method.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_clear_method.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_clear_method.js
+@@ -6,31 +6,31 @@
+ /* import-globals-from head.js */
+ 
+ // Check that console.clear() does not clear the output of the browser console.
+ 
+ "use strict";
+ 
+ const TEST_URI = "data:text/html;charset=utf8,<p>Bug 1296870";
+ 
+-add_task(function* () {
+-  yield loadTab(TEST_URI);
+-  let hud = yield HUDService.toggleBrowserConsole();
++add_task(async function() {
++  await loadTab(TEST_URI);
++  let hud = await HUDService.toggleBrowserConsole();
+ 
+   info("Log a new message from the content page");
+-  ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
++  ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
+     content.wrappedJSObject.console.log("msg");
+   });
+-  yield waitForMessage("msg", hud);
++  await waitForMessage("msg", hud);
+ 
+   info("Send a console.clear() from the content page");
+-  ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
++  ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
+     content.wrappedJSObject.console.clear();
+   });
+-  yield waitForMessage("Console was cleared", hud);
++  await waitForMessage("Console was cleared", hud);
+ 
+   info("Check that the messages logged after the first clear are still displayed");
+   isnot(hud.outputNode.textContent.indexOf("msg"), -1, "msg is in the output");
+ });
+ 
+ function waitForMessage(message, webconsole) {
+   return waitForMessages({
+     webconsole,
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_webconsole_private_browsing.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_webconsole_private_browsing.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_webconsole_private_browsing.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_webconsole_private_browsing.js
+@@ -36,17 +36,17 @@ function test() {
+     gBrowser.selectedBrowser.removeEventListener("load", onLoadTab, true);
+     info("onLoadTab()");
+ 
+     // Make sure we have a clean state to start with.
+     Services.console.reset();
+     ConsoleAPIStorage.clearEvents();
+ 
+     // Add a non-private message to the browser console.
+-    ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
++    ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
+       content.console.log("bug874061-not-private");
+     });
+ 
+     nonPrivateMessage = {
+       name: "console message from a non-private window",
+       text: "bug874061-not-private",
+       category: CATEGORY_WEBDEV,
+       severity: SEVERITY_LOG,
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_closure_inspection.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_closure_inspection.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_closure_inspection.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_closure_inspection.js
+@@ -12,17 +12,17 @@
+ 
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+                  "new-console-output/test/mochitest/test-closures.html";
+ 
+ var gWebConsole, gJSTerm, gVariablesView;
+ 
+ // Force the old debugger UI since it's directly used (see Bug 1301705)
+ Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+-registerCleanupFunction(function* () {
++registerCleanupFunction(function() {
+   Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+ });
+ 
+ function test() {
+   registerCleanupFunction(() => {
+     gWebConsole = gJSTerm = gVariablesView = null;
+   });
+ 
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_console_table.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_console_table.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_console_table.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_console_table.js
+@@ -6,18 +6,18 @@
+ "use strict";
+ 
+ // Check console.table calls with all the test cases shown
+ // in the MDN doc (https://developer.mozilla.org/en-US/docs/Web/API/Console/table)
+ 
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+                  "new-console-output/test/mochitest/test-console-table.html";
+ 
+-add_task(function* () {
+-  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
++add_task(async function() {
++  let toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
+   let hud = toolbox.getCurrentPanel().hud;
+ 
+   function Person(firstName, lastName) {
+     this.firstName = firstName;
+     this.lastName = lastName;
+   }
+ 
+   const testCases = [{
+@@ -126,24 +126,24 @@ add_task(function* () {
+       columns: ["(index)", "a", "b", "c", "d", "e"],
+       rows: [
+         ["0", "null", "false", "undefined", "0", "undefined"],
+         ["1", "undefined", "null", "false", "undefined", "0"],
+       ]
+     }
+   }];
+ 
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, testCases, function(tests) {
++  await ContentTask.spawn(gBrowser.selectedBrowser, testCases, function(tests) {
+     tests.forEach((test) => {
+       content.wrappedJSObject.doConsoleTable(test.input, test.headers);
+     });
+   });
+   let nodes = [];
+   for (let testCase of testCases) {
+-    let node = yield waitFor(
++    let node = await waitFor(
+       () => findConsoleTable(hud.ui.outputNode, testCases.indexOf(testCase))
+     );
+     nodes.push(node);
+   }
+   let consoleTableNodes = hud.ui.outputNode.querySelectorAll(
+     ".message .new-consoletable");
+   is(consoleTableNodes.length, testCases.length,
+     "console has the expected number of consoleTable items");
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_file_uri.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_file_uri.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_file_uri.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_file_uri.js
+@@ -7,41 +7,41 @@
+ 
+ // See Bug 595223.
+ 
+ const PREF = "devtools.webconsole.persistlog";
+ const TEST_FILE = "test-network.html";
+ 
+ var hud;
+ 
+-add_task(function* () {
++add_task(async function() {
+   Services.prefs.setBoolPref(PREF, true);
+ 
+   let jar = getJar(getRootDirectory(gTestPath));
+   let dir = jar ?
+             extractJarToTmp(jar) :
+             getChromeDir(getResolvedURI(gTestPath));
+ 
+   dir.append(TEST_FILE);
+   let uri = Services.io.newFileURI(dir);
+ 
+   // Open tab with correct remote type so we don't switch processes when we load
+   // the file:// URI, otherwise we won't get the same web console.
+   let remoteType = E10SUtils.getRemoteTypeForURI(uri.spec,
+                                                  gMultiProcessBrowser);
+-  let { browser } = yield loadTab("about:blank", remoteType);
++  let { browser } = await loadTab("about:blank", remoteType);
+ 
+-  hud = yield openConsole();
++  hud = await openConsole();
+   hud.jsterm.clearOutput();
+ 
+   let loaded = loadBrowser(browser);
+   BrowserTestUtils.loadURI(gBrowser.selectedBrowser, uri.spec);
+-  yield loaded;
++  await loaded;
+ 
+-  yield testMessages();
++  await testMessages();
+ 
+   Services.prefs.clearUserPref(PREF);
+   hud = null;
+ });
+ 
+ function testMessages() {
+   return waitForMessages({
+     webconsole: hud,
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_init.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_init.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_init.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_init.js
+@@ -3,35 +3,35 @@
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+                  "new-console-output/test/mochitest/test-console.html";
+ 
+-add_task(function* () {
+-  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
++add_task(async function() {
++  let toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
+   let hud = toolbox.getCurrentPanel().hud;
+   let {ui} = hud;
+ 
+   ok(ui.jsterm, "jsterm exists");
+   ok(ui.newConsoleOutput, "newConsoleOutput exists");
+ 
+   let receievedMessages = waitForMessages({
+     hud,
+     messages: [
+       { text: "19" },
+     ]
+   });
+ 
+-  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
++  await ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
+     content.wrappedJSObject.doLogs(20);
+   });
+ 
+-  yield receievedMessages;
++  await receievedMessages;
+ 
+   const outputContainer = ui.outputNode.querySelector(".webconsole-output");
+   is(outputContainer.querySelectorAll(".message.console-api").length, 20,
+     "Correct number of messages appear");
+   is(outputContainer.scrollWidth, outputContainer.clientWidth,
+     "No horizontal overflow");
+ });
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_input_field_focus_on_panel_select.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_input_field_focus_on_panel_select.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_input_field_focus_on_panel_select.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_input_field_focus_on_panel_select.js
+@@ -7,30 +7,30 @@
+ 
+ // Test that the JS input field is focused when the user switches back to the
+ // web console from other tools, see bug 891581.
+ 
+ "use strict";
+ 
+ const TEST_URI = "data:text/html;charset=utf8,<p>hello";
+ 
+-add_task(function* () {
+-  yield loadTab(TEST_URI);
+-  let hud = yield openConsole();
++add_task(async function() {
++  await loadTab(TEST_URI);
++  let hud = await openConsole();
+   hud.jsterm.clearOutput();
+ 
+   is(hud.jsterm.inputNode.hasAttribute("focused"), true,
+      "inputNode should be focused");
+ 
+   hud.ui.filterBox.focus();
+ 
+   is(hud.ui.filterBox.hasAttribute("focused"), true,
+      "filterBox should be focused");
+ 
+   is(hud.jsterm.inputNode.hasAttribute("focused"), false,
+      "inputNode shouldn't be focused");
+ 
+-  yield openInspector();
+-  hud = yield openConsole();
++  await openInspector();
++  hud = await openConsole();
+ 
+   is(hud.jsterm.inputNode.hasAttribute("focused"), true,
+      "inputNode should be focused");
+ });
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_location_scratchpad_link.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_location_scratchpad_link.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_location_scratchpad_link.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_location_scratchpad_link.js
+@@ -2,54 +2,54 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_URI = "data:text/html;charset=utf8,<p>test Scratchpad panel linking</p>";
+ 
+-add_task(function* () {
+-  yield pushPref("devtools.scratchpad.enabled", true);
+-  yield openNewTabAndToolbox(TEST_URI);
++add_task(async function() {
++  await pushPref("devtools.scratchpad.enabled", true);
++  await openNewTabAndToolbox(TEST_URI);
+ 
+   info("Opening toolbox with Scratchpad panel");
+ 
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  let toolbox = yield gDevTools.showToolbox(target, "scratchpad", "window");
++  let toolbox = await gDevTools.showToolbox(target, "scratchpad", "window");
+ 
+   let scratchpadPanel = toolbox.getPanel("scratchpad");
+   let { scratchpad } = scratchpadPanel;
+   is(toolbox.getCurrentPanel(), scratchpadPanel,
+     "Scratchpad is currently selected panel");
+ 
+   info("Switching to webconsole panel");
+ 
+-  let webconsolePanel = yield toolbox.selectTool("webconsole");
++  let webconsolePanel = await toolbox.selectTool("webconsole");
+   let { hud } = webconsolePanel;
+   is(toolbox.getCurrentPanel(), webconsolePanel,
+     "Webconsole is currently selected panel");
+ 
+   info("console.log()ing from Scratchpad");
+ 
+   let messageText = "foobar-from-scratchpad";
+   scratchpad.setText(`console.log('${messageText}')`);
+   scratchpad.run();
+-  let message = yield waitFor(() => findMessage(hud, messageText));
++  let message = await waitFor(() => findMessage(hud, messageText));
+ 
+   info("Clicking link to switch to and focus Scratchpad");
+ 
+   ok(message, "Found logged message from Scratchpad");
+   let anchor = message.querySelector(".message-location .frame-link-filename");
+ 
+   let onScratchpadSelected = new Promise((resolve) => {
+     toolbox.once("scratchpad-selected", resolve);
+   });
+ 
+   EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
+-  yield onScratchpadSelected;
++  await onScratchpadSelected;
+ 
+   is(toolbox.getCurrentPanel(), scratchpadPanel,
+     "Clicking link switches to Scratchpad panel");
+ 
+   is(Services.ww.activeWindow, toolbox.win.parent,
+      "Scratchpad's toolbox is focused");
+ });
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_loglimit.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_loglimit.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_loglimit.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_loglimit.js
+@@ -11,30 +11,30 @@ const TEST_URI = "data:text/html;charset
+                  "Old messages are removed after passing devtools.hud.loglimit";
+ 
+ add_task(async function() {
+   await pushPref("devtools.hud.loglimit", 140);
+   let hud = await openNewTabAndConsole(TEST_URI);
+   hud.jsterm.clearOutput();
+ 
+   let onMessage = waitForMessage(hud, "test message [149]");
+-  ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
++  ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
+     for (let i = 0; i < 150; i++) {
+       content.console.log(`test message [${i}]`);
+     }
+   });
+   await onMessage;
+ 
+   ok(!findMessage(hud, "test message [0]"), "Message 0 has been pruned");
+   ok(!findMessage(hud, "test message [9]"), "Message 9 has been pruned");
+   ok(findMessage(hud, "test message [10]"), "Message 10 is still displayed");
+   is(findMessages(hud, "").length, 140, "Number of displayed messages is correct");
+ 
+   onMessage = waitForMessage(hud, "hello world");
+-  ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
++  ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
+     content.console.log("hello world");
+   });
+   await onMessage;
+ 
+   ok(!findMessage(hud, "test message [10]"), "Message 10 has been pruned");
+   ok(findMessage(hud, "test message [11]"), "Message 11 is still displayed");
+   is(findMessages(hud, "").length, 140, "Number of displayed messages is still correct");
+ });
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_longstring_expand.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_longstring_expand.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_longstring_expand.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_longstring_expand.js
+@@ -7,74 +7,74 @@
+ 
+ // Test that long strings can be expanded in the console output.
+ 
+ "use strict";
+ 
+ const TEST_URI = "data:text/html;charset=utf8,test for bug 787981 - check " +
+                  "that long strings can be expanded in the output.";
+ 
+-add_task(function* () {
++add_task(async function() {
+   let { DebuggerServer } = require("devtools/server/main");
+ 
+   let longString = (new Array(DebuggerServer.LONG_STRING_LENGTH + 4))
+                     .join("a") + "foobar";
+   let initialString =
+     longString.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
+ 
+-  yield loadTab(TEST_URI);
++  await loadTab(TEST_URI);
+ 
+-  let hud = yield openConsole();
++  let hud = await openConsole();
+ 
+   hud.jsterm.clearOutput(true);
+   hud.jsterm.execute("console.log('bazbaz', '" + longString + "', 'boom')");
+ 
+-  let [result] = yield waitForMessages({
++  let [result] = await waitForMessages({
+     webconsole: hud,
+     messages: [{
+       name: "console.log output",
+       text: ["bazbaz", "boom", initialString],
+       noText: "foobar",
+       longString: true,
+     }],
+   });
+ 
+   let clickable = result.longStrings[0];
+   ok(clickable, "long string ellipsis is shown");
+ 
+   clickable.scrollIntoView(false);
+ 
+   EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+ 
+-  yield waitForMessages({
++  await waitForMessages({
+     webconsole: hud,
+     messages: [{
+       name: "full string",
+       text: ["bazbaz", "boom", longString],
+       category: CATEGORY_WEBDEV,
+       longString: false,
+     }],
+   });
+ 
+   hud.jsterm.clearOutput(true);
+-  let msg = yield execute(hud, "'" + longString + "'");
++  let msg = await execute(hud, "'" + longString + "'");
+ 
+   isnot(msg.textContent.indexOf(initialString), -1,
+       "initial string is shown");
+   is(msg.textContent.indexOf(longString), -1,
+       "full string is not shown");
+ 
+   clickable = msg.querySelector(".longStringEllipsis");
+   ok(clickable, "long string ellipsis is shown");
+ 
+   clickable.scrollIntoView(false);
+ 
+   EventUtils.synthesizeMouse(clickable, 3, 4, {}, hud.iframeWindow);
+ 
+-  yield waitForMessages({
++  await waitForMessages({
+     webconsole: hud,
+     messages: [{
+       name: "full string",
+       text: longString,
+       category: CATEGORY_OUTPUT,
+       longString: false,
+     }],
+   });
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_longstring_hang.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_longstring_hang.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_longstring_hang.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_longstring_hang.js
+@@ -8,24 +8,24 @@
+ // Test that very long strings do not hang the browser.
+ 
+ "use strict";
+ 
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+                  "new-console-output/test/mochitest/" +
+                  "test-bug-859170-longstring-hang.html";
+ 
+-add_task(function* () {
+-  yield loadTab(TEST_URI);
++add_task(async function() {
++  await loadTab(TEST_URI);
+ 
+-  let hud = yield openConsole();
++  let hud = await openConsole();
+ 
+   info("wait for the initial long string");
+ 
+-  let results = yield waitForMessages({
++  let results = await waitForMessages({
+     webconsole: hud,
+     messages: [
+       {
+         name: "find 'foobar', no 'foobaz', in long string output",
+         text: "foobar",
+         noText: "foobaz",
+         category: CATEGORY_WEBDEV,
+         longString: true,
+@@ -36,17 +36,17 @@ add_task(function* () {
+   let clickable = results[0].longStrings[0];
+   ok(clickable, "long string ellipsis is shown");
+   clickable.scrollIntoView(false);
+ 
+   EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+ 
+   info("wait for long string expansion");
+ 
+-  yield waitForMessages({
++  await waitForMessages({
+     webconsole: hud,
+     messages: [
+       {
+         name: "find 'foobaz' after expand, but no 'boom!' at the end",
+         text: "foobaz",
+         noText: "boom!",
+         category: CATEGORY_WEBDEV,
+         longString: false,
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_message_categories.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_message_categories.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_message_categories.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_message_categories.js
+@@ -25,17 +25,17 @@ const TESTS = [
+     matchString: "shape=\"rect\"",
+   },
+   {
+     // #2
+     file: "test-message-categories-html.html",
+     category: "HTML",
+     matchString: "multipart/form-data",
+     onload: function() {
+-      ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
++      ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
+         let form = content.document.querySelector("form");
+         form.submit();
+       });
+     },
+   },
+   {
+     // #3
+     file: "test-message-categories-workers.html",
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector_while_debugging_and_inspecting.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector_while_debugging_and_inspecting.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector_while_debugging_and_inspecting.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector_while_debugging_and_inspecting.js
+@@ -68,13 +68,13 @@ add_task(async function() {
+ async function waitForFrameAdded() {
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+   let toolbox = gDevTools.getToolbox(target);
+   let thread = toolbox.threadClient;
+ 
+   info("Waiting for framesadded");
+   await new Promise(resolve => {
+     thread.addOneTimeListener("framesadded", resolve);
+-    ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
++    ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
+       content.wrappedJSObject.firstCall();
+     });
+   });
+ }
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_optimized_out_vars.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_optimized_out_vars.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_optimized_out_vars.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_optimized_out_vars.js
+@@ -24,17 +24,17 @@ add_task(async function() {
+   let sources = debuggerPanel.panelWin.DebuggerView.Sources;
+   await debuggerPanel.addBreakpoint({ actor: sources.values[0], line: 18 });
+   await ensureThreadClientState(debuggerPanel, "resumed");
+ 
+   let { FETCHED_SCOPES } = debuggerPanel.panelWin.EVENTS;
+   let fetchedScopes = debuggerPanel.panelWin.once(FETCHED_SCOPES);
+ 
+   // Cause the debuggee to pause
+-  ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
++  ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
+     let button = content.document.querySelector("button");
+     button.click();
+   });
+ 
+   await fetchedScopes;
+   ok(true, "Scopes were fetched");
+ 
+   await toolbox.selectTool("webconsole");
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_stacktrace_location_scratchpad_link.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_stacktrace_location_scratchpad_link.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_stacktrace_location_scratchpad_link.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_stacktrace_location_scratchpad_link.js
+@@ -2,33 +2,33 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ const TEST_URI = "data:text/html;charset=utf8,<p>test stacktrace scratchpad linking</p>";
+ 
+-add_task(function* () {
+-  yield pushPref("devtools.scratchpad.enabled", true);
+-  yield openNewTabAndToolbox(TEST_URI);
++add_task(async function() {
++  await pushPref("devtools.scratchpad.enabled", true);
++  await openNewTabAndToolbox(TEST_URI);
+ 
+   info("Opening toolbox with Scratchpad panel");
+ 
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+-  let toolbox = yield gDevTools.showToolbox(target, "scratchpad", "window");
++  let toolbox = await gDevTools.showToolbox(target, "scratchpad", "window");
+ 
+   let scratchpadPanel = toolbox.getPanel("scratchpad");
+   let { scratchpad } = scratchpadPanel;
+   is(toolbox.getCurrentPanel(), scratchpadPanel,
+     "Scratchpad is currently selected panel");
+ 
+   info("Switching to webconsole panel");
+ 
+-  let webconsolePanel = yield toolbox.selectTool("webconsole");
++  let webconsolePanel = await toolbox.selectTool("webconsole");
+   let { hud } = webconsolePanel;
+   is(toolbox.getCurrentPanel(), webconsolePanel,
+     "Webconsole is currently selected panel");
+ 
+   info("console.trace()ing from Scratchpad");
+ 
+   scratchpad.setText(`
+     function foo() {
+@@ -37,26 +37,26 @@ add_task(function* () {
+ 
+     function bar() {
+       console.trace();
+     }
+ 
+     foo();
+   `);
+   scratchpad.run();
+-  let message = yield waitFor(() => findMessage(hud, "console.trace()"));
++  let message = await waitFor(() => findMessage(hud, "console.trace()"));
+ 
+   info("Clicking link to switch to and focus Scratchpad");
+ 
+   ok(message, "Found console.trace message from Scratchpad");
+   let anchor = message.querySelector(".stack-trace .frame-link .frame-link-filename");
+ 
+   let onScratchpadSelected = toolbox.once("scratchpad-selected");
+ 
+   EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
+-  yield onScratchpadSelected;
++  await onScratchpadSelected;
+ 
+   is(toolbox.getCurrentPanel(), scratchpadPanel,
+     "Clicking link in stacktrace switches to Scratchpad panel");
+ 
+   is(Services.ww.activeWindow, toolbox.win.parent,
+      "Scratchpad's toolbox is focused");
+ });

+ 73 - 0
frg/work-js/mozilla-release/patches/1440321-2-61a1.patch

@@ -0,0 +1,73 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520763000 -7200
+# Node ID f79b38183eba5c1601eea3b036cc7beda3e9d981
+# Parent  b615e2c10ed95edd447e2bac0aaa79aa426b052f
+Bug 1440321 - Manually convert generators from devtools/client/memory/test/browser/. r=jryans
+
+diff --git a/devtools/client/memory/test/browser/browser_memory_tree_map-01.js b/devtools/client/memory/test/browser/browser_memory_tree_map-01.js
+--- a/devtools/client/memory/test/browser/browser_memory_tree_map-01.js
++++ b/devtools/client/memory/test/browser/browser_memory_tree_map-01.js
+@@ -6,17 +6,17 @@
+ 
+ "use strict";
+ 
+ const CanvasUtils = require("devtools/client/memory/components/tree-map/canvas-utils");
+ const D3_SCRIPT = '<script type="application/javascript" ' +
+                   'src="chrome://devtools/content/shared/vendor/d3.js>';
+ const TEST_URL = `data:text/html,<html><body>${D3_SCRIPT}</body></html>`;
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function ({ tab, panel }) {
+   const document = panel.panelWin.document;
+   const window = panel.panelWin;
+   const div = document.createElement("div");
+ 
+   Object.assign(div.style, {
+     width: "100px",
+     height: "200px",
+     position: "absolute"
+diff --git a/devtools/client/memory/test/browser/browser_memory_tree_map-02.js b/devtools/client/memory/test/browser/browser_memory_tree_map-02.js
+--- a/devtools/client/memory/test/browser/browser_memory_tree_map-02.js
++++ b/devtools/client/memory/test/browser/browser_memory_tree_map-02.js
+@@ -9,17 +9,17 @@
+ const CanvasUtils = require("devtools/client/memory/components/tree-map/canvas-utils");
+ const DragZoom = require("devtools/client/memory/components/tree-map/drag-zoom");
+ 
+ const TEST_URL = "data:text/html,<html><body></body></html>";
+ const PIXEL_SCROLL_MODE = 0;
+ const PIXEL_DELTA = 10;
+ const MAX_RAF_LOOP = 1000;
+ 
+-this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function ({ tab, panel }) {
+   const panelWin = panel.panelWin;
+   const panelDoc = panelWin.document;
+   const div = panelDoc.createElement("div");
+ 
+   Object.assign(div.style, {
+     width: "100px",
+     height: "200px",
+     position: "absolute",
+diff --git a/devtools/client/memory/test/browser/head.js b/devtools/client/memory/test/browser/head.js
+--- a/devtools/client/memory/test/browser/head.js
++++ b/devtools/client/memory/test/browser/head.js
+@@ -43,17 +43,17 @@ this.closeMemoryPanel = async function(t
+ 
+ /**
+  * Return a test function that adds a tab with the given url, opens the memory
+  * panel, runs the given generator, closes the memory panel, removes the tab,
+  * and finishes.
+  *
+  * Example usage:
+  *
+- *     this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
++ *     this.test = makeMemoryTest(TEST_URL, async function ({ tab, panel }) {
+  *         // Your tests go here...
+  *     });
+  */
+ function makeMemoryTest(url, generator) {
+   return async function() {
+     waitForExplicitFinish();
+ 
+     // It can take a long time to save a snapshot to disk, read the snapshots

+ 29 - 0
frg/work-js/mozilla-release/patches/1440321-3-61a1.patch

@@ -0,0 +1,29 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1520763060 -7200
+# Node ID 896864ff931dd3d58f8cd55085a912d9b3a4de9c
+# Parent  aef06d93c4f5e4da78bc168852d5207553c81ea7
+Bug 1440321 - Fix race within browser_net_view-source-debugger.js r=jryans
+
+diff --git a/devtools/client/netmonitor/test/browser_net_view-source-debugger.js b/devtools/client/netmonitor/test/browser_net_view-source-debugger.js
+--- a/devtools/client/netmonitor/test/browser_net_view-source-debugger.js
++++ b/devtools/client/netmonitor/test/browser_net_view-source-debugger.js
+@@ -56,16 +56,17 @@ async function checkClickOnNode(toolbox,
+ 
+   // create the promise
+   const onJsDebuggerSelected = toolbox.once("jsdebugger-selected");
+   // Fire the click event
+   frameLinkNode.querySelector(".frame-link-source").click();
+   // wait for the promise to resolve
+   await onJsDebuggerSelected;
+ 
+-  let dbg = toolbox.getPanel("jsdebugger");
++  let dbg = await toolbox.getPanelWhenReady("jsdebugger");
++  await waitUntil(() => dbg._selectors.getSelectedSource(dbg._getState()));
+   is(
+     dbg._selectors.getSelectedSource(dbg._getState()).get("url"),
+     url,
+     "expected source url"
+   );
+ }
+ 

+ 53 - 0
frg/work-js/mozilla-release/patches/1440321-4-61a1.patch

@@ -0,0 +1,53 @@
+# HG changeset patch
+# User J. Ryan Stinnett <jryans@gmail.com>
+# Date 1520902423 18000
+# Node ID 9788b85f2fb26eb6d5c3346216a9e1c3584faa76
+# Parent  d14bca88dd1395025e5f880e1c0558158ebc3985
+Bug 1440321 - Follow up linting fix in Memory tool tests. r=me
+
+MozReview-Commit-ID: Jr8Mic4whwl
+
+diff --git a/devtools/client/memory/test/browser/browser_memory_tree_map-01.js b/devtools/client/memory/test/browser/browser_memory_tree_map-01.js
+--- a/devtools/client/memory/test/browser/browser_memory_tree_map-01.js
++++ b/devtools/client/memory/test/browser/browser_memory_tree_map-01.js
+@@ -6,17 +6,17 @@
+ 
+ "use strict";
+ 
+ const CanvasUtils = require("devtools/client/memory/components/tree-map/canvas-utils");
+ const D3_SCRIPT = '<script type="application/javascript" ' +
+                   'src="chrome://devtools/content/shared/vendor/d3.js>';
+ const TEST_URL = `data:text/html,<html><body>${D3_SCRIPT}</body></html>`;
+ 
+-this.test = makeMemoryTest(TEST_URL, async function ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const document = panel.panelWin.document;
+   const window = panel.panelWin;
+   const div = document.createElement("div");
+ 
+   Object.assign(div.style, {
+     width: "100px",
+     height: "200px",
+     position: "absolute"
+diff --git a/devtools/client/memory/test/browser/browser_memory_tree_map-02.js b/devtools/client/memory/test/browser/browser_memory_tree_map-02.js
+--- a/devtools/client/memory/test/browser/browser_memory_tree_map-02.js
++++ b/devtools/client/memory/test/browser/browser_memory_tree_map-02.js
+@@ -9,17 +9,17 @@
+ const CanvasUtils = require("devtools/client/memory/components/tree-map/canvas-utils");
+ const DragZoom = require("devtools/client/memory/components/tree-map/drag-zoom");
+ 
+ const TEST_URL = "data:text/html,<html><body></body></html>";
+ const PIXEL_SCROLL_MODE = 0;
+ const PIXEL_DELTA = 10;
+ const MAX_RAF_LOOP = 1000;
+ 
+-this.test = makeMemoryTest(TEST_URL, async function ({ tab, panel }) {
++this.test = makeMemoryTest(TEST_URL, async function({ tab, panel }) {
+   const panelWin = panel.panelWin;
+   const panelDoc = panelWin.document;
+   const div = panelDoc.createElement("div");
+ 
+   Object.assign(div.style, {
+     width: "100px",
+     height: "200px",
+     position: "absolute",

+ 1355 - 0
frg/work-js/mozilla-release/patches/1441225-60a1.patch

@@ -0,0 +1,1355 @@
+# HG changeset patch
+# User Gabriel Luong <gabriel.luong@gmail.com>
+# Date 1519702297 18000
+# Node ID 706c1a934a7d97932cceacfa8f818dd8c6df75f2
+# Parent  d9b5e43c6fee4978341a5bfdef74af5ade345bc4
+Bug 1441225 - Update Codemirror to 5.35.0. r=bgrins
+
+diff --git a/devtools/client/sourceeditor/README b/devtools/client/sourceeditor/README
+--- a/devtools/client/sourceeditor/README
++++ b/devtools/client/sourceeditor/README
+@@ -1,16 +1,16 @@
+ This is the CodeMirror editor packaged for the Mozilla Project. CodeMirror
+ is a JavaScript component that provides a code editor in the browser. When
+ a mode is available for the language you are coding in, it will color your
+ code, and optionally help with indentation.
+ 
+ # Upgrade
+ 
+-Currently used version is 5.34.0. To upgrade: download a new version of
++Currently used version is 5.35.0. To upgrade: download a new version of
+ CodeMirror from the project's page [1] and replace all JavaScript and
+ CSS files inside the codemirror directory [2].
+ 
+ Then to recreate codemirror.bundle.js:
+  > cd devtools/client/sourceeditor
+  > npm install
+  > webpack
+ 
+diff --git a/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js b/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js
+--- a/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js
++++ b/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js
+@@ -85,17 +85,17 @@
+     clearTimeout(state.timeout);
+     state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay);
+   }
+ 
+   function addOverlay(cm, query, hasBoundary, style) {
+     var state = cm.state.matchHighlighter;
+     cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
+     if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
+-      var searchFor = hasBoundary ? new RegExp("\\b" + query + "\\b") : query;
++      var searchFor = hasBoundary ? new RegExp("\\b" + query.replace(/[\\\[+*?(){|^$]/g, "\\$&") + "\\b") : query;
+       state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
+         {className: "CodeMirror-selection-highlight-scrollbar"});
+     }
+   }
+ 
+   function removeOverlay(cm) {
+     var state = cm.state.matchHighlighter;
+     if (state.overlay) {
+diff --git a/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js b/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js
+--- a/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js
++++ b/devtools/client/sourceeditor/codemirror/addon/search/searchcursor.js
+@@ -14,48 +14,52 @@
+ 
+   function regexpFlags(regexp) {
+     var flags = regexp.flags
+     return flags != null ? flags : (regexp.ignoreCase ? "i" : "")
+       + (regexp.global ? "g" : "")
+       + (regexp.multiline ? "m" : "")
+   }
+ 
+-  function ensureGlobal(regexp) {
+-    return regexp.global ? regexp : new RegExp(regexp.source, regexpFlags(regexp) + "g")
++  function ensureFlags(regexp, flags) {
++    var current = regexpFlags(regexp), target = current
++    for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)
++      target += flags.charAt(i)
++    return current == target ? regexp : new RegExp(regexp.source, target)
+   }
+ 
+   function maybeMultiline(regexp) {
+     return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)
+   }
+ 
+   function searchRegexpForward(doc, regexp, start) {
+-    regexp = ensureGlobal(regexp)
++    regexp = ensureFlags(regexp, "g")
+     for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {
+       regexp.lastIndex = ch
+       var string = doc.getLine(line), match = regexp.exec(string)
+       if (match)
+         return {from: Pos(line, match.index),
+                 to: Pos(line, match.index + match[0].length),
+                 match: match}
+     }
+   }
+ 
+   function searchRegexpForwardMultiline(doc, regexp, start) {
+     if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)
+ 
+-    regexp = ensureGlobal(regexp)
++    regexp = ensureFlags(regexp, "gm")
+     var string, chunk = 1
+     for (var line = start.line, last = doc.lastLine(); line <= last;) {
+       // This grows the search buffer in exponentially-sized chunks
+       // between matches, so that nearby matches are fast and don't
+       // require concatenating the whole document (in case we're
+       // searching for something that has tons of matches), but at the
+       // same time, the amount of retries is limited.
+       for (var i = 0; i < chunk; i++) {
++        if (line > last) break
+         var curLine = doc.getLine(line++)
+         string = string == null ? curLine : string + "\n" + curLine
+       }
+       chunk = chunk * 2
+       regexp.lastIndex = start.ch
+       var match = regexp.exec(string)
+       if (match) {
+         var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
+@@ -76,30 +80,30 @@
+       if (!newMatch) return match
+       match = newMatch
+       cutOff = match.index + (match[0].length || 1)
+       if (cutOff == string.length) return match
+     }
+   }
+ 
+   function searchRegexpBackward(doc, regexp, start) {
+-    regexp = ensureGlobal(regexp)
++    regexp = ensureFlags(regexp, "g")
+     for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {
+       var string = doc.getLine(line)
+       if (ch > -1) string = string.slice(0, ch)
+       var match = lastMatchIn(string, regexp)
+       if (match)
+         return {from: Pos(line, match.index),
+                 to: Pos(line, match.index + match[0].length),
+                 match: match}
+     }
+   }
+ 
+   function searchRegexpBackwardMultiline(doc, regexp, start) {
+-    regexp = ensureGlobal(regexp)
++    regexp = ensureFlags(regexp, "gm")
+     var string, chunk = 1
+     for (var line = start.line, first = doc.firstLine(); line >= first;) {
+       for (var i = 0; i < chunk; i++) {
+         var curLine = doc.getLine(line--)
+         string = string == null ? curLine.slice(0, start.ch) : curLine + "\n" + string
+       }
+       chunk *= 2
+ 
+@@ -208,17 +212,17 @@
+     }
+ 
+     if (typeof query == "string") {
+       if (caseFold == null) caseFold = false
+       this.matches = function(reverse, pos) {
+         return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
+       }
+     } else {
+-      query = ensureGlobal(query)
++      query = ensureFlags(query, "gm")
+       if (!options || options.multiline !== false)
+         this.matches = function(reverse, pos) {
+           return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
+         }
+       else
+         this.matches = function(reverse, pos) {
+           return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
+         }
+diff --git a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+--- a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
++++ b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+@@ -5449,17 +5449,18 @@ var CodeMirror =
+ 	      rebased.push(doc.history)
+ 	    }
+ 	    makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
+ 	  })
+ 	}
+ 
+ 	// Revert a change stored in a document's history.
+ 	function makeChangeFromHistory(doc, type, allowSelectionOnly) {
+-	  if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
++	  var suppress = doc.cm && doc.cm.state.suppressEdits
++	  if (suppress && !allowSelectionOnly) { return }
+ 
+ 	  var hist = doc.history, event, selAfter = doc.sel
+ 	  var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done
+ 
+ 	  // Verify that there is a useable event (so that ctrl-z won't
+ 	  // needlessly clear selection events)
+ 	  var i = 0
+ 	  for (; i < source.length; i++) {
+@@ -5474,18 +5475,20 @@ var CodeMirror =
+ 	    event = source.pop()
+ 	    if (event.ranges) {
+ 	      pushSelectionToHistory(event, dest)
+ 	      if (allowSelectionOnly && !event.equals(doc.sel)) {
+ 	        setSelection(doc, event, {clearRedo: false})
+ 	        return
+ 	      }
+ 	      selAfter = event
+-	    }
+-	    else { break }
++	    } else if (suppress) {
++	      source.push(event)
++	      return
++	    } else { break }
+ 	  }
+ 
+ 	  // Build up a reverse change object to add to the opposite history
+ 	  // stack (redo when undoing, and vice versa).
+ 	  var antiChanges = []
+ 	  pushSelectionToHistory(selAfter, dest)
+ 	  dest.push({changes: antiChanges, generation: hist.generation})
+ 	  hist.generation = event.generation || ++hist.maxGeneration
+@@ -5951,17 +5954,17 @@ var CodeMirror =
+ 	    if (cm && !lineIsHidden(doc, line)) {
+ 	      var aboveVisible = heightAtLine(line) < doc.scrollTop
+ 	      updateLineHeight(line, line.height + widgetHeight(widget))
+ 	      if (aboveVisible) { addToScrollTop(cm, widget.height) }
+ 	      cm.curOp.forceUpdate = true
+ 	    }
+ 	    return true
+ 	  })
+-	  signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle))
++	  if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)) }
+ 	  return widget
+ 	}
+ 
+ 	// TEXTMARKERS
+ 
+ 	// Created with markText and setBookmark methods. A TextMarker is a
+ 	// handle that can be used to clear or find a marked position in the
+ 	// document. Line objects hold arrays (markedSpans) containing
+@@ -9887,17 +9890,17 @@ var CodeMirror =
+ 	CodeMirror.defineDocExtension = function (name, func) {
+ 	  Doc.prototype[name] = func
+ 	}
+ 
+ 	CodeMirror.fromTextArea = fromTextArea
+ 
+ 	addLegacyProps(CodeMirror)
+ 
+-	CodeMirror.version = "5.34.0"
++	CodeMirror.version = "5.35.0"
+ 
+ 	return CodeMirror;
+ 
+ 	})));
+ 
+ /***/ }),
+ /* 3 */
+ /***/ (function(module, exports, __webpack_require__) {
+@@ -9918,48 +9921,52 @@ var CodeMirror =
+ 
+ 	  function regexpFlags(regexp) {
+ 	    var flags = regexp.flags
+ 	    return flags != null ? flags : (regexp.ignoreCase ? "i" : "")
+ 	      + (regexp.global ? "g" : "")
+ 	      + (regexp.multiline ? "m" : "")
+ 	  }
+ 
+-	  function ensureGlobal(regexp) {
+-	    return regexp.global ? regexp : new RegExp(regexp.source, regexpFlags(regexp) + "g")
++	  function ensureFlags(regexp, flags) {
++	    var current = regexpFlags(regexp), target = current
++	    for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)
++	      target += flags.charAt(i)
++	    return current == target ? regexp : new RegExp(regexp.source, target)
+ 	  }
+ 
+ 	  function maybeMultiline(regexp) {
+ 	    return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)
+ 	  }
+ 
+ 	  function searchRegexpForward(doc, regexp, start) {
+-	    regexp = ensureGlobal(regexp)
++	    regexp = ensureFlags(regexp, "g")
+ 	    for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {
+ 	      regexp.lastIndex = ch
+ 	      var string = doc.getLine(line), match = regexp.exec(string)
+ 	      if (match)
+ 	        return {from: Pos(line, match.index),
+ 	                to: Pos(line, match.index + match[0].length),
+ 	                match: match}
+ 	    }
+ 	  }
+ 
+ 	  function searchRegexpForwardMultiline(doc, regexp, start) {
+ 	    if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)
+ 
+-	    regexp = ensureGlobal(regexp)
++	    regexp = ensureFlags(regexp, "gm")
+ 	    var string, chunk = 1
+ 	    for (var line = start.line, last = doc.lastLine(); line <= last;) {
+ 	      // This grows the search buffer in exponentially-sized chunks
+ 	      // between matches, so that nearby matches are fast and don't
+ 	      // require concatenating the whole document (in case we're
+ 	      // searching for something that has tons of matches), but at the
+ 	      // same time, the amount of retries is limited.
+ 	      for (var i = 0; i < chunk; i++) {
++	        if (line > last) break
+ 	        var curLine = doc.getLine(line++)
+ 	        string = string == null ? curLine : string + "\n" + curLine
+ 	      }
+ 	      chunk = chunk * 2
+ 	      regexp.lastIndex = start.ch
+ 	      var match = regexp.exec(string)
+ 	      if (match) {
+ 	        var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
+@@ -9980,30 +9987,30 @@ var CodeMirror =
+ 	      if (!newMatch) return match
+ 	      match = newMatch
+ 	      cutOff = match.index + (match[0].length || 1)
+ 	      if (cutOff == string.length) return match
+ 	    }
+ 	  }
+ 
+ 	  function searchRegexpBackward(doc, regexp, start) {
+-	    regexp = ensureGlobal(regexp)
++	    regexp = ensureFlags(regexp, "g")
+ 	    for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {
+ 	      var string = doc.getLine(line)
+ 	      if (ch > -1) string = string.slice(0, ch)
+ 	      var match = lastMatchIn(string, regexp)
+ 	      if (match)
+ 	        return {from: Pos(line, match.index),
+ 	                to: Pos(line, match.index + match[0].length),
+ 	                match: match}
+ 	    }
+ 	  }
+ 
+ 	  function searchRegexpBackwardMultiline(doc, regexp, start) {
+-	    regexp = ensureGlobal(regexp)
++	    regexp = ensureFlags(regexp, "gm")
+ 	    var string, chunk = 1
+ 	    for (var line = start.line, first = doc.firstLine(); line >= first;) {
+ 	      for (var i = 0; i < chunk; i++) {
+ 	        var curLine = doc.getLine(line--)
+ 	        string = string == null ? curLine.slice(0, start.ch) : curLine + "\n" + string
+ 	      }
+ 	      chunk *= 2
+ 
+@@ -10112,17 +10119,17 @@ var CodeMirror =
+ 	    }
+ 
+ 	    if (typeof query == "string") {
+ 	      if (caseFold == null) caseFold = false
+ 	      this.matches = function(reverse, pos) {
+ 	        return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
+ 	      }
+ 	    } else {
+-	      query = ensureGlobal(query)
++	      query = ensureFlags(query, "gm")
+ 	      if (!options || options.multiline !== false)
+ 	        this.matches = function(reverse, pos) {
+ 	          return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
+ 	        }
+ 	      else
+ 	        this.matches = function(reverse, pos) {
+ 	          return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
+ 	        }
+@@ -11433,16 +11440,17 @@ var CodeMirror =
+ 	    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
+ 	    if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
+ 	    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
+ 	    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
+ 	    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
+ 	    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
+ 	    if (type == "quasi") return pass(quasi, maybeop);
+ 	    if (type == "new") return cont(maybeTarget(noComma));
++	    if (type == "import") return cont(expression);
+ 	    return cont();
+ 	  }
+ 	  function maybeexpression(type) {
+ 	    if (type.match(/[;\}\)\],]/)) return pass();
+ 	    return pass(expression);
+ 	  }
+ 
+ 	  function maybeoperatorComma(type, value) {
+@@ -11627,17 +11635,17 @@ var CodeMirror =
+ 	    }
+ 	  }
+ 	  function typearg(type) {
+ 	    if (type == "variable") return cont(typearg)
+ 	    else if (type == ":") return cont(typeexpr)
+ 	  }
+ 	  function afterType(type, value) {
+ 	    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+-	    if (value == "|" || type == ".") return cont(typeexpr)
++	    if (value == "|" || type == "." || value == "&") return cont(typeexpr)
+ 	    if (type == "[") return cont(expect("]"), afterType)
+ 	    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
+ 	  }
+ 	  function maybeTypeArgs(_, value) {
+ 	    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+ 	  }
+ 	  function typeparam() {
+ 	    return pass(typeexpr, maybeTypeDefault)
+@@ -11670,17 +11678,18 @@ var CodeMirror =
+ 	    if (value == "=") return cont(expressionNoComma);
+ 	  }
+ 	  function vardefCont(type) {
+ 	    if (type == ",") return cont(vardef);
+ 	  }
+ 	  function maybeelse(type, value) {
+ 	    if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
+ 	  }
+-	  function forspec(type) {
++	  function forspec(type, value) {
++	    if (value == "await") return cont(forspec);
+ 	    if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
+ 	  }
+ 	  function forspec1(type) {
+ 	    if (type == "var") return cont(vardef, expect(";"), forspec2);
+ 	    if (type == ";") return cont(forspec2);
+ 	    if (type == "variable") return cont(formaybeinof);
+ 	    return pass(expression, expect(";"), forspec2);
+ 	  }
+@@ -11759,16 +11768,17 @@ var CodeMirror =
+ 	    return pass(statement);
+ 	  }
+ 	  function exportField(type, value) {
+ 	    if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
+ 	    if (type == "variable") return pass(expressionNoComma, exportField);
+ 	  }
+ 	  function afterImport(type) {
+ 	    if (type == "string") return cont();
++	    if (type == "(") return pass(expression);
+ 	    return pass(importSpec, maybeMoreImports, maybeFrom);
+ 	  }
+ 	  function importSpec(type, value) {
+ 	    if (type == "{") return contCommasep(importSpec, "}");
+ 	    if (type == "variable") register(value);
+ 	    if (value == "*") cx.marked = "keyword";
+ 	    return cont(maybeAs);
+ 	  }
+@@ -14411,33 +14421,33 @@ var CodeMirror =
+ 	    name: "clike",
+ 	    keywords: words(cKeywords),
+ 	    types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " +
+ 	                 "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " +
+ 	                 "uint32_t uint64_t"),
+ 	    blockKeywords: words("case do else for if switch while struct"),
+ 	    defKeywords: words("struct"),
+ 	    typeFirstDefinitions: true,
+-	    atoms: words("null true false"),
++	    atoms: words("NULL true false"),
+ 	    hooks: {"#": cppHook, "*": pointerHook},
+ 	    modeProps: {fold: ["brace", "include"]}
+ 	  });
+ 
+ 	  def(["text/x-c++src", "text/x-c++hdr"], {
+ 	    name: "clike",
+ 	    keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " +
+ 	                    "static_cast typeid catch operator template typename class friend private " +
+ 	                    "this using const_cast inline public throw virtual delete mutable protected " +
+ 	                    "alignas alignof constexpr decltype nullptr noexcept thread_local final " +
+ 	                    "static_assert override"),
+ 	    types: words(cTypes + " bool wchar_t"),
+ 	    blockKeywords: words("catch class do else finally for if struct switch try while"),
+ 	    defKeywords: words("class namespace struct enum union"),
+ 	    typeFirstDefinitions: true,
+-	    atoms: words("true false null"),
++	    atoms: words("true false NULL"),
+ 	    dontIndentStatements: /^template$/,
+ 	    isIdentifierChar: /[\w\$_~\xa1-\uffff]/,
+ 	    hooks: {
+ 	      "#": cppHook,
+ 	      "*": pointerHook,
+ 	      "u": cpp11StringHook,
+ 	      "U": cpp11StringHook,
+ 	      "L": cpp11StringHook,
+@@ -14634,32 +14644,34 @@ var CodeMirror =
+ 	      return "string";
+ 	    }
+ 	  }
+ 
+ 	  def("text/x-kotlin", {
+ 	    name: "clike",
+ 	    keywords: words(
+ 	      /*keywords*/
+-	      "package as typealias class interface this super val " +
+-	      "var fun for is in This throw return " +
++	      "package as typealias class interface this super val operator " +
++	      "var fun for is in This throw return annotation " +
+ 	      "break continue object if else while do try when !in !is as? " +
+ 
+ 	      /*soft keywords*/
+ 	      "file import where by get set abstract enum open inner override private public internal " +
+ 	      "protected catch finally out final vararg reified dynamic companion constructor init " +
+ 	      "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
+-	      "external annotation crossinline const operator infix suspend actual expect"
++	      "external annotation crossinline const operator infix suspend actual expect setparam"
+ 	    ),
+ 	    types: words(
+ 	      /* package java.lang */
+ 	      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+ 	      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+ 	      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+-	      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
++	      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray " +
++	      "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " +
++	      "LazyThreadSafetyMode LongArray Nothing ShortArray Unit"
+ 	    ),
+ 	    intendSwitch: false,
+ 	    indentStatements: false,
+ 	    multiLineStrings: true,
+ 	    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+ 	    blockKeywords: words("catch class do else finally for if where try while enum"),
+ 	    defKeywords: words("class val var object interface fun"),
+ 	    atoms: words("true false null this"),
+@@ -16443,17 +16455,17 @@ var CodeMirror =
+ 	            }
+ 	            vimGlobalState.macroModeState.lastInsertModeChanges.changes.pop();
+ 	          }
+ 	          clearInputState(cm);
+ 	          return match.command;
+ 	        }
+ 
+ 	        function handleKeyNonInsertMode() {
+-	          if (handleMacroRecording() || handleEsc()) { return true; };
++	          if (handleMacroRecording() || handleEsc()) { return true; }
+ 
+ 	          var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
+ 	          if (/^[1-9]\d*$/.test(keys)) { return true; }
+ 
+ 	          var keysMatcher = /^(\d*)(.*)$/.exec(keys);
+ 	          if (!keysMatcher) { clearInputState(cm); return false; }
+ 	          var context = vim.visualMode ? 'visual' :
+ 	                                         'normal';
+@@ -17028,17 +17040,17 @@ var CodeMirror =
+ 	          }
+ 	        }
+ 	        if (command.type == 'keyToEx') {
+ 	          // Handle user defined Ex to Ex mappings
+ 	          exCommandDispatcher.processCommand(cm, command.exArgs.input);
+ 	        } else {
+ 	          if (vim.visualMode) {
+ 	            showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>',
+-	                onKeyDown: onPromptKeyDown});
++	                onKeyDown: onPromptKeyDown, selectValueOnOpen: false});
+ 	          } else {
+ 	            showPrompt(cm, { onClose: onPromptClose, prefix: ':',
+ 	                onKeyDown: onPromptKeyDown});
+ 	          }
+ 	        }
+ 	      },
+ 	      evalInput: function(cm, vim) {
+ 	        // If the motion command is set, execute both the operator and motion.
+@@ -19285,34 +19297,45 @@ var CodeMirror =
+ 	            onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp,
+ 	            selectValueOnOpen: false});
+ 	      }
+ 	      else {
+ 	        onClose(prompt(shortText, ''));
+ 	      }
+ 	    }
+ 	    function splitBySlash(argString) {
+-	      var slashes = findUnescapedSlashes(argString) || [];
++	      return splitBySeparator(argString, '/');
++	    }
++
++	    function findUnescapedSlashes(argString) {
++	      return findUnescapedSeparators(argString, '/');
++	    }
++
++	    function splitBySeparator(argString, separator) {
++	      var slashes = findUnescapedSeparators(argString, separator) || [];
+ 	      if (!slashes.length) return [];
+ 	      var tokens = [];
+ 	      // in case of strings like foo/bar
+ 	      if (slashes[0] !== 0) return;
+ 	      for (var i = 0; i < slashes.length; i++) {
+ 	        if (typeof slashes[i] == 'number')
+ 	          tokens.push(argString.substring(slashes[i] + 1, slashes[i+1]));
+ 	      }
+ 	      return tokens;
+ 	    }
+ 
+-	    function findUnescapedSlashes(str) {
++	    function findUnescapedSeparators(str, separator) {
++	      if (!separator)
++	        separator = '/';
++
+ 	      var escapeNextChar = false;
+ 	      var slashes = [];
+ 	      for (var i = 0; i < str.length; i++) {
+ 	        var c = str.charAt(i);
+-	        if (!escapeNextChar && c == '/') {
++	        if (!escapeNextChar && c == separator) {
+ 	          slashes.push(i);
+ 	        }
+ 	        escapeNextChar = !escapeNextChar && (c == '\\');
+ 	      }
+ 	      return slashes;
+ 	    }
+ 
+ 	    // Translates a search string from ex (vim) syntax into javascript form.
+@@ -20168,17 +20191,17 @@ var CodeMirror =
+ 	        nextCommand();
+ 	      },
+ 	      substitute: function(cm, params) {
+ 	        if (!cm.getSearchCursor) {
+ 	          throw new Error('Search feature not available. Requires searchcursor.js or ' +
+ 	              'any other getSearchCursor implementation.');
+ 	        }
+ 	        var argString = params.argString;
+-	        var tokens = argString ? splitBySlash(argString) : [];
++	        var tokens = argString ? splitBySeparator(argString, argString[0]) : [];
+ 	        var regexPart, replacePart = '', trailing, flagsPart, count;
+ 	        var confirm = false; // Whether to confirm each replace.
+ 	        var global = false; // True to replace all instances on a line, false to replace only 1.
+ 	        if (tokens.length) {
+ 	          regexPart = tokens[0];
+ 	          replacePart = tokens[1];
+ 	          if (regexPart && regexPart[regexPart.length - 1] === '$') {
+ 	            regexPart = regexPart.slice(0, regexPart.length - 1) + '\\n';
+@@ -20212,17 +20235,17 @@ var CodeMirror =
+ 	            if (flagsPart.indexOf('c') != -1) {
+ 	              confirm = true;
+ 	              flagsPart.replace('c', '');
+ 	            }
+ 	            if (flagsPart.indexOf('g') != -1) {
+ 	              global = true;
+ 	              flagsPart.replace('g', '');
+ 	            }
+-	            regexPart = regexPart + '/' + flagsPart;
++	            regexPart = regexPart.replace(/\//g, "\\/") + '/' + flagsPart;
+ 	          }
+ 	        }
+ 	        if (regexPart) {
+ 	          // If regex part is empty, then use the previous query. Otherwise use
+ 	          // the regex part as the new query.
+ 	          try {
+ 	            updateSearchQuery(cm, regexPart, true /** ignoreCase */,
+ 	              true /** smartCase */);
+@@ -20428,17 +20451,17 @@ var CodeMirror =
+ 	      // Actually do replace.
+ 	      next();
+ 	      if (done) {
+ 	        showConfirm(cm, 'No matches for ' + query.source);
+ 	        return;
+ 	      }
+ 	      if (!confirm) {
+ 	        replaceAll();
+-	        if (callback) { callback(); };
++	        if (callback) { callback(); }
+ 	        return;
+ 	      }
+ 	      showPrompt(cm, {
+ 	        prefix: 'replace with <strong>' + replaceWith + '</strong> (y/n/a/q/l)',
+ 	        onKeyDown: onPromptKeyDown
+ 	      });
+ 	    }
+ 
+@@ -20564,17 +20587,17 @@ var CodeMirror =
+ 	          if (vim.insertMode) {
+ 	            var changes = register.insertModeChanges[imc++].changes;
+ 	            vimGlobalState.macroModeState.lastInsertModeChanges.changes =
+ 	                changes;
+ 	            repeatInsertModeChanges(cm, changes, 1);
+ 	            exitInsertMode(cm);
+ 	          }
+ 	        }
+-	      };
++	      }
+ 	      macroModeState.isPlaying = false;
+ 	    }
+ 
+ 	    function logKey(macroModeState, key) {
+ 	      if (macroModeState.isPlaying) { return; }
+ 	      var registerName = macroModeState.latestRegister;
+ 	      var register = vimGlobalState.registerController.getRegister(registerName);
+ 	      if (register) {
+@@ -20768,17 +20791,17 @@ var CodeMirror =
+ 	      }
+ 	      vim.inputState = cachedInputState;
+ 	      if (vim.insertMode && !repeatForInsert) {
+ 	        // Don't exit insert mode twice. If repeatForInsert is set, then we
+ 	        // were called by an exitInsertMode call lower on the stack.
+ 	        exitInsertMode(cm);
+ 	      }
+ 	      macroModeState.isPlaying = false;
+-	    };
++	    }
+ 
+ 	    function repeatInsertModeChanges(cm, changes, repeat) {
+ 	      function keyHandler(binding) {
+ 	        if (typeof binding == 'string') {
+ 	          CodeMirror.commands[binding](cm);
+ 	        } else {
+ 	          binding(cm);
+ 	        }
+@@ -20983,18 +21006,24 @@ var CodeMirror =
+ 	    if (fullWord)
+ 	      cm.state.sublimeFindFullWord = cm.doc.sel;
+ 	  };
+ 
+ 	  function addCursorToSelection(cm, dir) {
+ 	    var ranges = cm.listSelections(), newRanges = [];
+ 	    for (var i = 0; i < ranges.length; i++) {
+ 	      var range = ranges[i];
+-	      var newAnchor = cm.findPosV(range.anchor, dir, "line");
+-	      var newHead = cm.findPosV(range.head, dir, "line");
++	      var newAnchor = cm.findPosV(
++	          range.anchor, dir, "line", range.anchor.goalColumn);
++	      var newHead = cm.findPosV(
++	          range.head, dir, "line", range.head.goalColumn);
++	      newAnchor.goalColumn = range.anchor.goalColumn != null ?
++	          range.anchor.goalColumn : cm.cursorCoords(range.anchor, "div").left;
++	      newHead.goalColumn = range.head.goalColumn != null ?
++	          range.head.goalColumn : cm.cursorCoords(range.head, "div").left;
+ 	      var newRange = {anchor: newAnchor, head: newHead};
+ 	      newRanges.push(range);
+ 	      newRanges.push(newRange);
+ 	    }
+ 	    cm.setSelections(newRanges);
+ 	  }
+ 	  cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
+ 	  cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };
+diff --git a/devtools/client/sourceeditor/codemirror/keymap/sublime.js b/devtools/client/sourceeditor/codemirror/keymap/sublime.js
+--- a/devtools/client/sourceeditor/codemirror/keymap/sublime.js
++++ b/devtools/client/sourceeditor/codemirror/keymap/sublime.js
+@@ -151,18 +151,24 @@
+     if (fullWord)
+       cm.state.sublimeFindFullWord = cm.doc.sel;
+   };
+ 
+   function addCursorToSelection(cm, dir) {
+     var ranges = cm.listSelections(), newRanges = [];
+     for (var i = 0; i < ranges.length; i++) {
+       var range = ranges[i];
+-      var newAnchor = cm.findPosV(range.anchor, dir, "line");
+-      var newHead = cm.findPosV(range.head, dir, "line");
++      var newAnchor = cm.findPosV(
++          range.anchor, dir, "line", range.anchor.goalColumn);
++      var newHead = cm.findPosV(
++          range.head, dir, "line", range.head.goalColumn);
++      newAnchor.goalColumn = range.anchor.goalColumn != null ?
++          range.anchor.goalColumn : cm.cursorCoords(range.anchor, "div").left;
++      newHead.goalColumn = range.head.goalColumn != null ?
++          range.head.goalColumn : cm.cursorCoords(range.head, "div").left;
+       var newRange = {anchor: newAnchor, head: newHead};
+       newRanges.push(range);
+       newRanges.push(newRange);
+     }
+     cm.setSelections(newRanges);
+   }
+   cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
+   cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };
+diff --git a/devtools/client/sourceeditor/codemirror/keymap/vim.js b/devtools/client/sourceeditor/codemirror/keymap/vim.js
+--- a/devtools/client/sourceeditor/codemirror/keymap/vim.js
++++ b/devtools/client/sourceeditor/codemirror/keymap/vim.js
+@@ -836,17 +836,17 @@
+             }
+             vimGlobalState.macroModeState.lastInsertModeChanges.changes.pop();
+           }
+           clearInputState(cm);
+           return match.command;
+         }
+ 
+         function handleKeyNonInsertMode() {
+-          if (handleMacroRecording() || handleEsc()) { return true; };
++          if (handleMacroRecording() || handleEsc()) { return true; }
+ 
+           var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
+           if (/^[1-9]\d*$/.test(keys)) { return true; }
+ 
+           var keysMatcher = /^(\d*)(.*)$/.exec(keys);
+           if (!keysMatcher) { clearInputState(cm); return false; }
+           var context = vim.visualMode ? 'visual' :
+                                          'normal';
+@@ -1421,17 +1421,17 @@
+           }
+         }
+         if (command.type == 'keyToEx') {
+           // Handle user defined Ex to Ex mappings
+           exCommandDispatcher.processCommand(cm, command.exArgs.input);
+         } else {
+           if (vim.visualMode) {
+             showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>',
+-                onKeyDown: onPromptKeyDown});
++                onKeyDown: onPromptKeyDown, selectValueOnOpen: false});
+           } else {
+             showPrompt(cm, { onClose: onPromptClose, prefix: ':',
+                 onKeyDown: onPromptKeyDown});
+           }
+         }
+       },
+       evalInput: function(cm, vim) {
+         // If the motion command is set, execute both the operator and motion.
+@@ -3678,34 +3678,45 @@
+             onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp,
+             selectValueOnOpen: false});
+       }
+       else {
+         onClose(prompt(shortText, ''));
+       }
+     }
+     function splitBySlash(argString) {
+-      var slashes = findUnescapedSlashes(argString) || [];
++      return splitBySeparator(argString, '/');
++    }
++
++    function findUnescapedSlashes(argString) {
++      return findUnescapedSeparators(argString, '/');
++    }
++
++    function splitBySeparator(argString, separator) {
++      var slashes = findUnescapedSeparators(argString, separator) || [];
+       if (!slashes.length) return [];
+       var tokens = [];
+       // in case of strings like foo/bar
+       if (slashes[0] !== 0) return;
+       for (var i = 0; i < slashes.length; i++) {
+         if (typeof slashes[i] == 'number')
+           tokens.push(argString.substring(slashes[i] + 1, slashes[i+1]));
+       }
+       return tokens;
+     }
+ 
+-    function findUnescapedSlashes(str) {
++    function findUnescapedSeparators(str, separator) {
++      if (!separator)
++        separator = '/';
++
+       var escapeNextChar = false;
+       var slashes = [];
+       for (var i = 0; i < str.length; i++) {
+         var c = str.charAt(i);
+-        if (!escapeNextChar && c == '/') {
++        if (!escapeNextChar && c == separator) {
+           slashes.push(i);
+         }
+         escapeNextChar = !escapeNextChar && (c == '\\');
+       }
+       return slashes;
+     }
+ 
+     // Translates a search string from ex (vim) syntax into javascript form.
+@@ -4561,17 +4572,17 @@
+         nextCommand();
+       },
+       substitute: function(cm, params) {
+         if (!cm.getSearchCursor) {
+           throw new Error('Search feature not available. Requires searchcursor.js or ' +
+               'any other getSearchCursor implementation.');
+         }
+         var argString = params.argString;
+-        var tokens = argString ? splitBySlash(argString) : [];
++        var tokens = argString ? splitBySeparator(argString, argString[0]) : [];
+         var regexPart, replacePart = '', trailing, flagsPart, count;
+         var confirm = false; // Whether to confirm each replace.
+         var global = false; // True to replace all instances on a line, false to replace only 1.
+         if (tokens.length) {
+           regexPart = tokens[0];
+           replacePart = tokens[1];
+           if (regexPart && regexPart[regexPart.length - 1] === '$') {
+             regexPart = regexPart.slice(0, regexPart.length - 1) + '\\n';
+@@ -4605,17 +4616,17 @@
+             if (flagsPart.indexOf('c') != -1) {
+               confirm = true;
+               flagsPart.replace('c', '');
+             }
+             if (flagsPart.indexOf('g') != -1) {
+               global = true;
+               flagsPart.replace('g', '');
+             }
+-            regexPart = regexPart + '/' + flagsPart;
++            regexPart = regexPart.replace(/\//g, "\\/") + '/' + flagsPart;
+           }
+         }
+         if (regexPart) {
+           // If regex part is empty, then use the previous query. Otherwise use
+           // the regex part as the new query.
+           try {
+             updateSearchQuery(cm, regexPart, true /** ignoreCase */,
+               true /** smartCase */);
+@@ -4821,17 +4832,17 @@
+       // Actually do replace.
+       next();
+       if (done) {
+         showConfirm(cm, 'No matches for ' + query.source);
+         return;
+       }
+       if (!confirm) {
+         replaceAll();
+-        if (callback) { callback(); };
++        if (callback) { callback(); }
+         return;
+       }
+       showPrompt(cm, {
+         prefix: 'replace with <strong>' + replaceWith + '</strong> (y/n/a/q/l)',
+         onKeyDown: onPromptKeyDown
+       });
+     }
+ 
+@@ -4957,17 +4968,17 @@
+           if (vim.insertMode) {
+             var changes = register.insertModeChanges[imc++].changes;
+             vimGlobalState.macroModeState.lastInsertModeChanges.changes =
+                 changes;
+             repeatInsertModeChanges(cm, changes, 1);
+             exitInsertMode(cm);
+           }
+         }
+-      };
++      }
+       macroModeState.isPlaying = false;
+     }
+ 
+     function logKey(macroModeState, key) {
+       if (macroModeState.isPlaying) { return; }
+       var registerName = macroModeState.latestRegister;
+       var register = vimGlobalState.registerController.getRegister(registerName);
+       if (register) {
+@@ -5161,17 +5172,17 @@
+       }
+       vim.inputState = cachedInputState;
+       if (vim.insertMode && !repeatForInsert) {
+         // Don't exit insert mode twice. If repeatForInsert is set, then we
+         // were called by an exitInsertMode call lower on the stack.
+         exitInsertMode(cm);
+       }
+       macroModeState.isPlaying = false;
+-    };
++    }
+ 
+     function repeatInsertModeChanges(cm, changes, repeat) {
+       function keyHandler(binding) {
+         if (typeof binding == 'string') {
+           CodeMirror.commands[binding](cm);
+         } else {
+           binding(cm);
+         }
+diff --git a/devtools/client/sourceeditor/codemirror/lib/codemirror.js b/devtools/client/sourceeditor/codemirror/lib/codemirror.js
+--- a/devtools/client/sourceeditor/codemirror/lib/codemirror.js
++++ b/devtools/client/sourceeditor/codemirror/lib/codemirror.js
+@@ -5207,17 +5207,18 @@ function makeChangeInner(doc, change) {
+       rebased.push(doc.history)
+     }
+     makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
+   })
+ }
+ 
+ // Revert a change stored in a document's history.
+ function makeChangeFromHistory(doc, type, allowSelectionOnly) {
+-  if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
++  var suppress = doc.cm && doc.cm.state.suppressEdits
++  if (suppress && !allowSelectionOnly) { return }
+ 
+   var hist = doc.history, event, selAfter = doc.sel
+   var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done
+ 
+   // Verify that there is a useable event (so that ctrl-z won't
+   // needlessly clear selection events)
+   var i = 0
+   for (; i < source.length; i++) {
+@@ -5232,18 +5233,20 @@ function makeChangeFromHistory(doc, type
+     event = source.pop()
+     if (event.ranges) {
+       pushSelectionToHistory(event, dest)
+       if (allowSelectionOnly && !event.equals(doc.sel)) {
+         setSelection(doc, event, {clearRedo: false})
+         return
+       }
+       selAfter = event
+-    }
+-    else { break }
++    } else if (suppress) {
++      source.push(event)
++      return
++    } else { break }
+   }
+ 
+   // Build up a reverse change object to add to the opposite history
+   // stack (redo when undoing, and vice versa).
+   var antiChanges = []
+   pushSelectionToHistory(selAfter, dest)
+   dest.push({changes: antiChanges, generation: hist.generation})
+   hist.generation = event.generation || ++hist.maxGeneration
+@@ -5709,17 +5712,17 @@ function addLineWidget(doc, handle, node
+     if (cm && !lineIsHidden(doc, line)) {
+       var aboveVisible = heightAtLine(line) < doc.scrollTop
+       updateLineHeight(line, line.height + widgetHeight(widget))
+       if (aboveVisible) { addToScrollTop(cm, widget.height) }
+       cm.curOp.forceUpdate = true
+     }
+     return true
+   })
+-  signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle))
++  if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)) }
+   return widget
+ }
+ 
+ // TEXTMARKERS
+ 
+ // Created with markText and setBookmark methods. A TextMarker is a
+ // handle that can be used to clear or find a marked position in the
+ // document. Line objects hold arrays (markedSpans) containing
+@@ -9645,13 +9648,13 @@ CodeMirror.defineExtension = function (n
+ CodeMirror.defineDocExtension = function (name, func) {
+   Doc.prototype[name] = func
+ }
+ 
+ CodeMirror.fromTextArea = fromTextArea
+ 
+ addLegacyProps(CodeMirror)
+ 
+-CodeMirror.version = "5.34.0"
++CodeMirror.version = "5.35.0"
+ 
+ return CodeMirror;
+ 
+ })));
+\ No newline at end of file
+diff --git a/devtools/client/sourceeditor/codemirror/mode/clike/clike.js b/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
+--- a/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
++++ b/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
+@@ -369,33 +369,33 @@ CodeMirror.defineMode("clike", function(
+     name: "clike",
+     keywords: words(cKeywords),
+     types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " +
+                  "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " +
+                  "uint32_t uint64_t"),
+     blockKeywords: words("case do else for if switch while struct"),
+     defKeywords: words("struct"),
+     typeFirstDefinitions: true,
+-    atoms: words("null true false"),
++    atoms: words("NULL true false"),
+     hooks: {"#": cppHook, "*": pointerHook},
+     modeProps: {fold: ["brace", "include"]}
+   });
+ 
+   def(["text/x-c++src", "text/x-c++hdr"], {
+     name: "clike",
+     keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " +
+                     "static_cast typeid catch operator template typename class friend private " +
+                     "this using const_cast inline public throw virtual delete mutable protected " +
+                     "alignas alignof constexpr decltype nullptr noexcept thread_local final " +
+                     "static_assert override"),
+     types: words(cTypes + " bool wchar_t"),
+     blockKeywords: words("catch class do else finally for if struct switch try while"),
+     defKeywords: words("class namespace struct enum union"),
+     typeFirstDefinitions: true,
+-    atoms: words("true false null"),
++    atoms: words("true false NULL"),
+     dontIndentStatements: /^template$/,
+     isIdentifierChar: /[\w\$_~\xa1-\uffff]/,
+     hooks: {
+       "#": cppHook,
+       "*": pointerHook,
+       "u": cpp11StringHook,
+       "U": cpp11StringHook,
+       "L": cpp11StringHook,
+@@ -592,32 +592,34 @@ CodeMirror.defineMode("clike", function(
+       return "string";
+     }
+   }
+ 
+   def("text/x-kotlin", {
+     name: "clike",
+     keywords: words(
+       /*keywords*/
+-      "package as typealias class interface this super val " +
+-      "var fun for is in This throw return " +
++      "package as typealias class interface this super val operator " +
++      "var fun for is in This throw return annotation " +
+       "break continue object if else while do try when !in !is as? " +
+ 
+       /*soft keywords*/
+       "file import where by get set abstract enum open inner override private public internal " +
+       "protected catch finally out final vararg reified dynamic companion constructor init " +
+       "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
+-      "external annotation crossinline const operator infix suspend actual expect"
++      "external annotation crossinline const operator infix suspend actual expect setparam"
+     ),
+     types: words(
+       /* package java.lang */
+       "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+       "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+       "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+-      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
++      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray " +
++      "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " +
++      "LazyThreadSafetyMode LongArray Nothing ShortArray Unit"
+     ),
+     intendSwitch: false,
+     indentStatements: false,
+     multiLineStrings: true,
+     number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+     blockKeywords: words("catch class do else finally for if where try while enum"),
+     defKeywords: words("class val var object interface fun"),
+     atoms: words("true false null this"),
+diff --git a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
+--- a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
++++ b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
+@@ -395,16 +395,17 @@ CodeMirror.defineMode("javascript", func
+     if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
+     if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
+     if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
+     if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
+     if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
+     if (type == "{") return contCommasep(objprop, "}", null, maybeop);
+     if (type == "quasi") return pass(quasi, maybeop);
+     if (type == "new") return cont(maybeTarget(noComma));
++    if (type == "import") return cont(expression);
+     return cont();
+   }
+   function maybeexpression(type) {
+     if (type.match(/[;\}\)\],]/)) return pass();
+     return pass(expression);
+   }
+ 
+   function maybeoperatorComma(type, value) {
+@@ -589,17 +590,17 @@ CodeMirror.defineMode("javascript", func
+     }
+   }
+   function typearg(type) {
+     if (type == "variable") return cont(typearg)
+     else if (type == ":") return cont(typeexpr)
+   }
+   function afterType(type, value) {
+     if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+-    if (value == "|" || type == ".") return cont(typeexpr)
++    if (value == "|" || type == "." || value == "&") return cont(typeexpr)
+     if (type == "[") return cont(expect("]"), afterType)
+     if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
+   }
+   function maybeTypeArgs(_, value) {
+     if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+   }
+   function typeparam() {
+     return pass(typeexpr, maybeTypeDefault)
+@@ -632,17 +633,18 @@ CodeMirror.defineMode("javascript", func
+     if (value == "=") return cont(expressionNoComma);
+   }
+   function vardefCont(type) {
+     if (type == ",") return cont(vardef);
+   }
+   function maybeelse(type, value) {
+     if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
+   }
+-  function forspec(type) {
++  function forspec(type, value) {
++    if (value == "await") return cont(forspec);
+     if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
+   }
+   function forspec1(type) {
+     if (type == "var") return cont(vardef, expect(";"), forspec2);
+     if (type == ";") return cont(forspec2);
+     if (type == "variable") return cont(formaybeinof);
+     return pass(expression, expect(";"), forspec2);
+   }
+@@ -721,16 +723,17 @@ CodeMirror.defineMode("javascript", func
+     return pass(statement);
+   }
+   function exportField(type, value) {
+     if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
+     if (type == "variable") return pass(expressionNoComma, exportField);
+   }
+   function afterImport(type) {
+     if (type == "string") return cont();
++    if (type == "(") return pass(expression);
+     return pass(importSpec, maybeMoreImports, maybeFrom);
+   }
+   function importSpec(type, value) {
+     if (type == "{") return contCommasep(importSpec, "}");
+     if (type == "variable") register(value);
+     if (value == "*") cx.marked = "keyword";
+     return cont(maybeAs);
+   }
+diff --git a/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js b/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
+--- a/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
++++ b/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
+@@ -58,24 +58,33 @@
+      "[keyword function] [def foo]() {",
+      "  [keyword import] [def $] [keyword from] [string 'jquery'];",
+      "  [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];",
+      "}");
+ 
+   MT("import_trailing_comma",
+      "[keyword import] {[def foo], [def bar],} [keyword from] [string 'baz']")
+ 
++  MT("import_dynamic",
++     "[keyword import]([string 'baz']).[property then]")
++
++  MT("import_dynamic",
++     "[keyword const] [def t] [operator =] [keyword import]([string 'baz']).[property then]")
++
+   MT("const",
+      "[keyword function] [def f]() {",
+      "  [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];",
+      "}");
+ 
+   MT("for/of",
+      "[keyword for]([keyword let] [def of] [keyword of] [variable something]) {}");
+ 
++  MT("for await",
++     "[keyword for] [keyword await]([keyword let] [def of] [keyword of] [variable something]) {}");
++
+   MT("generator",
+      "[keyword function*] [def repeat]([def n]) {",
+      "  [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])",
+      "    [keyword yield] [variable-2 i];",
+      "}");
+ 
+   MT("quotedStringAddition",
+      "[keyword let] [def f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];");
+diff --git a/devtools/client/sourceeditor/test/codemirror/test.js b/devtools/client/sourceeditor/test/codemirror/test.js
+--- a/devtools/client/sourceeditor/test/codemirror/test.js
++++ b/devtools/client/sourceeditor/test/codemirror/test.js
+@@ -2325,17 +2325,17 @@ testCM("lineSeparator", function(cm) {
+   eq(cm.getValue("\n"), "foo\nbar\n\nbaz\nquux");
+   cm.setOption("lineSeparator", null);
+   cm.setValue("foo\nbar\r\nbaz\rquux");
+   eq(cm.lineCount(), 4);
+ }, {value: "foo\nbar\r\nbaz\rquux",
+     lineSeparator: "\n"});
+ 
+ var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/
+-var getChar = function (noExtending) { var res; do {res = String.fromCharCode(Math.floor(Math.random()*0x8ac)); } while ([0x90].includes(res.charCodeAt(0)) || (noExtending && extendingChars.test(res))); return res }
++var getChar = function (noExtending) { var res; do {res = String.fromCharCode(Math.floor(Math.random()*0x8ac)); } while ([0x90].indexOf(res.charCodeAt(0)) != -1 || (noExtending && extendingChars.test(res))); return res }
+ var getString = function (n) { var res = getChar(true); while (--n > 0) res += getChar(); return res }
+ 
+ function makeItWrapAfter(cm, pos) {
+   var firstLineTop = cm.cursorCoords(Pos(0, 0)).top;
+   for(var w = 0, posTop; posTop != firstLineTop; ++w) {
+     cm.setSize(w);
+     posTop = cm.charCoords(pos).top;
+   }
+@@ -2351,17 +2351,17 @@ function testMoveBidi(str) {
+   testCM("move_bidi_" + str, function(cm) {
+     if (cm.getOption("inputStyle") != "textarea" || !cm.getOption("rtlMoveVisually")) return;
+     cm.getScrollerElement().style.fontFamily = "monospace";
+     makeItWrapAfter(cm, Pos(0, 5));
+ 
+     var steps = str.length - countIf(str.split(""), function(ch) { return extendingChars.test(ch) });
+     var lineBreaks = {}
+     lineBreaks[6 - countIf(str.substr(0, 5).split(""), function(ch) { return extendingChars.test(ch) })] = 'w';
+-    if (str.includes("\n")) {
++    if (str.indexOf("\n") != -1) {
+       lineBreaks[steps - 2] = 'n';
+     }
+ 
+     // Make sure we are at the visual beginning of the first line
+     cm.execCommand("goLineStart");
+ 
+     var prevCoords = cm.cursorCoords(), coords;
+     for(var i = 0; i < steps; ++i) {
+diff --git a/devtools/client/sourceeditor/test/codemirror/vim_test.js b/devtools/client/sourceeditor/test/codemirror/vim_test.js
+--- a/devtools/client/sourceeditor/test/codemirror/vim_test.js
++++ b/devtools/client/sourceeditor/test/codemirror/vim_test.js
+@@ -148,17 +148,17 @@ function testVim(name, run, opts, expect
+         }
+         for (var i = 0; i < arguments.length; i++) {
+           var key = arguments[i];
+           // Find key in keymap and handle.
+           var handled = CodeMirror.lookupKey(key, cm.getOption('keyMap'), executeHandler, cm);
+           // Record for insert mode.
+           if (handled == "handled" && cm.state.vim.insertMode && arguments[i] != 'Esc') {
+             var lastChange = CodeMirror.Vim.getVimGlobalState_().macroModeState.lastInsertModeChanges;
+-            if (lastChange && (key.includes('Delete') || key.includes('Backspace'))) {
++            if (lastChange && (key.indexOf('Delete') != -1 || key.indexOf('Backspace') != -1)) {
+               lastChange.changes.push(new CodeMirror.Vim.InsertModeKey(key));
+             }
+           }
+         }
+       }
+     }
+     function doExFn(cm) {
+       return function(command) {
+@@ -250,17 +250,17 @@ function testJumplist(name, keys, endPos
+   startPos = makeCursor(startPos[0], startPos[1]);
+   testVim(name, function(cm, vim, helpers) {
+     CodeMirror.Vim.resetVimGlobalState_();
+     if(dialog)cm.openDialog = helpers.fakeOpenDialog('word');
+     cm.setCursor(startPos);
+     helpers.doKeys.apply(null, keys);
+     helpers.assertCursorAt(endPos);
+   }, {value: jumplistScene});
+-};
++}
+ testJumplist('jumplist_H', ['H', '<C-o>'], [5,2], [5,2]);
+ testJumplist('jumplist_M', ['M', '<C-o>'], [2,2], [2,2]);
+ testJumplist('jumplist_L', ['L', '<C-o>'], [2,2], [2,2]);
+ testJumplist('jumplist_[[', ['[', '[', '<C-o>'], [5,2], [5,2]);
+ testJumplist('jumplist_]]', [']', ']', '<C-o>'], [2,2], [2,2]);
+ testJumplist('jumplist_G', ['G', '<C-o>'], [5,2], [5,2]);
+ testJumplist('jumplist_gg', ['g', 'g', '<C-o>'], [5,2], [5,2]);
+ testJumplist('jumplist_%', ['%', '<C-o>'], [1,5], [1,5]);
+@@ -294,25 +294,25 @@ function testMotion(name, keys, endPos, 
+   testVim(name, function(cm, vim, helpers) {
+     if (!startPos) {
+       startPos = new Pos(0, 0);
+     }
+     cm.setCursor(startPos);
+     helpers.doKeys(keys);
+     helpers.assertCursorAt(endPos);
+   });
+-};
++}
+ 
+ function makeCursor(line, ch) {
+   return new Pos(line, ch);
+-};
++}
+ 
+ function offsetCursor(cur, offsetLine, offsetCh) {
+   return new Pos(cur.line + offsetLine, cur.ch + offsetCh);
+-};
++}
+ 
+ // Motion tests
+ testMotion('|', '|', makeCursor(0, 0), makeCursor(0,4));
+ testMotion('|_repeat', ['3', '|'], makeCursor(0, 2), makeCursor(0,4));
+ testMotion('h', 'h', makeCursor(0, 0), word1.start);
+ testMotion('h_repeat', ['3', 'h'], offsetCursor(word1.end, 0, -3), word1.end);
+ testMotion('l', 'l', makeCursor(0, 1));
+ testMotion('l_repeat', ['2', 'l'], makeCursor(0, 2));
+@@ -3643,16 +3643,21 @@ testVim('ex_global_confirm', function(cm
+   eq('one two\n two two\n one one\n two one\n one one', cm.getValue());
+ }, {value: 'one one\n one one\n one one\n one one\n one one'});
+ // Basic substitute tests.
+ testVim('ex_substitute_same_line', function(cm, vim, helpers) {
+   cm.setCursor(1, 0);
+   helpers.doEx('s/one/two/g');
+   eq('one one\n two two', cm.getValue());
+ }, { value: 'one one\n one one'});
++testVim('ex_substitute_alternate_separator', function(cm, vim, helpers) {
++  cm.setCursor(1, 0);
++  helpers.doEx('s#o/e#two#g');
++  eq('o/e o/e\n two two', cm.getValue());
++}, { value: 'o/e o/e\n o/e o/e'});
+ testVim('ex_substitute_full_file', function(cm, vim, helpers) {
+   cm.setCursor(1, 0);
+   helpers.doEx('%s/one/two/g');
+   eq('two two\n two two', cm.getValue());
+ }, { value: 'one one\n one one'});
+ testVim('ex_substitute_input_range', function(cm, vim, helpers) {
+   cm.setCursor(1, 0);
+   helpers.doEx('1,3s/\\d/0/g');
+@@ -3899,17 +3904,17 @@ function testSubstituteConfirm(name, com
+     } catch(e) {
+       throw e
+     } finally {
+       // Restore overridden functions.
+       CodeMirror.keyName = savedKeyName;
+       cm.openDialog = savedOpenDialog;
+     }
+   }, { value: initialValue });
+-};
++}
+ testSubstituteConfirm('ex_substitute_confirm_emptydoc',
+     '%s/x/b/c', '', '', '', makeCursor(0, 0));
+ testSubstituteConfirm('ex_substitute_confirm_nomatch',
+     '%s/x/b/c', 'ba a\nbab', 'ba a\nbab', '', makeCursor(0, 0));
+ testSubstituteConfirm('ex_substitute_confirm_accept',
+     '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'yyy', makeCursor(1, 1));
+ testSubstituteConfirm('ex_substitute_confirm_random_keys',
+     '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'ysdkywerty', makeCursor(1, 1));

+ 43 - 0
frg/work-js/mozilla-release/patches/1441528-60a1.patch

@@ -0,0 +1,43 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1519737728 28800
+# Node ID ea212cb424e30cfbcdba8caba51f8270a2c8337d
+# Parent  37b4bfb6040c0c717f8282bf3f053d35f2c2b489
+Bug 1441528 - Remove duplicated waitForMultipleChildrenUpdates from inspector head files. r=jryans
+
+MozReview-Commit-ID: KwUUsWy3IZm
+
+diff --git a/devtools/client/inspector/markup/test/head.js b/devtools/client/inspector/markup/test/head.js
+--- a/devtools/client/inspector/markup/test/head.js
++++ b/devtools/client/inspector/markup/test/head.js
+@@ -414,30 +414,16 @@ function checkFocusedAttribute(attrName,
+  */
+ var getAttributesFromEditor = Task.async(function* (selector, inspector) {
+   let nodeList = (yield getContainerForSelector(selector, inspector))
+     .tagLine.querySelectorAll("[data-attr]");
+ 
+   return [...nodeList].map(node => node.getAttribute("data-attr"));
+ });
+ 
+-// The expand all operation of the markup-view calls itself recursively and
+-// there's not one event we can wait for to know when it's done so use this
+-// helper function to wait until all recursive children updates are done.
+-function* waitForMultipleChildrenUpdates(inspector) {
+-  // As long as child updates are queued up while we wait for an update already
+-  // wait again
+-  if (inspector.markup._queuedChildUpdates &&
+-      inspector.markup._queuedChildUpdates.size) {
+-    yield waitForChildrenUpdated(inspector);
+-    return yield waitForMultipleChildrenUpdates(inspector);
+-  }
+-  return undefined;
+-}
+-
+ /**
+  * Registers new backend tab actor.
+  *
+  * @param {DebuggerClient} client RDP client object (toolbox.target.client)
+  * @param {Object} options Configuration object with the following options:
+  *
+  * - moduleUrl {String}: URL of the module that contains actor implementation.
+  * - prefix {String}: prefix of the actor.

+ 705 - 0
frg/work-js/mozilla-release/patches/1441896-60a1.patch

@@ -0,0 +1,705 @@
+# HG changeset patch
+# User Bobby Holley <bobbyholley@gmail.com>
+# Date 1519805340 -7200
+# Node ID 8d4be00621c3b155b1d4978ef7cbf631f957468c
+# Parent  1a7366d47d9ce0bc28df3a3d1be89f42e3ceeabf
+Bug 1441896 - Pass an explicit parent SheetLoadData for child stylesheet loads. r=bz CLOSED TREE
+
+MozReview-Commit-ID: 7XNu42NtITm
+
+diff --git a/dom/base/nsTreeSanitizer.cpp b/dom/base/nsTreeSanitizer.cpp
+--- a/dom/base/nsTreeSanitizer.cpp
++++ b/dom/base/nsTreeSanitizer.cpp
+@@ -1112,23 +1112,24 @@ nsTreeSanitizer::SanitizeStyleSheet(cons
+ #endif
+   }
+   sheet->SetURIs(aDocument->GetDocumentURI(), nullptr, aBaseURI);
+   sheet->SetPrincipal(aDocument->NodePrincipal());
+   if (aDocument->IsStyledByServo()) {
+     rv = sheet->AsServo()->ParseSheet(
+         aDocument->CSSLoader(), NS_ConvertUTF16toUTF8(aOriginal),
+         aDocument->GetDocumentURI(), aBaseURI, aDocument->NodePrincipal(),
+-        0, aDocument->GetCompatibilityMode());
++        /* aLoadData = */ nullptr, 0, aDocument->GetCompatibilityMode());
+   } else {
+ #ifdef MOZ_OLD_STYLE
+     // Create the CSS parser, and parse the CSS text.
+     nsCSSParser parser(nullptr, sheet->AsGecko());
+-    rv = parser.ParseSheet(aOriginal, aDocument->GetDocumentURI(), aBaseURI,
+-                           aDocument->NodePrincipal(), 0);
++    rv = parser.ParseSheet(aOriginal, aDocument->GetDocumentURI(),
++                           aBaseURI, aDocument->NodePrincipal(),
++                           /* aLoadData = */ nullptr, 0);
+ #else
+     MOZ_CRASH("old style system disabled");
+ #endif
+   }
+   NS_ENSURE_SUCCESS(rv, true);
+   // Mark the sheet as complete.
+   MOZ_ASSERT(!sheet->IsModified(),
+              "should not get marked modified during parsing");
+diff --git a/layout/style/CSSStyleSheet.cpp b/layout/style/CSSStyleSheet.cpp
+--- a/layout/style/CSSStyleSheet.cpp
++++ b/layout/style/CSSStyleSheet.cpp
+@@ -919,17 +919,18 @@ CSSStyleSheet::ReparseSheet(const nsAStr
+     nsCOMPtr<nsIStyleSheetLinkingElement> link = do_QueryInterface(mOwningNode);
+     if (link) {
+       lineNumber = link->GetLineNumber();
+     }
+   }
+ 
+   nsCSSParser parser(loader, this);
+   nsresult rv = parser.ParseSheet(aInput, mInner->mSheetURI, mInner->mBaseURI,
+-                                  mInner->mPrincipal, lineNumber, &reusableSheets);
++                                  mInner->mPrincipal, /* aLoadData = */ nullptr,
++                                  lineNumber, &reusableSheets);
+   DidDirty(); // we are always 'dirty' here since we always remove rules first
+   NS_ENSURE_SUCCESS(rv, rv);
+ 
+   // notify document of all new rules
+   for (int32_t index = 0; index < Inner()->mOrderedRules.Count(); ++index) {
+     RefPtr<css::Rule> rule = Inner()->mOrderedRules.ObjectAt(index);
+     if (rule->GetType() == css::Rule::IMPORT_RULE &&
+         RuleHasPendingChildSheet(rule)) {
+diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp
+--- a/layout/style/Loader.cpp
++++ b/layout/style/Loader.cpp
+@@ -1675,27 +1675,29 @@ Loader::ParseSheet(const nsAString& aUTF
+ 
+   if (aLoadData->mSheet->IsGecko()) {
+ #ifdef MOZ_OLD_STYLE
+     nsCSSParser parser(this, aLoadData->mSheet->AsGecko());
+     rv = parser.ParseSheet(aUTF16,
+                            sheetURI,
+                            baseURI,
+                            aLoadData->mSheet->Principal(),
++                           aLoadData,
+                            aLoadData->mLineNumber);
+ #else
+     MOZ_CRASH("old style system disabled");
+ #endif
+   } else {
+     rv = aLoadData->mSheet->AsServo()->ParseSheet(
+       this,
+       aUTF8.IsEmpty() ? NS_ConvertUTF16toUTF8(aUTF16) : aUTF8,
+       sheetURI,
+       baseURI,
+       aLoadData->mSheet->Principal(),
++      aLoadData,
+       aLoadData->mLineNumber,
+       GetCompatibilityMode());
+   }
+ 
+   mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1);
+ 
+   if (NS_FAILED(rv)) {
+     LOG_ERROR(("  Low-level error in parser!"));
+@@ -2148,16 +2150,17 @@ HaveAncestorDataWithURI(SheetLoadData *a
+     aData = aData->mNext;
+   }
+ 
+   return false;
+ }
+ 
+ nsresult
+ Loader::LoadChildSheet(StyleSheet* aParentSheet,
++                       SheetLoadData* aParentData,
+                        nsIURI* aURL,
+                        dom::MediaList* aMedia,
+                        ImportRule* aGeckoParentRule,
+                        LoaderReusableStyleSheets* aReusableSheets)
+ {
+   LOG(("css::Loader::LoadChildSheet"));
+   NS_PRECONDITION(aURL, "Must have a URI to load");
+   NS_PRECONDITION(aParentSheet, "Must have a parent sheet");
+@@ -2191,32 +2194,29 @@ Loader::LoadChildSheet(StyleSheet* aPare
+     context = mDocument;
+     loadingPrincipal = mDocument->NodePrincipal();
+   }
+ 
+   nsIPrincipal* principal = aParentSheet->Principal();
+   nsresult rv = CheckContentPolicy(loadingPrincipal, principal, aURL, context, false);
+   NS_ENSURE_SUCCESS(rv, rv);
+ 
+-  SheetLoadData* parentData = nullptr;
+   nsCOMPtr<nsICSSLoaderObserver> observer;
+ 
+-  int32_t count = mParsingDatas.Length();
+-  if (count > 0) {
++  if (aParentData) {
+     LOG(("  Have a parent load"));
+-    parentData = mParsingDatas.ElementAt(count - 1);
+     // Check for cycles
+-    if (HaveAncestorDataWithURI(parentData, aURL)) {
++    if (HaveAncestorDataWithURI(aParentData, aURL)) {
+       // Houston, we have a loop, blow off this child and pretend this never
+       // happened
+       LOG_ERROR(("  @import cycle detected, dropping load"));
+       return NS_OK;
+     }
+ 
+-    NS_ASSERTION(parentData->mSheet == aParentSheet,
++    NS_ASSERTION(aParentData->mSheet == aParentSheet,
+                  "Unexpected call to LoadChildSheet");
+   } else {
+     LOG(("  No parent load; must be CSSOM"));
+     // No parent load data, so the sheet will need to be notified when
+     // we finish, if it can be, if we do the load asynchronously.
+     observer = aParentSheet;
+   }
+ 
+@@ -2236,17 +2236,17 @@ Loader::LoadChildSheet(StyleSheet* aPare
+   } else {
+     IsAlternate isAlternate;
+     const nsAString& empty = EmptyString();
+     // For now, use CORS_NONE for child sheets
+     rv = CreateSheet(aURL, nullptr, principal,
+                      aParentSheet->ParsingMode(),
+                      CORS_NONE, aParentSheet->GetReferrerPolicy(),
+                      EmptyString(), // integrity is only checked on main sheet
+-                     parentData ? parentData->mSyncLoad : false,
++                     aParentData ? aParentData->mSyncLoad : false,
+                      false, empty, state, &isAlternate, &sheet);
+     NS_ENSURE_SUCCESS(rv, rv);
+ 
+     PrepareSheet(sheet, empty, empty, aMedia, isAlternate);
+   }
+ 
+   rv = InsertChildSheet(sheet, aParentSheet, aGeckoParentRule);
+   NS_ENSURE_SUCCESS(rv, rv);
+@@ -2255,17 +2255,17 @@ Loader::LoadChildSheet(StyleSheet* aPare
+     LOG(("  Sheet already complete"));
+     // We're completely done.  No need to notify, even, since the
+     // @import rule addition/modification will trigger the right style
+     // changes automatically.
+     return NS_OK;
+   }
+ 
+   nsCOMPtr<nsINode> requestingNode = do_QueryInterface(context);
+-  SheetLoadData* data = new SheetLoadData(this, aURL, sheet, parentData,
++  SheetLoadData* data = new SheetLoadData(this, aURL, sheet, aParentData,
+                                           observer, principal, requestingNode);
+ 
+   NS_ADDREF(data);
+   bool syncLoad = data->mSyncLoad;
+ 
+   // Load completion will release the data
+   rv = LoadSheet(data, state, false);
+   NS_ENSURE_SUCCESS(rv, rv);
+diff --git a/layout/style/Loader.h b/layout/style/Loader.h
+--- a/layout/style/Loader.h
++++ b/layout/style/Loader.h
+@@ -291,25 +291,28 @@ public:
+    * there is no sheet currently being parsed and the child sheet is not
+    * complete when this method returns, then when the child sheet becomes
+    * complete aParentSheet will be QIed to nsICSSLoaderObserver and
+    * asynchronously notified, just like for LoadStyleLink.  Note that if the
+    * child sheet is already complete when this method returns, no
+    * nsICSSLoaderObserver notification will be sent.
+    *
+    * @param aParentSheet the parent of this child sheet
++   * @param aParentData the SheetLoadData corresponding to the load of the
++   *                    parent sheet.
+    * @param aURL the URL of the child sheet
+    * @param aMedia the already-parsed media list for the child sheet
+    * @param aGeckoParentRule the @import rule importing this child, when using
+    *                         Gecko's style system. This is used to properly
+    *                         order the child sheet list of aParentSheet.
+    * @param aSavedSheets any saved style sheets which could be reused
+    *              for this load
+    */
+   nsresult LoadChildSheet(StyleSheet* aParentSheet,
++                          SheetLoadData* aParentData,
+                           nsIURI* aURL,
+                           dom::MediaList* aMedia,
+                           ImportRule* aGeckoParentRule,
+                           LoaderReusableStyleSheets* aSavedSheets);
+ 
+   /**
+    * Synchronously load and return the stylesheet at aURL.  Any child sheets
+    * will also be loaded synchronously.  Note that synchronous loads over some
+diff --git a/layout/style/ServoBindingList.h b/layout/style/ServoBindingList.h
+--- a/layout/style/ServoBindingList.h
++++ b/layout/style/ServoBindingList.h
+@@ -46,16 +46,17 @@ SERVO_BINDING_FUNC(Servo_InvalidateStyle
+                    const nsTArray<RawServoStyleSetBorrowed>* sets,
+                    uint64_t aStatesChanged)
+ 
+ // Styleset and Stylesheet management
+ SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes,
+                    RawServoStyleSheetContentsStrong,
+                    mozilla::css::Loader* loader,
+                    mozilla::ServoStyleSheet* gecko_stylesheet,
++                   mozilla::css::SheetLoadData* load_data,
+                    const uint8_t* data,
+                    size_t data_len,
+                    mozilla::css::SheetParsingMode parsing_mode,
+                    RawGeckoURLExtraData* extra_data,
+                    uint32_t line_number_offset,
+                    nsCompatibility quirks_mode,
+                    mozilla::css::LoaderReusableStyleSheets* reusable_sheets)
+ SERVO_BINDING_FUNC(Servo_StyleSheet_Empty, RawServoStyleSheetContentsStrong,
+diff --git a/layout/style/ServoBindings.cpp b/layout/style/ServoBindings.cpp
+--- a/layout/style/ServoBindings.cpp
++++ b/layout/style/ServoBindings.cpp
+@@ -2532,16 +2532,17 @@ Gecko_GetAppUnitsPerPhysicalInch(RawGeck
+ {
+   nsPresContext* presContext = const_cast<nsPresContext*>(aPresContext);
+   return presContext->DeviceContext()->AppUnitsPerPhysicalInch();
+ }
+ 
+ ServoStyleSheet*
+ Gecko_LoadStyleSheet(css::Loader* aLoader,
+                      ServoStyleSheet* aParent,
++                     SheetLoadData* aParentLoadData,
+                      css::LoaderReusableStyleSheets* aReusableSheets,
+                      RawGeckoURLExtraData* aURLExtraData,
+                      const uint8_t* aURLString,
+                      uint32_t aURLStringLength,
+                      RawServoMediaListStrong aMediaList)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   MOZ_ASSERT(aLoader, "Should've catched this before");
+@@ -2553,17 +2554,17 @@ Gecko_LoadStyleSheet(css::Loader* aLoade
+   nsDependentCSubstring urlSpec(reinterpret_cast<const char*>(aURLString),
+                                 aURLStringLength);
+   nsCOMPtr<nsIURI> uri;
+   nsresult rv = NS_NewURI(getter_AddRefs(uri), urlSpec, nullptr,
+                           aURLExtraData->BaseURI());
+ 
+   StyleSheet* previousFirstChild = aParent->GetFirstChild();
+   if (NS_SUCCEEDED(rv)) {
+-    rv = aLoader->LoadChildSheet(aParent, uri, media, nullptr, aReusableSheets);
++    rv = aLoader->LoadChildSheet(aParent, aParentLoadData, uri, media, nullptr, aReusableSheets);
+   }
+ 
+   if (NS_FAILED(rv) ||
+       !aParent->GetFirstChild() ||
+       aParent->GetFirstChild() == previousFirstChild) {
+     // Servo and Gecko have different ideas of what a valid URL is, so we might
+     // get in here with a URL string that NS_NewURI can't handle.  We may also
+     // reach here via an import cycle.  For the import cycle case, we need some
+diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h
+--- a/layout/style/ServoBindings.h
++++ b/layout/style/ServoBindings.h
+@@ -8,16 +8,17 @@
+ #define mozilla_ServoBindings_h
+ 
+ #include <stdint.h>
+ 
+ #include "mozilla/AtomArray.h"
+ #include "mozilla/ServoTypes.h"
+ #include "mozilla/ServoBindingTypes.h"
+ #include "mozilla/ServoElementSnapshot.h"
++#include "mozilla/css/SheetLoadData.h"
+ #include "mozilla/css/SheetParsingMode.h"
+ #include "mozilla/css/URLMatchingFunction.h"
+ #include "mozilla/EffectCompositor.h"
+ #include "mozilla/ComputedTimingFunction.h"
+ #include "nsChangeHint.h"
+ #include "nsCSSPseudoClasses.h"
+ #include "nsIDocument.h"
+ #include "nsStyleStruct.h"
+@@ -174,16 +175,17 @@ void Gecko_ServoStyleContext_Destroy(moz
+ void Gecko_ConstructStyleChildrenIterator(RawGeckoElementBorrowed aElement,
+                                           RawGeckoStyleChildrenIteratorBorrowedMut aIterator);
+ void Gecko_DestroyStyleChildrenIterator(RawGeckoStyleChildrenIteratorBorrowedMut aIterator);
+ RawGeckoNodeBorrowedOrNull Gecko_GetNextStyleChild(RawGeckoStyleChildrenIteratorBorrowedMut it);
+ 
+ mozilla::ServoStyleSheet*
+ Gecko_LoadStyleSheet(mozilla::css::Loader* loader,
+                      mozilla::ServoStyleSheet* parent,
++                     mozilla::css::SheetLoadData* parent_load_data,
+                      mozilla::css::LoaderReusableStyleSheets* reusable_sheets,
+                      RawGeckoURLExtraData* base_url_data,
+                      const uint8_t* url_bytes,
+                      uint32_t url_length,
+                      RawServoMediaListStrong media_list);
+ 
+ // Selector Matching.
+ uint64_t Gecko_ElementState(RawGeckoElementBorrowed element);
+diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml
+--- a/layout/style/ServoBindings.toml
++++ b/layout/style/ServoBindings.toml
+@@ -42,16 +42,17 @@ args = [
+ headers = [
+     "nsStyleStruct.h",
+     "mozilla/ServoPropPrefList.h",
+     "mozilla/StyleAnimationValue.h",
+     "gfxFontConstants.h",
+     "gfxFontFeatures.h",
+     "nsThemeConstants.h",
+     "mozilla/css/Loader.h",
++    "mozilla/css/SheetLoadData.h",
+     "mozilla/dom/AnimationEffectReadOnlyBinding.h",
+     "mozilla/dom/HTMLSlotElement.h",
+     "mozilla/dom/KeyframeEffectBinding.h",
+     "mozilla/AnimationPropertySegment.h",
+     "mozilla/ComputedTiming.h",
+     "mozilla/ComputedTimingFunction.h",
+     "mozilla/Keyframe.h",
+     "mozilla/ServoElementSnapshot.h",
+@@ -210,16 +211,17 @@ whitelist-types = [
+     "mozilla::SeenPtrs",
+     "mozilla::ServoElementSnapshot.*",
+     "mozilla::ServoStyleContext",
+     "mozilla::ServoStyleSheetInner",
+     "mozilla::ServoStyleSetSizes",
+     "mozilla::CSSPseudoClassType",
+     "mozilla::css::ErrorReporter",
+     "mozilla::css::LoaderReusableStyleSheets",
++    "mozilla::css::SheetLoadData",
+     "mozilla::css::SheetParsingMode",
+     "mozilla::css::URLMatchingFunction",
+     "mozilla::dom::IterationCompositeOperation",
+     "mozilla::dom::StyleChildrenIterator",
+     "mozilla::HalfCorner",
+     "mozilla::MallocSizeOf",
+     "mozilla::OriginFlags",
+     "mozilla::PropertyStyleAnimationValuePair",
+@@ -577,16 +579,17 @@ structs-types = [
+     "nsStyleVisibility",
+     "nsStyleXUL",
+     "nsTimingFunction",
+     "nscolor",
+     "nscoord",
+     "nsresult",
+     "Loader",
+     "LoaderReusableStyleSheets",
++    "SheetLoadData",
+     "ServoStyleSheet",
+     "ServoComputedData",
+     "ServoStyleContext",
+     "ServoStyleContextStrong",
+     "EffectCompositor_CascadeLevel",
+     "UpdateAnimationsTasks",
+     "ParsingMode",
+     "InheritTarget",
+diff --git a/layout/style/ServoStyleSheet.cpp b/layout/style/ServoStyleSheet.cpp
+--- a/layout/style/ServoStyleSheet.cpp
++++ b/layout/style/ServoStyleSheet.cpp
+@@ -198,26 +198,28 @@ ServoStyleSheet::HasRules() const
+ }
+ 
+ nsresult
+ ServoStyleSheet::ParseSheet(css::Loader* aLoader,
+                             Span<const uint8_t> aInput,
+                             nsIURI* aSheetURI,
+                             nsIURI* aBaseURI,
+                             nsIPrincipal* aSheetPrincipal,
++                            css::SheetLoadData* aLoadData,
+                             uint32_t aLineNumber,
+                             nsCompatibility aCompatMode,
+                             css::LoaderReusableStyleSheets* aReusableSheets)
+ {
+   MOZ_ASSERT(!mMedia || mMedia->IsServo());
+   RefPtr<URLExtraData> extraData =
+     new URLExtraData(aBaseURI, aSheetURI, aSheetPrincipal);
+ 
+   Inner()->mContents = Servo_StyleSheet_FromUTF8Bytes(aLoader,
+                                                       this,
++                                                      aLoadData,
+                                                       aInput.Elements(),
+                                                       aInput.Length(),
+                                                       mParsingMode,
+                                                       extraData,
+                                                       aLineNumber,
+                                                       aCompatMode,
+                                                       aReusableSheets)
+                          .Consume();
+@@ -300,16 +302,17 @@ ServoStyleSheet::ReparseSheet(const nsAS
+ 
+   DropRuleList();
+ 
+   nsresult rv = ParseSheet(loader,
+                            NS_ConvertUTF16toUTF8(aInput),
+                            mInner->mSheetURI,
+                            mInner->mBaseURI,
+                            mInner->mPrincipal,
++                           /* aLoadData = */ nullptr,
+                            lineNumber,
+                            eCompatibility_FullStandards,
+                            &reusableSheets);
+   DidDirty();
+   NS_ENSURE_SUCCESS(rv, rv);
+ 
+   // Notify the stylesets about the new rules.
+   {
+diff --git a/layout/style/ServoStyleSheet.h b/layout/style/ServoStyleSheet.h
+--- a/layout/style/ServoStyleSheet.h
++++ b/layout/style/ServoStyleSheet.h
+@@ -18,16 +18,17 @@
+ 
+ namespace mozilla {
+ 
+ class ServoCSSRuleList;
+ 
+ namespace css {
+ class Loader;
+ class LoaderReusableStyleSheets;
++class SheetLoadData;
+ }
+ 
+ // -------------------------------
+ // Servo Style Sheet Inner Data Container
+ //
+ 
+ struct ServoStyleSheetInner final : public StyleSheetInfo
+ {
+@@ -79,22 +80,25 @@ public:
+ 
+   NS_DECL_ISUPPORTS_INHERITED
+   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServoStyleSheet, StyleSheet)
+ 
+   NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVO_STYLE_SHEET_IMPL_CID)
+ 
+   bool HasRules() const;
+ 
++  // Parses a stylesheet. The aLoadData argument corresponds to the
++  // SheetLoadData for this stylesheet. It may be null in some cases.
+   MOZ_MUST_USE nsresult
+   ParseSheet(css::Loader* aLoader,
+              Span<const uint8_t> aInput,
+              nsIURI* aSheetURI,
+              nsIURI* aBaseURI,
+              nsIPrincipal* aSheetPrincipal,
++             css::SheetLoadData* aLoadData,
+              uint32_t aLineNumber,
+              nsCompatibility aCompatMode,
+              css::LoaderReusableStyleSheets* aReusableSheets = nullptr);
+ 
+   nsresult ReparseSheet(const nsAString& aInput);
+ 
+   const RawServoStyleSheetContents* RawContents() const {
+     return Inner()->mContents;
+diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp
+--- a/layout/style/nsCSSParser.cpp
++++ b/layout/style/nsCSSParser.cpp
+@@ -138,16 +138,17 @@ public:
+ 
+   // Clears everything set by the above Set*() functions.
+   void Reset();
+ 
+   nsresult ParseSheet(const nsAString& aInput,
+                       nsIURI*          aSheetURI,
+                       nsIURI*          aBaseURI,
+                       nsIPrincipal*    aSheetPrincipal,
++                      SheetLoadData*   aLoadData,
+                       uint32_t         aLineNumber,
+                       css::LoaderReusableStyleSheets* aReusableSheets);
+ 
+   already_AddRefed<css::Declaration>
+            ParseStyleAttribute(const nsAString&  aAttributeValue,
+                                nsIURI*           aDocURL,
+                                nsIURI*           aBaseURL,
+                                nsIPrincipal*     aNodePrincipal);
+@@ -1293,16 +1294,19 @@ protected:
+   nsCOMPtr<nsIURI> mSheetURI;
+ 
+   // The principal of the sheet involved
+   nsCOMPtr<nsIPrincipal> mSheetPrincipal;
+ 
+   // The sheet we're parsing into
+   RefPtr<CSSStyleSheet> mSheet;
+ 
++  // The data describing this load, if applicable.
++  RefPtr<SheetLoadData> mLoadData;
++
+   // Used for @import rules
+   css::Loader* mChildLoader; // not ref counted, it owns us
+ 
+   // Any sheets we may reuse when parsing an @import.
+   css::LoaderReusableStyleSheets* mReusableSheets;
+ 
+   // Sheet section we're in.  This is used to enforce correct ordering of the
+   // various rule types (eg the fact that a @charset rule must come before
+@@ -1563,16 +1567,17 @@ CSSParserImpl::ReleaseScanner()
+   mSheetPrincipal = nullptr;
+ }
+ 
+ nsresult
+ CSSParserImpl::ParseSheet(const nsAString& aInput,
+                           nsIURI*          aSheetURI,
+                           nsIURI*          aBaseURI,
+                           nsIPrincipal*    aSheetPrincipal,
++                          SheetLoadData*   aLoadData,
+                           uint32_t         aLineNumber,
+                           css::LoaderReusableStyleSheets* aReusableSheets)
+ {
+   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
+   NS_PRECONDITION(aBaseURI, "need base URI");
+   NS_PRECONDITION(aSheetURI, "need sheet URI");
+   NS_PRECONDITION(mSheet, "Must have sheet to parse into");
+   NS_ENSURE_STATE(mSheet);
+@@ -1586,16 +1591,17 @@ CSSParserImpl::ParseSheet(const nsAStrin
+                                                         &equal)) &&
+                equal,
+                "Sheet principal does not match passed principal");
+ #endif
+ 
+   nsCSSScanner scanner(aInput, aLineNumber);
+   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
+   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
++  mLoadData = aLoadData;
+ 
+   int32_t ruleCount = mSheet->StyleRuleCount();
+   if (0 < ruleCount) {
+     const css::Rule* lastRule = mSheet->GetStyleRuleAt(ruleCount - 1);
+     if (lastRule) {
+       switch (lastRule->GetType()) {
+         case css::Rule::CHARSET_RULE:
+         case css::Rule::IMPORT_RULE:
+@@ -1635,16 +1641,17 @@ CSSParserImpl::ParseSheet(const nsAStrin
+     UngetToken();
+     if (ParseRuleSet(AppendRuleToSheet, this)) {
+       mSection = eCSSSection_General;
+     }
+   }
+ 
+   mSheet->SetSourceMapURLFromComment(scanner.GetSourceMapURL());
+   mSheet->SetSourceURL(scanner.GetSourceURL());
++  mLoadData = nullptr;
+   ReleaseScanner();
+ 
+   mParsingMode = css::eAuthorSheetFeatures;
+   mIsChrome = false;
+   mReusableSheets = nullptr;
+ 
+   return NS_OK;
+ }
+@@ -3686,17 +3693,17 @@ CSSParserImpl::ProcessImport(const nsStr
+       // import url is bad
+       REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec);
+       OUTPUT_ERROR();
+     }
+     return;
+   }
+ 
+   if (mChildLoader) {
+-    mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule, mReusableSheets);
++    mChildLoader->LoadChildSheet(mSheet, mLoadData, url, aMedia, rule, mReusableSheets);
+   }
+ }
+ 
+ // Parse the {} part of an @media or @-moz-document rule.
+ bool
+ CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
+                               RuleAppendFunc aAppendFunc,
+                               void* aData)
+@@ -17970,22 +17977,23 @@ nsCSSParser::Shutdown()
+ 
+ // Wrapper methods
+ 
+ nsresult
+ nsCSSParser::ParseSheet(const nsAString& aInput,
+                         nsIURI*          aSheetURI,
+                         nsIURI*          aBaseURI,
+                         nsIPrincipal*    aSheetPrincipal,
++                        SheetLoadData*   aLoadData,
+                         uint32_t         aLineNumber,
+                         css::LoaderReusableStyleSheets* aReusableSheets)
+ {
+   return static_cast<CSSParserImpl*>(mImpl)->
+-    ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber,
+-               aReusableSheets);
++    ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLoadData,
++               aLineNumber, aReusableSheets);
+ }
+ 
+ already_AddRefed<css::Declaration>
+ nsCSSParser::ParseStyleAttribute(const nsAString&  aAttributeValue,
+                                  nsIURI*           aDocURI,
+                                  nsIURI*           aBaseURI,
+                                  nsIPrincipal*     aNodePrincipal)
+ {
+diff --git a/layout/style/nsCSSParser.h b/layout/style/nsCSSParser.h
+--- a/layout/style/nsCSSParser.h
++++ b/layout/style/nsCSSParser.h
+@@ -8,16 +8,17 @@
+ 
+ #ifndef nsCSSParser_h___
+ #define nsCSSParser_h___
+ 
+ #ifdef MOZ_OLD_STYLE
+ 
+ #include "mozilla/Attributes.h"
+ #include "mozilla/css/Loader.h"
++#include "mozilla/css/SheetLoadData.h"
+ 
+ #include "nsCSSPropertyID.h"
+ #include "nsCSSScanner.h"
+ #include "nsCOMPtr.h"
+ #include "nsAutoPtr.h"
+ #include "nsStringFwd.h"
+ #include "nsTArrayForwardDeclare.h"
+ 
+@@ -69,24 +70,26 @@ public:
+    * @param aInput the data to parse
+    * @param aSheetURL the URI to use as the sheet URI (for error reporting).
+    *                  This must match the URI of the sheet passed to
+    *                  the constructor.
+    * @param aBaseURI the URI to use for relative URI resolution
+    * @param aSheetPrincipal the principal of the stylesheet.  This must match
+    *                        the principal of the sheet passed to the
+    *                        constructor.
++   * @param aLoadData the SheetLoadData for this sheet, if applicable.
+    * @param aLineNumber the line number of the first line of the sheet.
+    * @param aReusableSheets style sheets that can be reused by an @import.
+    *                        This can be nullptr.
+    */
+   nsresult ParseSheet(const nsAString& aInput,
+                       nsIURI*          aSheetURL,
+                       nsIURI*          aBaseURI,
+                       nsIPrincipal*    aSheetPrincipal,
++                      mozilla::css::SheetLoadData* aLoadData,
+                       uint32_t         aLineNumber,
+                       mozilla::css::LoaderReusableStyleSheets* aReusableSheets =
+                         nullptr);
+ 
+   // Parse HTML style attribute or its equivalent in other markup
+   // languages.  aBaseURL is the base url to use for relative links in
+   // the declaration.
+   already_AddRefed<mozilla::css::Declaration>
+diff --git a/layout/style/nsLayoutStylesheetCache.cpp b/layout/style/nsLayoutStylesheetCache.cpp
+--- a/layout/style/nsLayoutStylesheetCache.cpp
++++ b/layout/style/nsLayoutStylesheetCache.cpp
+@@ -978,17 +978,17 @@ nsLayoutStylesheetCache::BuildPreference
+     sheet->AsGecko()->ReparseSheet(NS_ConvertUTF8toUTF16(sheetText));
+ #else
+     MOZ_CRASH("old style system disabled");
+ #endif
+   } else {
+     ServoStyleSheet* servoSheet = sheet->AsServo();
+     // NB: The pref sheet never has @import rules.
+     nsresult rv = servoSheet->ParseSheet(
+-      nullptr, sheetText, uri, uri, nullptr, 0, eCompatibility_FullStandards);
++      nullptr, sheetText, uri, uri, nullptr, /* aLoadData = */ nullptr, 0, eCompatibility_FullStandards);
+     // Parsing the about:PreferenceStyleSheet URI can only fail on OOM. If we
+     // are OOM before we parsed any documents we might as well abort.
+     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+   }
+ 
+ #undef NS_GET_R_G_B
+ }
+ 
+diff --git a/layout/style/test/gtest/StyloParsingBench.cpp b/layout/style/test/gtest/StyloParsingBench.cpp
+--- a/layout/style/test/gtest/StyloParsingBench.cpp
++++ b/layout/style/test/gtest/StyloParsingBench.cpp
+@@ -29,16 +29,17 @@ static void ServoParsingBench() {
+   ASSERT_EQ(Encoding::UTF8ValidUpTo(css), css.Length());
+ 
+   RefPtr<URLExtraData> data = new URLExtraData(
+     NullPrincipalURI::Create(), nullptr, NullPrincipal::Create());
+   for (int i = 0; i < PARSING_REPETITIONS; i++) {
+     RefPtr<RawServoStyleSheetContents> stylesheet =
+       Servo_StyleSheet_FromUTF8Bytes(nullptr,
+                                      nullptr,
++                                     nullptr,
+                                      css.Elements(),
+                                      css.Length(),
+                                      eAuthorSheetFeatures,
+                                      data,
+                                      0,
+                                      eCompatibility_FullStandards,
+                                      nullptr)
+         .Consume();

+ 556 - 0
frg/work-js/mozilla-release/patches/1442126-60a1.patch

@@ -0,0 +1,556 @@
+# HG changeset patch
+# User Boris Zbarsky <bzbarsky@mit.edu>
+# Date 1520365527 18000
+# Node ID 6d99da728f77889eaed19f7b9fae25d0e3971326
+# Parent  4f7c4aee860987dc2267e7348f8403affa2f74e2
+Bug 1442126.  Make sure to consistently fail a sheet load if any of its imports fail.  r=bholley
+
+This fixes a race where we would fail if and only if our last-to-complete import failed.
+
+MozReview-Commit-ID: L33bIxlkj08
+
+diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp
+--- a/layout/style/Loader.cpp
++++ b/layout/style/Loader.cpp
+@@ -166,16 +166,17 @@ SheetLoadData::SheetLoadData(Loader* aLo
+   , mIsNonDocumentSheet(false)
+   , mIsLoading(false)
+   , mIsCancelled(false)
+   , mMustNotify(false)
+   , mWasAlternate(aIsAlternate)
+   , mMediaMatched(aMediaMatches)
+   , mUseSystemPrincipal(false)
+   , mSheetAlreadyComplete(false)
++  , mLoadFailed(false)
+   , mOwningElement(aOwningElement)
+   , mObserver(aObserver)
+   , mLoaderPrincipal(aLoaderPrincipal)
+   , mRequestingNode(aRequestingNode)
+   , mPreloadEncoding(nullptr)
+ {
+   NS_PRECONDITION(mLoader, "Must have a loader!");
+ }
+@@ -199,16 +200,17 @@ SheetLoadData::SheetLoadData(Loader* aLo
+   , mIsNonDocumentSheet(false)
+   , mIsLoading(false)
+   , mIsCancelled(false)
+   , mMustNotify(false)
+   , mWasAlternate(false)
+   , mMediaMatched(true)
+   , mUseSystemPrincipal(false)
+   , mSheetAlreadyComplete(false)
++  , mLoadFailed(false)
+   , mOwningElement(nullptr)
+   , mObserver(aObserver)
+   , mLoaderPrincipal(aLoaderPrincipal)
+   , mRequestingNode(aRequestingNode)
+   , mPreloadEncoding(nullptr)
+ {
+   NS_PRECONDITION(mLoader, "Must have a loader!");
+   if (mParentData) {
+@@ -242,16 +244,17 @@ SheetLoadData::SheetLoadData(Loader* aLo
+   , mIsNonDocumentSheet(true)
+   , mIsLoading(false)
+   , mIsCancelled(false)
+   , mMustNotify(false)
+   , mWasAlternate(false)
+   , mMediaMatched(true)
+   , mUseSystemPrincipal(aUseSystemPrincipal)
+   , mSheetAlreadyComplete(false)
++  , mLoadFailed(false)
+   , mOwningElement(nullptr)
+   , mObserver(aObserver)
+   , mLoaderPrincipal(aLoaderPrincipal)
+   , mRequestingNode(aRequestingNode)
+   , mPreloadEncoding(aPreloadEncoding)
+ {
+   NS_PRECONDITION(mLoader, "Must have a loader!");
+   MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
+@@ -308,36 +311,34 @@ SheetLoadData::FireLoadEvent(nsIThreadIn
+   aThread->RemoveObserver(this);
+ 
+   // Now fire the event
+   nsCOMPtr<nsINode> node = do_QueryInterface(mOwningElement);
+   NS_ASSERTION(node, "How did that happen???");
+ 
+   nsContentUtils::DispatchTrustedEvent(node->OwnerDoc(),
+                                        node,
+-                                       NS_SUCCEEDED(mStatus) ?
+-                                         NS_LITERAL_STRING("load") :
+-                                         NS_LITERAL_STRING("error"),
++                                       mLoadFailed ?
++                                         NS_LITERAL_STRING("error") :
++                                         NS_LITERAL_STRING("load"),
+                                        false, false);
+ 
+   // And unblock onload
+   if (mLoader->mDocument) {
+     mLoader->mDocument->UnblockOnload(true);
+   }
+ }
+ 
+ void
+-SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus)
++SheetLoadData::ScheduleLoadEventIfNeeded()
+ {
+   if (!mOwningElement) {
+     return;
+   }
+ 
+-  mStatus = aStatus;
+-
+   nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+   nsCOMPtr<nsIThreadInternal> internalThread = do_QueryInterface(thread);
+   if (NS_SUCCEEDED(internalThread->AddObserver(this))) {
+     // Make sure to block onload here
+     if (mLoader->mDocument) {
+       mLoader->mDocument->BlockOnload();
+     }
+   }
+@@ -1725,23 +1726,31 @@ Loader::ParseSheet(const nsAString& aUTF
+  * "completed" hashtable, massages the XUL cache, handles siblings of
+  * the load data (other loads for the same URI), handles unblocking
+  * blocked parent loads as needed, and most importantly calls
+  * NS_RELEASE on the load data to destroy the whole mess.
+  */
+ void
+ Loader::SheetComplete(SheetLoadData* aLoadData, nsresult aStatus)
+ {
+-  LOG(("css::Loader::SheetComplete"));
++  LOG(("css::Loader::SheetComplete, status: 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
++
++  // If aStatus is a failure we need to mark this data failed.  We also need to
++  // mark any ancestors of a failing data as failed and any sibling of a
++  // failing data as failed.  Note that SheetComplete is never called on a
++  // SheetLoadData that is the mNext of some other SheetLoadData.
++  if (NS_FAILED(aStatus)) {
++    MarkLoadTreeFailed(aLoadData);
++  }
+ 
+   // 8 is probably big enough for all our common cases.  It's not likely that
+   // imports will nest more than 8 deep, and multiple sheets with the same URI
+   // are rare.
+   AutoTArray<RefPtr<SheetLoadData>, 8> datasToNotify;
+-  DoSheetComplete(aLoadData, aStatus, datasToNotify);
++  DoSheetComplete(aLoadData, datasToNotify);
+ 
+   // Now it's safe to go ahead and notify observers
+   uint32_t count = datasToNotify.Length();
+   mDatasToNotifyOn += count;
+   for (uint32_t i = 0; i < count; ++i) {
+     --mDatasToNotifyOn;
+ 
+     SheetLoadData* data = datasToNotify[i];
+@@ -1765,26 +1774,23 @@ Loader::SheetComplete(SheetLoadData* aLo
+ 
+   if (mSheets->mLoadingDatas.Count() == 0 && mSheets->mPendingDatas.Count() > 0) {
+     LOG(("  No more loading sheets; starting deferred loads"));
+     StartDeferredLoads();
+   }
+ }
+ 
+ void
+-Loader::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
+-                        LoadDataArray& aDatasToNotify)
++Loader::DoSheetComplete(SheetLoadData* aLoadData, LoadDataArray& aDatasToNotify)
+ {
+   LOG(("css::Loader::DoSheetComplete"));
+   NS_PRECONDITION(aLoadData, "Must have a load data!");
+   NS_PRECONDITION(aLoadData->mSheet, "Must have a sheet");
+   NS_ASSERTION(mSheets, "mLoadingDatas should be initialized by now.");
+ 
+-  LOG(("Load completed, status: 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
+-
+   // Twiddle the hashtables
+   if (aLoadData->mURI) {
+     LOG_URI("  Finished loading: '%s'", aLoadData->mURI);
+     // Remove the data from the list of loading datas
+     if (aLoadData->mIsLoading) {
+       URIPrincipalReferrerPolicyAndCORSModeHashKey key(aLoadData->mURI,
+                                          aLoadData->mLoaderPrincipal,
+                                          aLoadData->mSheet->GetCORSMode(),
+@@ -1807,17 +1813,17 @@ Loader::DoSheetComplete(SheetLoadData* a
+   while (data) {
+     if (!data->mSheetAlreadyComplete) {
+       // If mSheetAlreadyComplete, then the sheet could well be modified between
+       // when we posted the async call to SheetComplete and now, since the sheet
+       // was page-accessible during that whole time.
+       MOZ_ASSERT(!data->mSheet->IsModified(),
+                  "should not get marked modified during parsing");
+       data->mSheet->SetComplete();
+-      data->ScheduleLoadEventIfNeeded(aStatus);
++      data->ScheduleLoadEventIfNeeded();
+     }
+     if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) {
+       // Don't notify here so we don't trigger script.  Remember the
+       // info we need to notify, then do it later when it's safe.
+       aDatasToNotify.AppendElement(data);
+ 
+       // On append failure, just press on.  We'll fail to notify the observer,
+       // but not much we can do about that....
+@@ -1830,27 +1836,27 @@ Loader::DoSheetComplete(SheetLoadData* a
+     // If we have a parent, our parent is no longer being parsed, and
+     // we are the last pending child, then our load completion
+     // completes the parent too.  Note that the parent _can_ still be
+     // being parsed (eg if the child (us) failed to open the channel
+     // or some such).
+     if (data->mParentData &&
+         --(data->mParentData->mPendingChildren) == 0 &&
+         !mParsingDatas.Contains(data->mParentData)) {
+-      DoSheetComplete(data->mParentData, aStatus, aDatasToNotify);
++      DoSheetComplete(data->mParentData, aDatasToNotify);
+     }
+ 
+     data = data->mNext;
+   }
+ 
+   // Now that it's marked complete, put the sheet in our cache.
+-  // If we ever start doing this for failure aStatus, we'll need to
++  // If we ever start doing this for failed loads, we'll need to
+   // adjust the PostLoadEvent code that thinks anything already
+   // complete must have loaded succesfully.
+-  if (NS_SUCCEEDED(aStatus) && aLoadData->mURI) {
++  if (!aLoadData->mLoadFailed && aLoadData->mURI) {
+     // Pick our sheet to cache carefully.  Ideally, we want to cache
+     // one of the sheets that will be kept alive by a document or
+     // parent sheet anyway, so that if someone then accesses it via
+     // CSSOM we won't have extra clones of the inner lying around.
+     data = aLoadData;
+     StyleSheet* sheet = aLoadData->mSheet;
+     while (data) {
+       if (data->mSheet->GetParentSheet() || data->mSheet->GetOwnerNode()) {
+@@ -1883,16 +1889,34 @@ Loader::DoSheetComplete(SheetLoadData* a
+ #ifdef MOZ_XUL
+     }
+ #endif
+   }
+ 
+   NS_RELEASE(aLoadData);  // this will release parents and siblings and all that
+ }
+ 
++void
++Loader::MarkLoadTreeFailed(SheetLoadData* aLoadData)
++{
++  if (aLoadData->mURI) {
++    LOG_URI("  Load failed: '%s'", aLoadData->mURI);
++  }
++
++  do {
++    aLoadData->mLoadFailed = true;
++
++    if (aLoadData->mParentData) {
++      MarkLoadTreeFailed(aLoadData->mParentData);
++    }
++
++    aLoadData = aLoadData->mNext;
++  } while (aLoadData);
++}
++
+ Result<Loader::LoadSheetResult, nsresult>
+ Loader::LoadInlineStyle(nsIContent* aElement,
+                         const nsAString& aBuffer,
+                         nsIPrincipal* aTriggeringPrincipal,
+                         uint32_t aLineNumber,
+                         const nsAString& aTitle,
+                         const nsAString& aMedia,
+                         ReferrerPolicy aReferrerPolicy,
+@@ -2491,20 +2515,22 @@ Loader::PostLoadEvent(nsIURI* aURI,
+       mDocument->BlockOnload();
+     }
+ 
+     // We want to notify the observer for this data.
+     evt->mMustNotify = true;
+     evt->mSheetAlreadyComplete = true;
+ 
+     // If we get to this code, aSheet loaded correctly at some point, so
+-    // we can just use NS_OK for the status.  Note that we do this here
+-    // and not from inside our SheetComplete so that we don't end up
+-    // running the load event async.
+-    evt->ScheduleLoadEventIfNeeded(NS_OK);
++    // we can just schedule a load event and don't need to touch the
++    // data's mLoadFailed.  Note that we do this here and not from
++    // inside our SheetComplete so that we don't end up running the load
++    // event async.
++    MOZ_ASSERT(!evt->mLoadFailed, "Why are we marked as failed?");
++    evt->ScheduleLoadEventIfNeeded();
+   }
+ 
+   return rv;
+ }
+ 
+ void
+ Loader::HandleLoadEvent(SheetLoadData* aEvent)
+ {
+diff --git a/layout/style/Loader.h b/layout/style/Loader.h
+--- a/layout/style/Loader.h
++++ b/layout/style/Loader.h
+@@ -576,18 +576,22 @@ private:
+ 
+   // The load of the sheet in aLoadData is done, one way or another.  Do final
+   // cleanup, including releasing aLoadData.
+   void SheetComplete(SheetLoadData* aLoadData, nsresult aStatus);
+ 
+   // The guts of SheetComplete.  This may be called recursively on parent datas
+   // or datas that had glommed on to a single load.  The array is there so load
+   // datas whose observers need to be notified can be added to it.
+-  void DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
+-                       LoadDataArray& aDatasToNotify);
++  void DoSheetComplete(SheetLoadData* aLoadData, LoadDataArray& aDatasToNotify);
++
++  // Mark the given SheetLoadData, as well as any of its siblings, parents, etc
++  // transitively, as failed.  The idea is to mark as failed any load that was
++  // directly or indirectly @importing the sheet this SheetLoadData represents.
++  void MarkLoadTreeFailed(SheetLoadData* aLoadData);
+ 
+   StyleBackendType GetStyleBackendType() const;
+ 
+   struct Sheets {
+     nsBaseHashtable<URIPrincipalReferrerPolicyAndCORSModeHashKey,
+                     RefPtr<StyleSheet>,
+                     StyleSheet*> mCompleteSheets;
+     nsDataHashtable<URIPrincipalReferrerPolicyAndCORSModeHashKey, SheetLoadData*>
+diff --git a/layout/style/SheetLoadData.h b/layout/style/SheetLoadData.h
+--- a/layout/style/SheetLoadData.h
++++ b/layout/style/SheetLoadData.h
+@@ -73,17 +73,17 @@ public:
+                 bool aUseSystemPrincipal,
+                 const Encoding* aPreloadEncoding,
+                 nsICSSLoaderObserver* aObserver,
+                 nsIPrincipal* aLoaderPrincipal,
+                 nsINode* aRequestingNode);
+ 
+   already_AddRefed<nsIURI> GetReferrerURI();
+ 
+-  void ScheduleLoadEventIfNeeded(nsresult aStatus);
++  void ScheduleLoadEventIfNeeded();
+ 
+   NotNull<const Encoding*> DetermineNonBOMEncoding(nsACString const& aSegment,
+                                                    nsIChannel* aChannel);
+ 
+   nsresult VerifySheetReadyToParse(nsresult aStatus,
+                                    const nsACString& aBytes,
+                                    nsIChannel* aChannel);
+ 
+@@ -162,16 +162,20 @@ public:
+   // this sheet, no matter what the channel principal is.  Only true for sync
+   // loads.
+   bool mUseSystemPrincipal : 1;
+ 
+   // If true, this SheetLoadData is being used as a way to handle
+   // async observer notification for an already-complete sheet.
+   bool mSheetAlreadyComplete : 1;
+ 
++  // Boolean flag indicating whether the load has failed.  This will be set
++  // to true if this load, or the load of any descendant import, fails.
++  bool mLoadFailed : 1;
++
+   // This is the element that imported the sheet.  Needed to get the
+   // charset set on it and to fire load/error events.
+   nsCOMPtr<nsIStyleSheetLinkingElement> mOwningElement;
+ 
+   // The observer that wishes to be notified of load completion
+   nsCOMPtr<nsICSSLoaderObserver> mObserver;
+ 
+   // The principal that identifies who started loading us.
+@@ -184,22 +188,16 @@ public:
+   // is non-null.
+   const Encoding* mPreloadEncoding;
+ 
+   bool ShouldDefer() const
+   {
+     return mWasAlternate || !mMediaMatched;
+   }
+ 
+-  // The status our load ended up with; this determines whether we
+-  // should fire error events or load events.  This gets initialized
+-  // by ScheduleLoadEventIfNeeded, and is only used after that has
+-  // been called.
+-  MOZ_INIT_OUTSIDE_CTOR nsresult mStatus;
+-
+ private:
+   void FireLoadEvent(nsIThreadInternal* aThread);
+ };
+ 
+ } // namespace css
+ } // namespace mozilla
+ 
+ #endif // mozilla_css_SheetLoadData_h
+diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini
+--- a/layout/style/test/mochitest.ini
++++ b/layout/style/test/mochitest.ini
+@@ -232,16 +232,17 @@ skip-if = toolkit == 'android'
+ [test_initial_computation.html]
+ skip-if = toolkit == 'android'
+ [test_initial_storage.html]
+ [test_invalidation_basic.html]
+ skip-if = !stylo
+ [test_keyframes_rules.html]
+ [test_keyframes_vendor_prefix.html]
+ [test_load_events_on_stylesheets.html]
++support-files = slow_broken_sheet.sjs slow_ok_sheet.sjs
+ [test_logical_properties.html]
+ [test_media_queries.html]
+ skip-if = android_version == '18' #debug-only failure; timed out #Android 4.3 aws only; bug 1030419
+ [test_media_queries_dynamic.html]
+ [test_media_queries_dynamic_xbl.html]
+ [test_media_query_list.html]
+ [test_media_query_serialization.html]
+ [test_moz_device_pixel_ratio.html]
+diff --git a/layout/style/test/slow_broken_sheet.sjs b/layout/style/test/slow_broken_sheet.sjs
+new file mode 100644
+--- /dev/null
++++ b/layout/style/test/slow_broken_sheet.sjs
+@@ -0,0 +1,16 @@
++// Make sure our timer stays alive.
++let gTimer;
++
++function handleRequest(request, response)
++{
++  response.setHeader("Content-Type", "text/html", false);
++  response.setStatusLine("1.1", 404, "Not Found");
++  response.processAsync();
++
++  gTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
++  // Wait for 1s before responding; this should usually make sure this load comes in last.
++  gTimer.init(() => {
++    response.write("<h1>Hello</h1>");
++    response.finish();
++  }, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
++}
+diff --git a/layout/style/test/slow_ok_sheet.sjs b/layout/style/test/slow_ok_sheet.sjs
+new file mode 100644
+--- /dev/null
++++ b/layout/style/test/slow_ok_sheet.sjs
+@@ -0,0 +1,18 @@
++// Make sure our timer stays alive.
++let gTimer;
++
++function handleRequest(request, response)
++{
++  response.setHeader("Content-Type", "text/css", false);
++  response.setStatusLine("1.1", 200, "OK");
++  response.processAsync();
++
++  gTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
++  // Wait for 1s before responding; this should usually make sure this load comes in last.
++  gTimer.init(() => {
++    // This sheet _does_ still get applied even though its importing link
++    // overall reports failure...
++    response.write("nosuchelement { background: red }");
++    response.finish();
++  }, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
++}
+diff --git a/layout/style/test/test_load_events_on_stylesheets.html b/layout/style/test/test_load_events_on_stylesheets.html
+--- a/layout/style/test/test_load_events_on_stylesheets.html
++++ b/layout/style/test/test_load_events_on_stylesheets.html
+@@ -42,19 +42,19 @@ is(pendingEventCounter, 0, "There should
+ // Test sheet that will already be complete when we write it out
+ ++pendingEventCounter;
+ 
+ // Make sure that a postMessage we do right now fires after the onload handler
+ // for the stylesheet.  If we ever change the timing of sheet onload, we will
+ // need to change that.
+ window.onmessage = function() {
+   messagePosted = true;
+-  // There are 4 pending events: two from the two direct example.com loads,
+-  // and 2 from the two data:text/css loads that import things
+-  is(pendingEventCounter, 4, "Load event for sheet should have fired");
++  // There are 6 pending events: two from the two direct example.com loads,
++  // and 4 from the two data:text/css loads that import things
++  is(pendingEventCounter, 6, "Load event for sheet should have fired");
+ }
+ window.postMessage("", "*");
+ 
+ function checkSheetComplete(sheet, length) {
+   try {
+     is(sheet.cssRules.length, length, `Should be loaded with ${length} rule(s)`);
+   } catch (e) {
+     ok(false, "Sheet has not been loaded completely");
+@@ -86,29 +86,29 @@ document.write('<style\
+                         ok(false, \'Error event firing on inline stylesheet\')"></style>');
+ 
+ // Make sure the load event for that second stylesheet has not fired yet
+ is(pendingEventCounter, 2, "There should be two pending events");
+ 
+ ++pendingEventCounter;
+ document.write('<link rel="stylesheet" href="http://www.example.com"\
+                 onload="--pendingEventCounter;\
+-                        ok(false, \'Load event firing on broken stylesheet\')"\
++                        ok(false, \'Load event firing on broken stylesheet 1\')"\
+                 onerror="--pendingEventCounter;\
+-                        ok(true, \'Error event firing on broken stylesheet\')">');
++                        ok(true, \'Error event firing on broken stylesheet 1\')">');
+ 
+ ++pendingEventCounter;
+ var link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = "http://www.example.com";
+ link.onload = function() { --pendingEventCounter;
+-  ok(false, 'Load event firing on broken stylesheet');
++  ok(false, 'Load event firing on broken stylesheet 2');
+ };
+ link.onerror = function() { --pendingEventCounter;
+-  ok(true, 'Error event firing on broken stylesheet');
++  ok(true, 'Error event firing on broken stylesheet 2');
+ }
+ document.body.appendChild(link);
+ 
+ ++pendingEventCounter;
+ link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = "data:text/css,*{}";
+ link.onload = function() { --pendingEventCounter;
+@@ -139,22 +139,50 @@ link.onerror = function() { --pendingEve
+ }
+ document.body.appendChild(link);
+ 
+ ++pendingEventCounter;
+ link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = "data:text/css,@import url('http://www.example.com')";
+ link.onload = function() { --pendingEventCounter;
+-  ok(false, 'Load event firing on broken stylesheet');
++  ok(false, 'Load event firing on broken stylesheet 3');
+ };
+ link.onerror = function() { --pendingEventCounter;
+-  ok(true, 'Error event firing on broken stylesheet');
++  ok(true, 'Error event firing on broken stylesheet 3');
++}
++document.body.appendChild(link);
++
++function absoluteURL(relativeURL) {
++  return new URL(relativeURL, location.href).href;
++}
++
++++pendingEventCounter;
++link = document.createElement("link");
++link.rel = "stylesheet";
++link.href = `data:text/css,@import url('http://www.example.com'); @import url(${absoluteURL('slow_ok_sheet.sjs')});`;
++link.onload = function() { --pendingEventCounter;
++  ok(false, 'Load event firing on broken stylesheet 4');
++};
++link.onerror = function() { --pendingEventCounter;
++  ok(true, 'Error event firing on broken stylesheet 4');
++}
++document.body.appendChild(link);
++
++++pendingEventCounter;
++link = document.createElement("link");
++link.rel = "stylesheet";
++link.href = `data:text/css,@import url(${absoluteURL('slow_broken_sheet.sjs')}); @import url('data:text/css,');`;
++link.onload = function() { --pendingEventCounter;
++  ok(false, 'Load event firing on broken stylesheet 5');
++};
++link.onerror = function() { --pendingEventCounter;
++  ok(true, 'Error event firing on broken stylesheet 5');
+ }
+ document.body.appendChild(link);
+ 
+ // Make sure the load events for all those stylesheets have not fired yet
+-is(pendingEventCounter, 7, "There should be one pending event");
++is(pendingEventCounter, 9, "There should be nine pending events");
+ 
+ </script>
+ </pre>
+ </body>
+ </html>

+ 81 - 0
frg/work-js/mozilla-release/patches/1442153-61a1.patch

@@ -0,0 +1,81 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1519892616 28800
+# Node ID 0a61a8fd3866b2a7a17bf866c9aaab8bff434c0b
+# Parent  625735487f0654a1f19bae7358b32dc0cb53fe71
+Bug 1442153 - Convert browser_inspector_expand-collapse.js to async/await. r=ochameau
+
+MozReview-Commit-ID: 39FYLGiDJCy
+
+diff --git a/devtools/client/inspector/test/browser_inspector_expand-collapse.js b/devtools/client/inspector/test/browser_inspector_expand-collapse.js
+--- a/devtools/client/inspector/test/browser_inspector_expand-collapse.js
++++ b/devtools/client/inspector/test/browser_inspector_expand-collapse.js
+@@ -4,27 +4,27 @@
+ 
+ "use strict";
+ 
+ // Tests that context menu items exapnd all and collapse are shown properly.
+ 
+ const TEST_URL = "data:text/html;charset=utf-8," +
+                  "<div id='parent-node'><div id='child-node'></div></div>";
+ 
+-add_task(function* () {
++add_task(async function() {
+   // Test is often exceeding time-out threshold, similar to Bug 1137765
+   requestLongerTimeout(2);
+ 
+-  let {inspector} = yield openInspectorForURL(TEST_URL);
++  let {inspector} = await openInspectorForURL(TEST_URL);
+ 
+   info("Selecting the parent node");
+ 
+-  let front = yield getNodeFrontForSelector("#parent-node", inspector);
++  let front = await getNodeFrontForSelector("#parent-node", inspector);
+ 
+-  yield selectNode(front, inspector);
++  await selectNode(front, inspector);
+ 
+   info("Simulating context menu click on the selected node container.");
+   let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+     target: getContainerForNodeFront(front, inspector).tagLine,
+   });
+   let nodeMenuCollapseElement =
+     allMenuItems.find(item => item.id === "node-menu-collapse");
+   let nodeMenuExpandElement =
+@@ -32,33 +32,33 @@ add_task(function* () {
+ 
+   ok(nodeMenuCollapseElement.disabled, "Collapse option is disabled");
+   ok(!nodeMenuExpandElement.disabled, "ExpandAll option is enabled");
+ 
+   info("Testing whether expansion works properly");
+   nodeMenuExpandElement.click();
+ 
+   info("Waiting for expansion to occur");
+-  yield waitForMultipleChildrenUpdates(inspector);
++  await waitForMultipleChildrenUpdates(inspector);
+   let markUpContainer = getContainerForNodeFront(front, inspector);
+   ok(markUpContainer.expanded, "node has been successfully expanded");
+ 
+   // reselecting node after expansion
+-  yield selectNode(front, inspector);
++  await selectNode(front, inspector);
+ 
+   info("Testing whether collapse works properly");
+   info("Simulating context menu click on the selected node container.");
+   allMenuItems = openContextMenuAndGetAllItems(inspector, {
+     target: getContainerForNodeFront(front, inspector).tagLine,
+   });
+   nodeMenuCollapseElement =
+     allMenuItems.find(item => item.id === "node-menu-collapse");
+   nodeMenuExpandElement =
+     allMenuItems.find(item => item.id === "node-menu-expand");
+ 
+   ok(!nodeMenuCollapseElement.disabled, "Collapse option is enabled");
+   ok(!nodeMenuExpandElement.disabled, "ExpandAll option is enabled");
+   nodeMenuCollapseElement.click();
+ 
+   info("Waiting for collapse to occur");
+-  yield waitForMultipleChildrenUpdates(inspector);
++  await waitForMultipleChildrenUpdates(inspector);
+   ok(!markUpContainer.expanded, "node has been successfully collapsed");
+ });

+ 48 - 48
frg/work-js/mozilla-release/patches/1443081-12-client-shared-61a1.patch

@@ -2,7 +2,7 @@
 # User J. Ryan Stinnett <jryans@gmail.com>
 # User J. Ryan Stinnett <jryans@gmail.com>
 # Date 1520879078 18000
 # Date 1520879078 18000
 # Node ID 49cce2c9e2ab21606a9abcdc0ab0af991806957f
 # Node ID 49cce2c9e2ab21606a9abcdc0ab0af991806957f
-# Parent  5471884af36f47861f542de793a331232ccdb694
+# Parent  fb4c04871f6f4da88139a76b6fed91fc4c54ad00
 Bug 1443081 - Apply spacing via `eslint --fix` for DevTools - part 12. r=jdescottes
 Bug 1443081 - Apply spacing via `eslint --fix` for DevTools - part 12. r=jdescottes
 
 
 MozReview-Commit-ID: 2RVNt140Zte
 MozReview-Commit-ID: 2RVNt140Zte
@@ -7208,19 +7208,19 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
  CSSFilterEditorWidget.prototype = {
  CSSFilterEditorWidget.prototype = {
 -  _initMarkup: function () {
 -  _initMarkup: function () {
 +  _initMarkup: function() {
 +  _initMarkup: function() {
-     let filterListSelectPlaceholder =
-       L10N.getStr("filterListSelectPlaceholder");
-     let addNewFilterButton = L10N.getStr("addNewFilterButton");
-     let presetsToggleButton = L10N.getStr("presetsToggleButton");
-     let newPresetPlaceholder = L10N.getStr("newPresetPlaceholder");
-     let savePresetButton = L10N.getStr("savePresetButton");
- 
-     // eslint-disable-next-line no-unsanitized/property
-@@ -191,47 +191,47 @@ CSSFilterEditorWidget.prototype = {
-     this.addPresetButton = this.el.querySelector(".presets-list .add");
-     this.addPresetInput = this.el.querySelector(".presets-list .footer input");
- 
-     this.el.querySelector(".presets-list input").value = "";
+     // The following structure is created:
+     //   <div class="filters-list">
+     //     <div id="filters"></div>
+     //     <div class="footer">
+     //       <select value="">
+     //         <option value="">${filterListSelectPlaceholder}</option>
+     //       </select>
+     //       <button id="add-filter" class="add">${addNewFilterButton}</button>
+@@ -231,47 +231,47 @@ CSSFilterEditorWidget.prototype = {
+     this.addPresetButton.textContent = L10N.getStr("savePresetButton");
+     presetListFooter.appendChild(this.addPresetButton);
+ 
+     this.el.appendChild(content);
  
  
      this._populateFilterSelect();
      this._populateFilterSelect();
    },
    },
@@ -7229,8 +7229,8 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
 +  _destroyMarkup: function() {
 +  _destroyMarkup: function() {
      this._filterItemMarkup.remove();
      this._filterItemMarkup.remove();
      this.el.remove();
      this.el.remove();
-     this.el = this.filtersList = this._filterItemMarkup = null;
-     this.presetsList = this.togglePresets = this.filterSelect = null;
+     this.el = this.filterList = this._filterItemMarkup = null;
+     this.presetList = this.togglePresets = this.filterSelect = null;
      this.addPresetButton = null;
      this.addPresetButton = null;
    },
    },
  
  
@@ -7268,7 +7268,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
  
  
      let value = this.doc.createElementNS(XHTML_NS, "div");
      let value = this.doc.createElementNS(XHTML_NS, "div");
      value.className = "filter-value";
      value.className = "filter-value";
-@@ -256,17 +256,17 @@ CSSFilterEditorWidget.prototype = {
+@@ -296,17 +296,17 @@ CSSFilterEditorWidget.prototype = {
  
  
      base.appendChild(name);
      base.appendChild(name);
      base.appendChild(value);
      base.appendChild(value);
@@ -7287,7 +7287,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
  
  
      let value = this.doc.createElementNS(XHTML_NS, "span");
      let value = this.doc.createElementNS(XHTML_NS, "span");
      base.appendChild(value);
      base.appendChild(value);
-@@ -274,17 +274,17 @@ CSSFilterEditorWidget.prototype = {
+@@ -314,17 +314,17 @@ CSSFilterEditorWidget.prototype = {
      let removeButton = this.doc.createElementNS(XHTML_NS, "button");
      let removeButton = this.doc.createElementNS(XHTML_NS, "button");
      removeButton.classList.add("remove-button");
      removeButton.classList.add("remove-button");
  
  
@@ -7300,43 +7300,43 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
 +  _addEventListeners: function() {
 +  _addEventListeners: function() {
      this.addButton = this.el.querySelector("#add-filter");
      this.addButton = this.el.querySelector("#add-filter");
      this.addButton.addEventListener("click", this._addButtonClick);
      this.addButton.addEventListener("click", this._addButtonClick);
-     this.filtersList.addEventListener("click", this._removeButtonClick);
-     this.filtersList.addEventListener("mousedown", this._mouseDown);
-     this.filtersList.addEventListener("keydown", this._keyDown);
+     this.filterList.addEventListener("click", this._removeButtonClick);
+     this.filterList.addEventListener("mousedown", this._mouseDown);
+     this.filterList.addEventListener("keydown", this._keyDown);
      this.el.addEventListener("mousedown", this._resetFocus);
      this.el.addEventListener("mousedown", this._resetFocus);
  
  
-     this.presetsList.addEventListener("click", this._presetClick);
-@@ -295,17 +295,17 @@ CSSFilterEditorWidget.prototype = {
+     this.presetList.addEventListener("click", this._presetClick);
+@@ -335,17 +335,17 @@ CSSFilterEditorWidget.prototype = {
      // drag-drop re-ordering and label-dragging
      // drag-drop re-ordering and label-dragging
      this.win.addEventListener("mousemove", this._mouseMove);
      this.win.addEventListener("mousemove", this._mouseMove);
      this.win.addEventListener("mouseup", this._mouseUp);
      this.win.addEventListener("mouseup", this._mouseUp);
  
  
      // Used to workaround float-precision problems
      // Used to workaround float-precision problems
-     this.filtersList.addEventListener("input", this._input);
+     this.filterList.addEventListener("input", this._input);
    },
    },
  
  
 -  _removeEventListeners: function () {
 -  _removeEventListeners: function () {
 +  _removeEventListeners: function() {
 +  _removeEventListeners: function() {
      this.addButton.removeEventListener("click", this._addButtonClick);
      this.addButton.removeEventListener("click", this._addButtonClick);
-     this.filtersList.removeEventListener("click", this._removeButtonClick);
-     this.filtersList.removeEventListener("mousedown", this._mouseDown);
-     this.filtersList.removeEventListener("keydown", this._keyDown);
+     this.filterList.removeEventListener("click", this._removeButtonClick);
+     this.filterList.removeEventListener("mousedown", this._mouseDown);
+     this.filterList.removeEventListener("keydown", this._keyDown);
      this.el.removeEventListener("mousedown", this._resetFocus);
      this.el.removeEventListener("mousedown", this._resetFocus);
  
  
-     this.presetsList.removeEventListener("click", this._presetClick);
+     this.presetList.removeEventListener("click", this._presetClick);
      this.togglePresets.removeEventListener("click", this._togglePresets);
      this.togglePresets.removeEventListener("click", this._togglePresets);
-@@ -314,21 +314,21 @@ CSSFilterEditorWidget.prototype = {
+@@ -354,21 +354,21 @@ CSSFilterEditorWidget.prototype = {
      // These events are used for drag drop re-ordering
      // These events are used for drag drop re-ordering
      this.win.removeEventListener("mousemove", this._mouseMove);
      this.win.removeEventListener("mousemove", this._mouseMove);
      this.win.removeEventListener("mouseup", this._mouseUp);
      this.win.removeEventListener("mouseup", this._mouseUp);
  
  
      // Used to workaround float-precision problems
      // Used to workaround float-precision problems
-     this.filtersList.removeEventListener("input", this._input);
+     this.filterList.removeEventListener("input", this._input);
    },
    },
  
  
 -  _getFilterElementIndex: function (el) {
 -  _getFilterElementIndex: function (el) {
 +  _getFilterElementIndex: function(el) {
 +  _getFilterElementIndex: function(el) {
-     return [...this.filtersList.children].indexOf(el);
+     return [...this.filterList.children].indexOf(el);
    },
    },
  
  
 -  _keyDown: function (e) {
 -  _keyDown: function (e) {
@@ -7349,7 +7349,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
  
  
      const direction = e.keyCode === 40 ? -1 : 1;
      const direction = e.keyCode === 40 ? -1 : 1;
  
  
-@@ -385,29 +385,29 @@ CSSFilterEditorWidget.prototype = {
+@@ -425,29 +425,29 @@ CSSFilterEditorWidget.prototype = {
        value = split.join("");
        value = split.join("");
        input.value = value;
        input.value = value;
        this.updateValueAt(index, value);
        this.updateValueAt(index, value);
@@ -7381,7 +7381,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
        filterEl.startingY = e.pageY;
        filterEl.startingY = e.pageY;
        filterEl.classList.add("dragging");
        filterEl.classList.add("dragging");
  
  
-@@ -422,48 +422,48 @@ CSSFilterEditorWidget.prototype = {
+@@ -462,48 +462,48 @@ CSSFilterEditorWidget.prototype = {
          index, label, input,
          index, label, input,
          startX: e.pageX
          startX: e.pageX
        };
        };
@@ -7426,7 +7426,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
  
  
 -  _dragFilterElement: function (e) {
 -  _dragFilterElement: function (e) {
 +  _dragFilterElement: function(e) {
 +  _dragFilterElement: function(e) {
-     const rect = this.filtersList.getBoundingClientRect();
+     const rect = this.filterList.getBoundingClientRect();
      let top = e.pageY - LIST_PADDING;
      let top = e.pageY - LIST_PADDING;
      let bottom = e.pageY + LIST_PADDING;
      let bottom = e.pageY + LIST_PADDING;
      // don't allow dragging over top/bottom of list
      // don't allow dragging over top/bottom of list
@@ -7434,7 +7434,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
        return;
        return;
      }
      }
  
  
-@@ -503,17 +503,17 @@ CSSFilterEditorWidget.prototype = {
+@@ -543,17 +543,17 @@ CSSFilterEditorWidget.prototype = {
      }
      }
  
  
      filterEl.removeAttribute("style");
      filterEl.removeAttribute("style");
@@ -7453,7 +7453,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
  
  
      if (e.altKey) {
      if (e.altKey) {
        multiplier = SLOW_VALUE_MULTIPLIER;
        multiplier = SLOW_VALUE_MULTIPLIER;
-@@ -536,17 +536,17 @@ CSSFilterEditorWidget.prototype = {
+@@ -576,17 +576,17 @@ CSSFilterEditorWidget.prototype = {
  
  
      input.value = fixFloat(value);
      input.value = fixFloat(value);
  
  
@@ -7472,7 +7472,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
      if (!this.isReorderingFilter) {
      if (!this.isReorderingFilter) {
        return;
        return;
      }
      }
-@@ -556,17 +556,17 @@ CSSFilterEditorWidget.prototype = {
+@@ -596,17 +596,17 @@ CSSFilterEditorWidget.prototype = {
      filterEl.classList.remove("dragging");
      filterEl.classList.remove("dragging");
      this.el.classList.remove("dragging");
      this.el.classList.remove("dragging");
      filterEl.removeAttribute("style");
      filterEl.removeAttribute("style");
@@ -7491,7 +7491,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
  
  
      let id = +preset.dataset.id;
      let id = +preset.dataset.id;
  
  
-@@ -581,22 +581,22 @@ CSSFilterEditorWidget.prototype = {
+@@ -621,22 +621,22 @@ CSSFilterEditorWidget.prototype = {
          let p = presets[id];
          let p = presets[id];
  
  
          this.setCssValue(p.value);
          this.setCssValue(p.value);
@@ -7516,7 +7516,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
      if (!name || !value || SPECIAL_VALUES.has(value)) {
      if (!name || !value || SPECIAL_VALUES.has(value)) {
        this.emit("preset-save-error");
        this.emit("preset-save-error");
        return;
        return;
-@@ -615,25 +615,25 @@ CSSFilterEditorWidget.prototype = {
+@@ -655,25 +655,25 @@ CSSFilterEditorWidget.prototype = {
                                      console.error);
                                      console.error);
      }, console.error);
      }, console.error);
    },
    },
@@ -7535,16 +7535,16 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
     * There are some delegated events bound in _addEventListeners method
     * There are some delegated events bound in _addEventListeners method
     */
     */
 -  render: function () {
 -  render: function () {
-+  render: function() 
++  render: function() {
      if (!this.filters.length) {
      if (!this.filters.length) {
    // eslint-disable-next-line no-unsanitized/property
    // eslint-disable-next-line no-unsanitized/property
-       this.filtersList.innerHTML = `<p> ${L10N.getStr("emptyFilterList")} <br />
+       this.filterList.innerHTML = `<p> ${L10N.getStr("emptyFilterList")} <br />
                                   ${L10N.getStr("addUsingList")} </p>`;
                                   ${L10N.getStr("addUsingList")} </p>`;
        this.emit("render");
        this.emit("render");
        return;
        return;
      }
      }
  
  
-@@ -699,17 +699,17 @@ CSSFilterEditorWidget.prototype = {
+@@ -739,17 +739,17 @@ CSSFilterEditorWidget.prototype = {
          const end = lastInput.value.length;
          const end = lastInput.value.length;
          lastInput.setSelectionRange(end, end);
          lastInput.setSelectionRange(end, end);
        }
        }
@@ -7557,13 +7557,13 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
 +  renderPresets: function() {
 +  renderPresets: function() {
      this.getPresets().then(presets => {
      this.getPresets().then(presets => {
        // getPresets is async and the widget may be destroyed in between.
        // getPresets is async and the widget may be destroyed in between.
-       if (!this.presetsList) {
+       if (!this.presetList) {
          return;
          return;
        }
        }
  
  
        if (!presets || !presets.length) {
        if (!presets || !presets.length) {
        // eslint-disable-next-line no-unsanitized/property
        // eslint-disable-next-line no-unsanitized/property
-@@ -741,28 +741,28 @@ CSSFilterEditorWidget.prototype = {
+@@ -781,28 +781,28 @@ CSSFilterEditorWidget.prototype = {
    /**
    /**
      * returns definition of a filter as defined in filterList
      * returns definition of a filter as defined in filterList
      *
      *
@@ -7594,7 +7594,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
  
  
      if (SPECIAL_VALUES.has(cssValue)) {
      if (SPECIAL_VALUES.has(cssValue)) {
        this._specialValue = cssValue;
        this._specialValue = cssValue;
-@@ -800,17 +800,17 @@ CSSFilterEditorWidget.prototype = {
+@@ -840,17 +840,17 @@ CSSFilterEditorWidget.prototype = {
      *        single quote, a double quote, or empty.
      *        single quote, a double quote, or empty.
      * @return {Number}
      * @return {Number}
      *        The index of the new filter in the current list of filters
      *        The index of the new filter in the current list of filters
@@ -7613,7 +7613,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
      if (value === null) {
      if (value === null) {
        // UNIT_MAPPING[string] is an empty string (falsy), so
        // UNIT_MAPPING[string] is an empty string (falsy), so
        // using || doesn't work here
        // using || doesn't work here
-@@ -863,17 +863,17 @@ CSSFilterEditorWidget.prototype = {
+@@ -903,17 +903,17 @@ CSSFilterEditorWidget.prototype = {
    /**
    /**
      * returns value + unit of the specified filter
      * returns value + unit of the specified filter
      *
      *
@@ -7632,7 +7632,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
      // Just return the value+unit for non-url functions.
      // Just return the value+unit for non-url functions.
      if (filter.name !== "url") {
      if (filter.name !== "url") {
        return filter.value + filter.unit;
        return filter.value + filter.unit;
-@@ -887,48 +887,48 @@ CSSFilterEditorWidget.prototype = {
+@@ -927,48 +927,48 @@ CSSFilterEditorWidget.prototype = {
      }
      }
  
  
      // Unquoted.  This approach might change the original input -- for
      // Unquoted.  This approach might change the original input -- for
@@ -7684,7 +7684,7 @@ diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/sh
      const def = this._definition(filter.name);
      const def = this._definition(filter.name);
  
  
      if (def.type !== "string") {
      if (def.type !== "string") {
-@@ -940,27 +940,27 @@ CSSFilterEditorWidget.prototype = {
+@@ -980,27 +980,27 @@ CSSFilterEditorWidget.prototype = {
        }
        }
      }
      }
  
  

+ 8 - 3
frg/work-js/mozilla-release/patches/1443081-20-server-tests-61a1.patch

@@ -2,7 +2,7 @@
 # User J. Ryan Stinnett <jryans@gmail.com>
 # User J. Ryan Stinnett <jryans@gmail.com>
 # Date 1520879078 18000
 # Date 1520879078 18000
 # Node ID 49cce2c9e2ab21606a9abcdc0ab0af991806957f
 # Node ID 49cce2c9e2ab21606a9abcdc0ab0af991806957f
-# Parent  11565820d2fdf24d286afe849032535c136ccf1d
+# Parent  da0a84a4deaf1ce1b3b1cb7b093a3050cca13baa
 Bug 1443081 - Apply spacing via `eslint --fix` for DevTools - part 20. r=jdescottes
 Bug 1443081 - Apply spacing via `eslint --fix` for DevTools - part 20. r=jdescottes
 
 
 MozReview-Commit-ID: 2RVNt140Zte
 MozReview-Commit-ID: 2RVNt140Zte
@@ -2212,7 +2212,7 @@ diff --git a/devtools/server/tests/mochitest/inspector-search-data.html b/devtoo
 diff --git a/devtools/server/tests/mochitest/inspector-traversal-data.html b/devtools/server/tests/mochitest/inspector-traversal-data.html
 diff --git a/devtools/server/tests/mochitest/inspector-traversal-data.html b/devtools/server/tests/mochitest/inspector-traversal-data.html
 --- a/devtools/server/tests/mochitest/inspector-traversal-data.html
 --- a/devtools/server/tests/mochitest/inspector-traversal-data.html
 +++ b/devtools/server/tests/mochitest/inspector-traversal-data.html
 +++ b/devtools/server/tests/mochitest/inspector-traversal-data.html
-@@ -15,30 +15,30 @@
+@@ -15,17 +15,17 @@
      }
      }
      #shadow::before {
      #shadow::before {
        content: "Testing ::before on a shadow host";
        content: "Testing ::before on a shadow host";
@@ -2227,7 +2227,12 @@ diff --git a/devtools/server/tests/mochitest/inspector-traversal-data.html b/dev
        let host = document.querySelector("#shadow");
        let host = document.querySelector("#shadow");
        if (host.createShadowRoot) {
        if (host.createShadowRoot) {
          let root = host.createShadowRoot();
          let root = host.createShadowRoot();
-         root.innerHTML = "<h3>Shadow <em>DOM</em></h3><select multiple></select>";
+ 
+         let h3 = document.createElement("h3");
+         h3.append("Shadow ");
+ 
+@@ -39,17 +39,17 @@
+         root.appendChild(select);
        }
        }
  
  
        // Put a copy of the body in an iframe to test frame traversal.
        // Put a copy of the body in an iframe to test frame traversal.

+ 174 - 0
frg/work-js/mozilla-release/patches/1443344-60a1.patch

@@ -0,0 +1,174 @@
+# HG changeset patch
+# User Boris Zbarsky <bzbarsky@mit.edu>
+# Date 1520365527 18000
+# Node ID c64f3b38bf5842ce7704ab8f3f5940926595db49
+# Parent  946e88315b04c02a0e03f030b061062abf9e520c
+Bug 1443344.  Flag a parent sheet load as failed if an import is blocked by content policy.  r=bholley
+
+MozReview-Commit-ID: AArgnuHbCYL
+
+diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp
+--- a/layout/style/Loader.cpp
++++ b/layout/style/Loader.cpp
+@@ -2216,17 +2216,22 @@ Loader::LoadChildSheet(StyleSheet* aPare
+     loadingPrincipal = owningNode->NodePrincipal();
+   } else if (mDocument) {
+     context = mDocument;
+     loadingPrincipal = mDocument->NodePrincipal();
+   }
+ 
+   nsIPrincipal* principal = aParentSheet->Principal();
+   nsresult rv = CheckContentPolicy(loadingPrincipal, principal, aURL, context, false);
+-  NS_ENSURE_SUCCESS(rv, rv);
++  if (NS_WARN_IF(NS_FAILED(rv))) {
++    if (aParentData) {
++      MarkLoadTreeFailed(aParentData);
++    }
++    return rv;
++  }
+ 
+   nsCOMPtr<nsICSSLoaderObserver> observer;
+ 
+   if (aParentData) {
+     LOG(("  Have a parent load"));
+     // Check for cycles
+     if (HaveAncestorDataWithURI(aParentData, aURL)) {
+       // Houston, we have a loop, blow off this child and pretend this never
+diff --git a/layout/style/test/file_bug1443344.css b/layout/style/test/file_bug1443344.css
+new file mode 100644
+--- /dev/null
++++ b/layout/style/test/file_bug1443344.css
+@@ -0,0 +1,1 @@
++#importTarget { color: red ! important }
+diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini
+--- a/layout/style/test/mochitest.ini
++++ b/layout/style/test/mochitest.ini
+@@ -154,16 +154,22 @@ support-files = file_bug1089417_iframe.h
+ [test_bug1292447.html]
+ [test_bug1371488.html]
+ [test_bug1375944.html]
+ support-files = file_bug1375944.html Ahem.ttf
+ [test_bug1382568.html]
+ support-files = bug1382568-iframe.html
+ [test_bug1394302.html]
+ skip-if = !stylo # This is a stylo test; gecko isn't deterministic here
++[test_bug1443344-1.html]
++scheme = https
++support-files = file_bug1443344.css
++[test_bug1443344-2.html]
++scheme = https
++support-files = file_bug1443344.css
+ [test_cascade.html]
+ [test_ch_ex_no_infloops.html]
+ [test_change_hint_optimizations.html]
+ [test_clip-path_polygon.html]
+ [test_color_rounding.html]
+ [test_compute_data_with_start_struct.html]
+ skip-if = toolkit == 'android'
+ [test_computed_style.html]
+diff --git a/layout/style/test/test_bug1443344-1.html b/layout/style/test/test_bug1443344-1.html
+new file mode 100644
+--- /dev/null
++++ b/layout/style/test/test_bug1443344-1.html
+@@ -0,0 +1,48 @@
++<!DOCTYPE HTML>
++<html>
++<!--
++https://bugzilla.mozilla.org/show_bug.cgi?id=1443344
++-->
++<head>
++  <meta charset="utf-8">
++  <title>Test for Bug 1443344</title>
++  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
++  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
++  <script type="application/javascript">
++
++  /** Test for Bug 1443344 **/
++  SimpleTest.waitForExplicitFinish();
++
++  var sheetURL = new URL("file_bug1443344.css", location.href);
++  sheetURL.protocol = "http";
++  var link = document.createElement("link");
++  link.href = `data:text/css,@import url("${sheetURL}");`
++  link.rel = "stylesheet";
++  var loadFired = false, errorFired = false;
++  link.onload = () => loadFired = true;
++  link.onerror = () => errorFired = true;
++  document.head.appendChild(link);
++
++  addLoadEvent(() => {
++    is(loadFired, false, "Should not fire onload for erroring @import");
++    is(errorFired, true, "Should fire onerror for erroring @import");
++    is(getComputedStyle($("importTarget")).color, "rgb(0, 255, 0)",
++       "Erroring sheet should not load");
++    SimpleTest.finish();
++  });
++
++  </script>
++  <style>
++    #importTarget { color: rgb(0, 255, 0); }
++  </style>
++</head>
++<body>
++<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1443344">Mozilla Bug 1443344</a>
++<p id="display"><div id="importTarget"></div></p>
++<div id="content" style="display: none">
++
++</div>
++<pre id="test">
++</pre>
++</body>
++</html>
+diff --git a/layout/style/test/test_bug1443344-2.html b/layout/style/test/test_bug1443344-2.html
+new file mode 100644
+--- /dev/null
++++ b/layout/style/test/test_bug1443344-2.html
+@@ -0,0 +1,48 @@
++<!DOCTYPE HTML>
++<html>
++<!--
++https://bugzilla.mozilla.org/show_bug.cgi?id=1443344
++-->
++<head>
++  <meta charset="utf-8">
++  <title>Test for Bug 1443344</title>
++  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
++  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
++  <script type="application/javascript">
++
++  /** Test for Bug 1443344 **/
++  SimpleTest.waitForExplicitFinish();
++
++  var sheetURL = new URL("file_bug1443344.css", location.href);
++  sheetURL.protocol = "http";
++  var link = document.createElement("link");
++  link.href = `data:text/css,@import url("data:text/css,@import url('${sheetURL}');");`
++  link.rel = "stylesheet";
++  var loadFired = false, errorFired = false;
++  link.onload = () => loadFired = true;
++  link.onerror = () => errorFired = true;
++  document.head.appendChild(link);
++
++  addLoadEvent(() => {
++    is(loadFired, false, "Should not fire onload for erroring @import");
++    is(errorFired, true, "Should fire onerror for erroring @import");
++    is(getComputedStyle($("importTarget")).color, "rgb(0, 255, 0)",
++       "Erroring sheet should not load");
++    SimpleTest.finish();
++  });
++
++  </script>
++  <style>
++    #importTarget { color: rgb(0, 255, 0); }
++  </style>
++</head>
++<body>
++<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1443344">Mozilla Bug 1443344</a>
++<p id="display"><div id="importTarget"></div></p>
++<div id="content" style="display: none">
++
++</div>
++<pre id="test">
++</pre>
++</body>
++</html>

+ 86 - 0
frg/work-js/mozilla-release/patches/1443393-60a1.patch

@@ -0,0 +1,86 @@
+# HG changeset patch
+# User Boris Zbarsky <bzbarsky@mit.edu>
+# Date 1520365527 18000
+# Node ID 6a0f29000b2920674c97df88987067bd932b0473
+# Parent  791a7698068672f6e127e7f356bd3f301b4acb01
+Bug 1443393.  Stop trying to import light-theme.css from URLs that don't return anything useful.  r=jdescottes
+
+MozReview-Commit-ID: BZrZMHWGN3X
+
+diff --git a/devtools/client/netmonitor/src/assets/styles/httpi.css b/devtools/client/netmonitor/src/assets/styles/httpi.css
+--- a/devtools/client/netmonitor/src/assets/styles/httpi.css
++++ b/devtools/client/netmonitor/src/assets/styles/httpi.css
+@@ -1,14 +1,13 @@
+ /* 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/. */
+ 
+ @import "chrome://devtools/skin/widgets.css";
+-@import "resource://devtools/client/themes/light-theme.css";
+ @import "resource://devtools/client/shared/components/splitter/SplitBox.css";
+ @import "resource://devtools/client/shared/components/tree/TreeView.css";
+ @import "resource://devtools/client/shared/components/tabs/Tabs.css";
+ @import "resource://devtools/client/shared/components/tabs/TabBar.css";
+ @import "chrome://devtools/skin/components-frame.css";
+ @import "chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css";
+ @import "chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css";
+ @import "chrome://devtools/content/sourceeditor/codemirror/mozilla.css";
+diff --git a/devtools/client/netmonitor/src/assets/styles/netmonitor.css b/devtools/client/netmonitor/src/assets/styles/netmonitor.css
+--- a/devtools/client/netmonitor/src/assets/styles/netmonitor.css
++++ b/devtools/client/netmonitor/src/assets/styles/netmonitor.css
+@@ -1,13 +1,12 @@
+ /* 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/. */
+ 
+-@import "resource://devtools/client/themes/light-theme.css";
+ @import "resource://devtools/client/shared/components/splitter/SplitBox.css";
+ @import "resource://devtools/client/shared/components/tree/TreeView.css";
+ @import "resource://devtools/client/shared/components/tabs/Tabs.css";
+ @import "resource://devtools/client/shared/components/tabs/TabBar.css";
+ @import "chrome://devtools/skin/components-frame.css";
+ @import "chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css";
+ @import "chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css";
+ @import "chrome://devtools/content/sourceeditor/codemirror/mozilla.css";
+diff --git a/devtools/client/shared/components/test/mochitest/test_tabs_menu.html b/devtools/client/shared/components/test/mochitest/test_tabs_menu.html
+--- a/devtools/client/shared/components/test/mochitest/test_tabs_menu.html
++++ b/devtools/client/shared/components/test/mochitest/test_tabs_menu.html
+@@ -8,17 +8,16 @@ Test all-tabs menu.
+ -->
+ <head>
+   <meta charset="utf-8">
+   <title>Tabs component All-tabs menu test</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">
+   <link rel="stylesheet" type="text/css" href="resource://devtools/client/themes/variables.css">
+   <link rel="stylesheet" type="text/css" href="resource://devtools/client/themes/common.css">
+-  <link rel="stylesheet" type="text/css" href="resource://devtools/client/themes/light-theme.css">
+   <link rel="stylesheet" type="text/css" href="resource://devtools/client/shared/components/tabs/Tabs.css">
+   <link rel="stylesheet" type="text/css" href="resource://devtools/client/shared/components/tabs/TabBar.css">
+   <link rel="stylesheet" type="text/css" href="resource://devtools/client/inspector/components/side-panel.css">
+   <link rel="stylesheet" type="text/css" href="resource://devtools/client/inspector/components/InspectorTabPanel.css">
+ </head>
+ <body>
+ <pre id="test">
+ <script src="head.js" type="application/javascript"></script>
+diff --git a/devtools/client/webconsole/webconsole.html b/devtools/client/webconsole/webconsole.html
+--- a/devtools/client/webconsole/webconsole.html
++++ b/devtools/client/webconsole/webconsole.html
+@@ -1,17 +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/. -->
+ <!DOCTYPE html>
+ <html dir="">
+ <head>
+   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+     <link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
+-    <link rel="stylesheet" href="resource://devtools/client/themes/light-theme.css"/>
+     <link rel="stylesheet" href="chrome://devtools/skin/webconsole.css"/>
+     <link rel="stylesheet" href="chrome://devtools/skin/components-frame.css"/>
+     <link rel="stylesheet" href="resource://devtools/client/shared/components/reps/reps.css"/>
+     <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/Tabs.css"/>
+     <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/TabBar.css"/>
+     <link rel="stylesheet" href="resource://devtools/client/shared/components/NotificationBox.css"/>
+     <link rel="stylesheet" href="chrome://devtools/content/netmonitor/src/assets/styles/httpi.css"/>
+ 

+ 230 - 0
frg/work-js/mozilla-release/patches/1444073-60a1.patch

@@ -0,0 +1,230 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1520517972 -3600
+# Node ID a5579339a6ddfe26b7e2572b3919997573ee5c19
+# Parent  cef61df35a1fa1fe50e9f4441d104e30ec8d5caf
+Bug 1444073 - Remove old-event-emitter usage from webAudioEditor; r=pbro.
+
+MozReview-Commit-ID: C9EozxGTRpa
+
+diff --git a/devtools/client/webaudioeditor/includes.js b/devtools/client/webaudioeditor/includes.js
+--- a/devtools/client/webaudioeditor/includes.js
++++ b/devtools/client/webaudioeditor/includes.js
+@@ -1,17 +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/. */
+ "use strict";
+ 
+ const { loader, require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+ const { Task } = require("devtools/shared/task");
+-const OldEventEmitter = require("devtools/shared/old-event-emitter");
+ const EventEmitter = require("devtools/shared/event-emitter");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ const Services = require("Services");
+ const { gDevTools } = require("devtools/client/framework/devtools");
+ const { LocalizationHelper } = require("devtools/shared/l10n");
+ const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");
+ 
+ // Use privileged promise in panel documents to prevent having them to freeze
+@@ -76,17 +75,17 @@ XPCOMUtils.defineConstant(this, "EVENTS"
+ /**
+  * The current target and the Web Audio Editor front, set by this tool's host.
+  */
+ var gToolbox, gTarget, gFront;
+ 
+ /**
+  * Convenient way of emitting events from the panel window.
+  */
+-OldEventEmitter.decorate(this);
++EventEmitter.decorate(this);
+ 
+ /**
+  * DOM query helper.
+  */
+ function $(selector, target = document) { return target.querySelector(selector); }
+ function $$(selector, target = document) { return target.querySelectorAll(selector); }
+ 
+ /**
+diff --git a/devtools/client/webaudioeditor/panel.js b/devtools/client/webaudioeditor/panel.js
+--- a/devtools/client/webaudioeditor/panel.js
++++ b/devtools/client/webaudioeditor/panel.js
+@@ -1,17 +1,17 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ft=javascript ts=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/. */
+ "use strict";
+ 
+ const { Cc, Ci, Cu, Cr } = require("chrome");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const { WebAudioFront } = require("devtools/shared/fronts/webaudio");
+ 
+ function WebAudioEditorPanel(iframeWindow, toolbox) {
+   this.panelWin = iframeWindow;
+   this._toolbox = toolbox;
+   this._destroyer = null;
+ 
+   EventEmitter.decorate(this);
+diff --git a/devtools/client/webaudioeditor/test/head.js b/devtools/client/webaudioeditor/test/head.js
+--- a/devtools/client/webaudioeditor/test/head.js
++++ b/devtools/client/webaudioeditor/test/head.js
+@@ -146,17 +146,17 @@ function getNSpread(front, eventName, co
+  * resolves when the graph was rendered with the correct count of
+  * nodes and edges.
+  */
+ function waitForGraphRendered(front, nodeCount, edgeCount, paramEdgeCount) {
+   let eventName = front.EVENTS.UI_GRAPH_RENDERED;
+   info(`Wait for graph rendered with ${nodeCount} nodes, ${edgeCount} edges`);
+ 
+   return new Promise((resolve, reject) => {
+-    front.on(eventName, function onGraphRendered(_, nodes, edges, pEdges) {
++    front.on(eventName, function onGraphRendered(nodes, edges, pEdges) {
+       let paramEdgesDone = paramEdgeCount != null ? paramEdgeCount === pEdges : true;
+       info(`Got graph rendered with ${nodes} / ${nodeCount} nodes, ` +
+            `${edges} / ${edgeCount} edges`);
+       if (nodes === nodeCount && edges === edgeCount && paramEdgesDone) {
+         front.off(eventName, onGraphRendered);
+         resolve();
+       }
+     });
+@@ -202,42 +202,42 @@ function checkVariableView(view, index, 
+ }
+ 
+ function modifyVariableView(win, view, index, prop, value) {
+   let scope = view.getScopeAtIndex(index);
+   let aVar = scope.get(prop);
+   scope.expand();
+ 
+   return new Promise((resolve, reject) => {
+-    win.on(win.EVENTS.UI_SET_PARAM, handleSetting);
+-    win.on(win.EVENTS.UI_SET_PARAM_ERROR, handleSetting);
++    const onParamSetSuccess = () => {
++      win.off(win.EVENTS.UI_SET_PARAM_ERROR, onParamSetError);
++      resolve();
++    }
++
++    const onParamSetError = () => {
++      win.off(win.EVENTS.UI_SET_PARAM, onParamSetSuccess);
++      reject();
++    }
++    win.once(win.EVENTS.UI_SET_PARAM, onParamSetSuccess);
++    win.once(win.EVENTS.UI_SET_PARAM_ERROR, onParamSetError);
+ 
+     // Focus and select the variable to begin editing
+     win.focus();
+     aVar.focus();
+     EventUtils.sendKey("RETURN", win);
+ 
+     // Must wait for the scope DOM to be available to receive
+     // events
+     executeSoon(() => {
+       info("Setting " + value + " for " + prop + "....");
+       for (let c of (value + "")) {
+         EventUtils.synthesizeKey(c, {}, win);
+       }
+       EventUtils.sendKey("RETURN", win);
+     });
+-
+-    function handleSetting(eventName) {
+-      win.off(win.EVENTS.UI_SET_PARAM, handleSetting);
+-      win.off(win.EVENTS.UI_SET_PARAM_ERROR, handleSetting);
+-      if (eventName === win.EVENTS.UI_SET_PARAM)
+-        resolve();
+-      if (eventName === win.EVENTS.UI_SET_PARAM_ERROR)
+-        reject();
+-    }
+   });
+ }
+ 
+ function findGraphEdge(win, source, target, param) {
+   let selector = ".edgePaths .edgePath[data-source='" + source + "'][data-target='" + target + "']";
+   if (param) {
+     selector += "[data-param='" + param + "']";
+   }
+diff --git a/devtools/client/webaudioeditor/views/automation.js b/devtools/client/webaudioeditor/views/automation.js
+--- a/devtools/client/webaudioeditor/views/automation.js
++++ b/devtools/client/webaudioeditor/views/automation.js
+@@ -148,12 +148,12 @@ var AutomationView = {
+    */
+   _onResize: function () {
+     this.graph.refresh();
+   },
+ 
+   /**
+    * Called when the inspector view determines a node is selected.
+    */
+-  _onNodeSet: function (_, id) {
++  _onNodeSet: function (id) {
+     this._setAudioNode(id != null ? gAudioNodes.get(id) : null);
+   }
+ };
+diff --git a/devtools/client/webaudioeditor/views/context.js b/devtools/client/webaudioeditor/views/context.js
+--- a/devtools/client/webaudioeditor/views/context.js
++++ b/devtools/client/webaudioeditor/views/context.js
+@@ -280,17 +280,17 @@ var ContextView = {
+     if (~GRAPH_REDRAW_EVENTS.indexOf(eventName)) {
+       this.draw();
+     }
+   },
+ 
+   /**
+    * Fired when the devtools theme changes.
+    */
+-  _onThemeChange: function (eventName, theme) {
++  _onThemeChange: function (theme) {
+     let markerColor = MARKER_STYLING[theme];
+     let marker = $("#arrowhead");
+     if (marker) {
+       marker.setAttribute("style", "fill: " + markerColor);
+     }
+   },
+ 
+   /**
+diff --git a/devtools/client/webaudioeditor/views/inspector.js b/devtools/client/webaudioeditor/views/inspector.js
+--- a/devtools/client/webaudioeditor/views/inspector.js
++++ b/devtools/client/webaudioeditor/views/inspector.js
+@@ -136,17 +136,17 @@ var InspectorView = {
+   /**
+    * Event handlers
+    */
+ 
+   /**
+    * Called on EVENTS.UI_SELECT_NODE, and takes an actorID `id`
+    * and calls `setCurrentAudioNode` to scaffold the inspector view.
+    */
+-  _onNodeSelect: function (_, id) {
++  _onNodeSelect: function (id) {
+     this.setCurrentAudioNode(gAudioNodes.get(id));
+ 
+     // Ensure inspector is visible when selecting a new node
+     this.show();
+   },
+ 
+   _onResize: function () {
+     if (this.el.getAttribute("width") < MIN_INSPECTOR_WIDTH) {
+diff --git a/devtools/client/webaudioeditor/views/properties.js b/devtools/client/webaudioeditor/views/properties.js
+--- a/devtools/client/webaudioeditor/views/properties.js
++++ b/devtools/client/webaudioeditor/views/properties.js
+@@ -114,17 +114,17 @@ var PropertiesView = {
+ 
+   /**
+    * Event handlers
+    */
+ 
+   /**
+    * Called when the inspector view determines a node is selected.
+    */
+-  _onNodeSet: function (_, id) {
++  _onNodeSet: function (id) {
+     this._setAudioNode(gAudioNodes.get(id));
+   },
+ 
+   /**
+    * Executed when an audio prop is changed in the UI.
+    */
+   _onEval: Task.async(function* (variable, value) {
+     let ownerScope = variable.ownerView;

+ 2 - 2
frg/work-js/mozilla-release/patches/1445496-61a1.patch

@@ -2,7 +2,7 @@
 # User Jason Laster <jason.laster.11@gmail.com>
 # User Jason Laster <jason.laster.11@gmail.com>
 # Date 1520980142 14400
 # Date 1520980142 14400
 # Node ID 97eff06108acba6c8d597294474421d58b3cb575
 # Node ID 97eff06108acba6c8d597294474421d58b3cb575
-# Parent  5d10218d99cdfb2b91feb4544537a0d028db85cb
+# Parent  f90214d3058c46890dcc5defac8c6c426b6b4879
 Bug 1445496 - Update Debugger Frontend v22. r=jdescottes
 Bug 1445496 - Update Debugger Frontend v22. r=jdescottes
 
 
 MozReview-Commit-ID: LjmcFwfWPP
 MozReview-Commit-ID: LjmcFwfWPP
@@ -55383,13 +55383,13 @@ diff --git a/devtools/client/debugger/new/test/mochitest/head.js b/devtools/clie
  );
  );
 +
 +
  var { Toolbox } = require("devtools/client/framework/toolbox");
  var { Toolbox } = require("devtools/client/framework/toolbox");
+ var { Task } = require("devtools/shared/task");
  const sourceUtils = {
  const sourceUtils = {
    isLoaded: source => source.get("loadedState") === "loaded"
    isLoaded: source => source.get("loadedState") === "loaded"
  };
  };
  
  
  const EXAMPLE_URL =
  const EXAMPLE_URL =
    "http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/";
    "http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/";
- 
 diff --git a/devtools/client/locales/en-US/debugger.properties b/devtools/client/locales/en-US/debugger.properties
 diff --git a/devtools/client/locales/en-US/debugger.properties b/devtools/client/locales/en-US/debugger.properties
 --- a/devtools/client/locales/en-US/debugger.properties
 --- a/devtools/client/locales/en-US/debugger.properties
 +++ b/devtools/client/locales/en-US/debugger.properties
 +++ b/devtools/client/locales/en-US/debugger.properties

+ 32 - 0
frg/work-js/mozilla-release/patches/1448194-61a1.patch

@@ -0,0 +1,32 @@
+# HG changeset patch
+# User Nicolas B. Pierron <nicolas.b.pierron@gmail.com>
+# Date 1522854488 0
+# Node ID be6e3454d8dde110a2f5af54efed096ef6f2c661
+# Parent  af78c390b5d2b9d114093836b9c811249211ceda
+Bug 1448194 - Substract the mBytecodeOffset from the bytecode length, as this could cause some assertion failures. r=mrbkap
+
+
+diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
+--- a/dom/script/ScriptLoader.cpp
++++ b/dom/script/ScriptLoader.cpp
+@@ -1795,17 +1795,18 @@ ScriptLoader::AttemptAsyncScriptCompile(
+     return rv;
+   }
+ 
+   if (aRequest->IsSource()) {
+     if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptText.length())) {
+       return NS_ERROR_FAILURE;
+     }
+   } else {
+-    if (!JS::CanDecodeOffThread(cx, options, aRequest->mScriptBytecode.length())) {
++    size_t length = aRequest->mScriptBytecode.length() - aRequest->mBytecodeOffset;
++    if (!JS::CanDecodeOffThread(cx, options, length)) {
+       return NS_ERROR_FAILURE;
+     }
+   }
+ 
+   RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable =
+     new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
+ 
+   if (aRequest->IsModuleRequest()) {
+

+ 132 - 0
frg/work-js/mozilla-release/patches/1711872-91a1.patch

@@ -0,0 +1,132 @@
+# HG changeset patch
+# User Yulia Startsev <ystartsev@mozilla.com>
+# Date 1623167189 0
+# Node ID 9c839a5373102ca45f10f96ceec060ba84dff49b
+# Parent  5b24f112003bcb87acc4fec06f354c6edb15e04c
+Bug 1711872 - Implement Object.hasOwn proposal; r=evilpie
+
+Differential Revision: https://phabricator.services.mozilla.com/D115483
+
+diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp
+--- a/js/src/builtin/Object.cpp
++++ b/js/src/builtin/Object.cpp
+@@ -1971,16 +1971,20 @@ static const JSFunctionSpec object_stati
+     JS_FN("getOwnPropertySymbols",     obj_getOwnPropertySymbols,   1, 0),
+     JS_SELF_HOSTED_FN("isExtensible",  "ObjectIsExtensible",        1, 0),
+     JS_FN("preventExtensions",         obj_preventExtensions,       1, 0),
+     JS_FN("freeze",                    obj_freeze,                  1, 0),
+     JS_FN("isFrozen",                  obj_isFrozen,                1, 0),
+     JS_FN("seal",                      obj_seal,                    1, 0),
+     JS_FN("isSealed",                  obj_isSealed,                1, 0),
+     JS_SELF_HOSTED_FN("fromEntries",   "ObjectFromEntries",         1, 0),
++/* Proposal */
++#ifdef NIGHTLY_BUILD
++    JS_SELF_HOSTED_FN("hasOwn", "ObjectHasOwn", 2, 0),
++#endif
+     JS_FS_END
+ };
+ 
+ static JSObject*
+ CreateObjectConstructor(JSContext* cx, JSProtoKey key)
+ {
+     Rooted<GlobalObject*> self(cx, cx->global());
+     if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
+diff --git a/js/src/builtin/Object.js b/js/src/builtin/Object.js
+--- a/js/src/builtin/Object.js
++++ b/js/src/builtin/Object.js
+@@ -301,8 +301,17 @@ function ObjectFromEntries(iter) {
+         if (!IsObject(pair))
+             ThrowTypeError(JSMSG_INVALID_MAP_ITERABLE, "Object.fromEntries");
+         _DefineDataProperty(obj, pair[0], pair[1]);
+     }
+ 
+     return obj;
+ }
+ 
++// Proposal https://github.com/tc39/proposal-accessible-object-hasownproperty
++// 1. Object.hasOwn ( O, P )
++function ObjectHasOwn(O, P) {
++    // Step 1.
++    var obj = ToObject(O);
++    // Step 2-3.
++    return hasOwn(P, obj);
++}
++
+diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list
+--- a/js/src/tests/jstests.list
++++ b/js/src/tests/jstests.list
+@@ -41,17 +41,16 @@ skip-if(!this.hasOwnProperty("Intl")) in
+ # Skip built-ins/Simd tests when SIMD isn't available.
+ skip-if(!this.hasOwnProperty("SIMD")) include test262/built-ins/Simd/jstests.list
+ 
+ # https://bugzilla.mozilla.org/show_bug.cgi?id=1415303
+ skip-if(!this.hasOwnProperty("SharedArrayBuffer")) script non262/SIMD/load-sab-buffer-compat.js
+ skip-if(!this.hasOwnProperty("Atomics")) include test262/built-ins/Atomics/jstests.list
+ skip-if(!this.hasOwnProperty("SharedArrayBuffer")) include test262/built-ins/SharedArrayBuffer/jstests.list
+ 
+-
+ #####################################
+ # Test262 tests disabled on browser #
+ #####################################
+ 
+ # Defines a non-configurable property on the WindowProxy object.
+ skip-if(!xulRuntime.shell) script test262/annexB/language/eval-code/direct/global-block-decl-eval-global-existing-global-update.js
+ skip-if(!xulRuntime.shell) script test262/annexB/language/eval-code/direct/global-if-decl-else-decl-a-eval-global-existing-global-update.js
+ skip-if(!xulRuntime.shell) script test262/annexB/language/eval-code/direct/global-if-decl-else-decl-b-eval-global-existing-global-update.js
+@@ -488,8 +487,9 @@ random script test262/built-ins/Atomics/
+ random script test262/built-ins/Atomics/wake/wake-all.js
+ random script test262/built-ins/Atomics/wake/wake-in-order.js
+ 
+ # zh-Hans-CN and zh are supported by ICU, but the test requires that ICU also
+ # explicitly supports zh-Hans. That isn't the case and it rather looks like the
+ # shouldn't require that zh-Hans is explicitly supported without a fallback to
+ # zh.
+ skip script test262/intl402/fallback-locales-are-supported.js
++
+diff --git a/js/src/tests/non262/object/hasOwn.js b/js/src/tests/non262/object/hasOwn.js
+new file mode 100644
+--- /dev/null
++++ b/js/src/tests/non262/object/hasOwn.js
+@@ -0,0 +1,19 @@
++// |reftest| skip-if(!Object.hasOwn)
++/*
++ * Any copyright is dedicated to the Public Domain.
++ * https://creativecommons.org/publicdomain/zero/1.0/
++ */
++
++assertEq(Object.hasOwn({}, "any"), false);
++assertThrowsInstanceOf(() => Object.hasOwn(null, "any"), TypeError);
++
++var x = { test: 'test value'}
++var y = {}
++var z = Object.create(x);
++
++assertEq(Object.hasOwn(x, "test"), true);
++assertEq(Object.hasOwn(y, "test"), false);
++assertEq(Object.hasOwn(z, "test"), false);
++
++if (typeof reportCompare === "function")
++    reportCompare(true, true);
+diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul
+--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
++++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
+@@ -191,16 +191,19 @@ https://bugzilla.mozilla.org/show_bug.cg
+      "__proto__"];
+   gConstructorProperties['Object'] =
+     constructorProps(["setPrototypeOf", "getOwnPropertyDescriptor", "getOwnPropertyDescriptors",
+                       "keys", "is", "defineProperty", "defineProperties", "create",
+                       "getOwnPropertyNames", "getOwnPropertySymbols",
+                       "preventExtensions", "freeze", "fromEntries", "isFrozen", "seal",
+                       "isSealed", "assign", "getPrototypeOf", "values",
+                       "entries", "isExtensible"]);
++  if (isNightlyBuild) {
++    gConstructorProperties['Object'].push("hasOwn");
++  }
+   gPrototypeProperties['Array'] =
+     ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
+       "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
+       "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
+       "findIndex", "copyWithin", "fill", Symbol.iterator, Symbol.unscopables, "entries", "keys",
+       "values", "constructor", "flat", "flatMap", "at"];
+   gConstructorProperties['Array'] =
+     constructorProps(["join", "reverse", "sort", "push", "pop", "shift",

+ 71 - 0
frg/work-js/mozilla-release/patches/1721149-92a1.patch

@@ -0,0 +1,71 @@
+# HG changeset patch
+# User Yulia Startsev <ystartsev@mozilla.com>
+# Date 1626773813 0
+# Node ID 2c60fcbcb24633ecb03784d04eaf4846ec694484
+# Parent  e051800ea11d73377754f363a061828d7081e3ca
+Bug 1721149 - Ship hasOwn; r=evilpie
+
+Differential Revision: https://phabricator.services.mozilla.com/D120212
+
+diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp
+--- a/js/src/builtin/Object.cpp
++++ b/js/src/builtin/Object.cpp
+@@ -1971,20 +1971,17 @@ static const JSFunctionSpec object_stati
+     JS_FN("getOwnPropertySymbols",     obj_getOwnPropertySymbols,   1, 0),
+     JS_SELF_HOSTED_FN("isExtensible",  "ObjectIsExtensible",        1, 0),
+     JS_FN("preventExtensions",         obj_preventExtensions,       1, 0),
+     JS_FN("freeze",                    obj_freeze,                  1, 0),
+     JS_FN("isFrozen",                  obj_isFrozen,                1, 0),
+     JS_FN("seal",                      obj_seal,                    1, 0),
+     JS_FN("isSealed",                  obj_isSealed,                1, 0),
+     JS_SELF_HOSTED_FN("fromEntries",   "ObjectFromEntries",         1, 0),
+-/* Proposal */
+-#ifdef NIGHTLY_BUILD
+     JS_SELF_HOSTED_FN("hasOwn", "ObjectHasOwn", 2, 0),
+-#endif
+     JS_FS_END
+ };
+ 
+ static JSObject*
+ CreateObjectConstructor(JSContext* cx, JSProtoKey key)
+ {
+     Rooted<GlobalObject*> self(cx, cx->global());
+     if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
+diff --git a/js/src/tests/non262/object/hasOwn.js b/js/src/tests/non262/object/hasOwn.js
+--- a/js/src/tests/non262/object/hasOwn.js
++++ b/js/src/tests/non262/object/hasOwn.js
+@@ -1,9 +1,8 @@
+-// |reftest| skip-if(!Object.hasOwn)
+ /*
+  * Any copyright is dedicated to the Public Domain.
+  * https://creativecommons.org/publicdomain/zero/1.0/
+  */
+ 
+ assertEq(Object.hasOwn({}, "any"), false);
+ assertThrowsInstanceOf(() => Object.hasOwn(null, "any"), TypeError);
+ 
+diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul
+--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
++++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
+@@ -190,20 +190,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+      "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__",
+      "__proto__"];
+   gConstructorProperties['Object'] =
+     constructorProps(["setPrototypeOf", "getOwnPropertyDescriptor", "getOwnPropertyDescriptors",
+                       "keys", "is", "defineProperty", "defineProperties", "create",
+                       "getOwnPropertyNames", "getOwnPropertySymbols",
+                       "preventExtensions", "freeze", "fromEntries", "isFrozen", "seal",
+                       "isSealed", "assign", "getPrototypeOf", "values",
+-                      "entries", "isExtensible"]);
+-  if (isNightlyBuild) {
+-    gConstructorProperties['Object'].push("hasOwn");
+-  }
++                      "entries", "isExtensible", "hasOwn"]);
+   gPrototypeProperties['Array'] =
+     ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
+       "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
+       "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
+       "findIndex", "copyWithin", "fill", Symbol.iterator, Symbol.unscopables, "entries", "keys",
+       "values", "constructor", "flat", "flatMap", "at"];
+   gConstructorProperties['Array'] =
+     constructorProps(["join", "reverse", "sort", "push", "pop", "shift",

+ 0 - 67
frg/work-js/mozilla-release/patches/L-1415120-1-59a1.patch

@@ -1,67 +0,0 @@
-# HG changeset patch
-# User Junior Hsu <juhsu@mozilla.com>
-# Date 1510768980 -7200
-# Node ID 2125c1a2272fb3f5e8a6f6bdbc7dfe8c9a0f03a4
-# Parent  d05e6e23030699d61790187a23566e8f13298b36
-Bug 1415120 - Part1: no db access while rebuilding, r=nwgh
-
-diff --git a/netwerk/cookie/nsCookieService.cpp b/netwerk/cookie/nsCookieService.cpp
---- a/netwerk/cookie/nsCookieService.cpp
-+++ b/netwerk/cookie/nsCookieService.cpp
-@@ -1724,16 +1724,17 @@ nsCookieService::CloseDBStates()
-   if (mDefaultDBState->dbConn) {
-     // Asynchronously close the connection. We will null it below.
-     mDefaultDBState->dbConn->AsyncClose(mDefaultDBState->closeListener);
-   }
- 
-   CleanupDefaultDBConnection();
- 
-   mDefaultDBState = nullptr;
-+  mInitializedDBConn = false;
-   mInitializedDBStates = false;
- }
- 
- // Null out the statements.
- // This must be done before closing the connection.
- void
- nsCookieService::CleanupCachedStatements()
- {
-@@ -1760,18 +1761,16 @@ nsCookieService::CleanupDefaultDBConnect
- 
-   // Manually null out our listeners. This is necessary because they hold a
-   // strong ref to the DBState itself. They'll stay alive until whatever
-   // statements are still executing complete.
-   mDefaultDBState->insertListener = nullptr;
-   mDefaultDBState->updateListener = nullptr;
-   mDefaultDBState->removeListener = nullptr;
-   mDefaultDBState->closeListener = nullptr;
--
--  mInitializedDBConn = false;
- }
- 
- void
- nsCookieService::HandleDBClosed(DBState* aDBState)
- {
-   COOKIE_LOGSTRING(LogLevel::Debug,
-     ("HandleDBClosed(): DBState %p closed", aDBState));
- 
-@@ -1911,17 +1910,17 @@ nsCookieService::RebuildCorruptDB(DBStat
-             return;
-           }
- 
-           // Notify observers that we're beginning the rebuild.
-           if (os) {
-             os->NotifyObservers(nullptr, "cookie-db-rebuilding", nullptr);
-           }
- 
--          gCookieService->InitDBConn();
-+          gCookieService->InitDBConnInternal();
- 
-           // Enumerate the hash, and add cookies to the params array.
-           mozIStorageAsyncStatement* stmt = gCookieService->mDefaultDBState->stmtInsert;
-           nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
-           stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
-           for (auto iter = gCookieService->mDefaultDBState->hostTable.Iter();
-                !iter.Done();
-                iter.Next()) {
-

+ 0 - 54
frg/work-js/mozilla-release/patches/L-1415120-2-59a1.patch

@@ -1,54 +0,0 @@
-# HG changeset patch
-# User Junior Hsu <juhsu@mozilla.com>
-# Date 1510769040 -7200
-# Node ID 271e6768d94afbfe03acab303007bd289700ea36
-# Parent  2125c1a2272fb3f5e8a6f6bdbc7dfe8c9a0f03a4
-Bug 1415120 - Part2: crash test for cookie db rebuild, r=nwgh
-
-diff --git a/extensions/cookie/test/unit/test_cookies_async_failure.js b/extensions/cookie/test/unit/test_cookies_async_failure.js
---- a/extensions/cookie/test/unit/test_cookies_async_failure.js
-+++ b/extensions/cookie/test/unit/test_cookies_async_failure.js
-@@ -158,19 +158,39 @@ function* run_test_1(generator)
- 
-   // Attempt to insert a cookie with the same (name, host, path) triplet.
-   Services.cookiemgr.add(cookie.host, cookie.path, cookie.name, "hallo",
-     cookie.isSecure, cookie.isHttpOnly, cookie.isSession, cookie.expiry, {});
- 
-   // Check that the cookie service accepted the new cookie.
-   do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1);
- 
--  // Wait for the cookie service to rename the old database and rebuild.
--  new _observer(sub_generator, "cookie-db-rebuilding");
--  yield;
-+  let isRebuildingDone = false;
-+  let rebuildingObserve = function (subject, topic, data) {
-+    isRebuildingDone = true;
-+    Services.obs.removeObserver(rebuildingObserve, "cookie-db-rebuilding");
-+  };
-+  Services.obs.addObserver(rebuildingObserve, "cookie-db-rebuilding");
-+
-+  // Crash test: we're going to rebuild the cookie database. Close all the db
-+  // connections in the main thread and initialize a new database file in the
-+  // cookie thread. Trigger some access of cookies to ensure we won't crash in
-+  // the chaos status.
-+  for (let i = 0; i < 10; ++i) {
-+    do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1);
-+    do_execute_soon(function() { do_run_generator(sub_generator); });
-+    yield;
-+  }
-+
-+  // Wait for the cookie service to rename the old database and rebuild if not yet.
-+  if (!isRebuildingDone) {
-+    Services.obs.removeObserver(rebuildingObserve, "cookie-db-rebuilding");
-+    new _observer(sub_generator, "cookie-db-rebuilding");
-+    yield;
-+  }
-   do_execute_soon(function() { do_run_generator(sub_generator); });
-   yield;
- 
-   // At this point, the cookies should still be in memory.
-   do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 1);
-   do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1);
-   do_check_eq(do_count_cookies(), 2);
- 
-

+ 0 - 0
frg/work-js/mozilla-release/patches/NOBUG-20180205-jsgdb-60a1.patch → frg/work-js/mozilla-release/patches/NOBUG-20180206-jsgdb-60a1.patch


+ 6706 - 0
frg/work-js/mozilla-release/patches/NOBUG-nukemozlinker-25319.patch

@@ -0,0 +1,6706 @@
+# HG changeset patch
+# User Frank-Rainer Grahl <frgrahl@gmx.net>
+# Date 1705411363 -3600
+# Parent  c5b743a51a813ebe620be9d753854636b3592b01
+No Bug - Nuke custom linker. r=me a=me
+
+It was only needed for Android.
+
+diff --git a/browser/app/moz.build b/browser/app/moz.build
+--- a/browser/app/moz.build
++++ b/browser/app/moz.build
+@@ -97,19 +97,16 @@ if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_
+ # The heap will grow if need be.
+ #
+ # Set it to 256k.  See bug 127069.
+ if CONFIG['OS_ARCH'] == 'WINNT' and CONFIG['CC_TYPE'] not in ('clang', 'gcc'):
+     LDFLAGS += ['/HEAP:0x40000']
+ 
+ DisableStlWrapping()
+ 
+-if CONFIG['MOZ_LINKER']:
+-    OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
+-
+ if CONFIG['HAVE_CLOCK_MONOTONIC']:
+     OS_LIBS += CONFIG['REALTIME_LIBS']
+ 
+ if CONFIG['MOZ_LINUX_32_SSE2_STARTUP_ERROR']:
+     DEFINES['MOZ_LINUX_32_SSE2_STARTUP_ERROR'] = True
+     COMPILE_FLAGS['OS_CXXFLAGS'] = [
+         f for f in COMPILE_FLAGS.get('OS_CXXFLAGS', [])
+         if not f.startswith('-march=') and f not in ('-msse', '-msse2', '-mfpmath=sse')
+diff --git a/js/src/js-config.mozbuild b/js/src/js-config.mozbuild
+--- a/js/src/js-config.mozbuild
++++ b/js/src/js-config.mozbuild
+@@ -28,12 +28,8 @@ if CONFIG['MOZ_DEBUG'] or CONFIG['NIGHTL
+ # NOTE: This Realm creation options decide if this is exposed to script.
+ DEFINES['ENABLE_SHARED_ARRAY_BUFFER'] = True
+ 
+ # CTypes
+ if CONFIG['JS_HAS_CTYPES']:
+     DEFINES['JS_HAS_CTYPES'] = True
+     if not CONFIG['MOZ_SYSTEM_FFI']:
+         DEFINES['FFI_BUILDING'] = True
+-
+-# Forward MOZ_LINKER config
+-if CONFIG['MOZ_LINKER']:
+-    DEFINES['MOZ_LINKER'] = True
+diff --git a/js/src/old-configure.in b/js/src/old-configure.in
+--- a/js/src/old-configure.in
++++ b/js/src/old-configure.in
+@@ -1511,17 +1511,16 @@ AC_SUBST(TARGET_XPCOM_ABI)
+ 
+ dnl === -AC_SUBST(GCC_USE_GNU_LD) Bug 1478499 part 5 ===
+ 
+ AC_SUBST_LIST(DSO_CFLAGS)
+ AC_SUBST_LIST(DSO_PIC_CFLAGS)
+ AC_SUBST(DSO_LDOPTS)
+ AC_SUBST(BIN_SUFFIX)
+ AC_SUBST(USE_N32)
+-AC_SUBST(MOZ_LINKER)
+ AC_SUBST(WIN32_CONSOLE_EXE_LDFLAGS)
+ AC_SUBST(WIN32_GUI_EXE_LDFLAGS)
+ 
+ AC_CHECK_FUNCS(posix_fadvise posix_fallocate)
+ 
+ dnl Set various defines and substitutions
+ dnl ========================================================
+ 
+diff --git a/js/src/wasm/WasmSignalHandlers.cpp b/js/src/wasm/WasmSignalHandlers.cpp
+--- a/js/src/wasm/WasmSignalHandlers.cpp
++++ b/js/src/wasm/WasmSignalHandlers.cpp
+@@ -1373,42 +1373,32 @@ WasmFaultHandler(int signum, siginfo_t* 
+         previousSignal->sa_sigaction(signum, info, context);
+     else if (previousSignal->sa_handler == SIG_DFL || previousSignal->sa_handler == SIG_IGN)
+         sigaction(signum, previousSignal, nullptr);
+     else
+         previousSignal->sa_handler(signum);
+ }
+ # endif // XP_WIN || XP_DARWIN || assume unix
+ 
+-#if defined(ANDROID) && defined(MOZ_LINKER)
+-extern "C" MFBT_API bool IsSignalHandlingBroken();
+-#endif
+-
+ static bool sTriedInstallSignalHandlers = false;
+ static bool sHaveSignalHandlers = false;
+ 
+ static bool
+ ProcessHasSignalHandlers()
+ {
+     // We assume that there are no races creating the first JSRuntime of the process.
+     if (sTriedInstallSignalHandlers)
+         return sHaveSignalHandlers;
+     sTriedInstallSignalHandlers = true;
+ 
+ #if defined (JS_CODEGEN_NONE)
+     // If there is no JIT, then there should be no Wasm signal handlers.
+     return false;
+ #endif
+ 
+-#if defined(ANDROID) && defined(MOZ_LINKER)
+-    // Signal handling is broken on some android systems.
+-    if (IsSignalHandlingBroken())
+-        return false;
+-#endif
+-
+     // Initalize ThreadLocal flag used by WasmFaultHandler
+     sAlreadyInSignalHandler.infallibleInit();
+ 
+     // Install a SIGSEGV handler to handle safely-out-of-bounds asm.js heap
+     // access and/or unaligned accesses.
+ #if defined(XP_WIN)
+ # if defined(MOZ_ASAN)
+     // Under ASan we need to let the ASan runtime's ShadowExceptionHandler stay
+diff --git a/js/sub.configure b/js/sub.configure
+--- a/js/sub.configure
++++ b/js/sub.configure
+@@ -82,17 +82,17 @@ def js_subconfigure(host, target, build_
+     # Variables that were explicitly exported from old-configure, and those
+     # explicitly set in the environment when invoking old-configure, were
+     # automatically inherited from subconfigure. We assume the relevant ones
+     # have a corresponding AC_SUBST in old-configure, making them available
+     # in `substs`.
+     for var in (
+         'MOZ_SYSTEM_ZLIB', 'MOZ_ZLIB_CFLAGS', 'MOZ_ZLIB_LIBS',
+         'MOZ_APP_NAME', 'MOZ_APP_REMOTINGNAME', 'MOZ_DEV_EDITION',
+-        'STLPORT_LIBS', 'DIST', 'MOZ_LINKER', 'ZLIB_IN_MOZGLUE', 'RANLIB',
++        'STLPORT_LIBS', 'DIST', 'ZLIB_IN_MOZGLUE', 'RANLIB',
+         'AR', 'CPP', 'CC', 'CXX', 'CPPFLAGS', 'CFLAGS', 'CXXFLAGS',
+         'LDFLAGS', 'HOST_CC', 'HOST_CXX', 'HOST_CPPFLAGS',
+         'HOST_CXXFLAGS', 'HOST_LDFLAGS'
+     ):
+         if var not in from_assignment and var in substs:
+             value = substs[var]
+         elif var in assignments:
+             value = assignments[var]
+diff --git a/mozglue/build/moz.build b/mozglue/build/moz.build
+--- a/mozglue/build/moz.build
++++ b/mozglue/build/moz.build
+@@ -99,21 +99,16 @@ if CONFIG['MOZ_WIDGET_TOOLKIT']:
+             'arm.cpp',
+         ]
+ 
+     if CONFIG['CPU_ARCH'].startswith('mips'):
+         SOURCES += [
+             'mips.cpp',
+         ]
+ 
+-    if CONFIG['MOZ_LINKER']:
+-        USE_LIBS += [
+-            'zlib',
+-        ]
+-
+ USE_LIBS += [
+     'mfbt',
+ ]
+ 
+ DEFINES['IMPL_MFBT'] = True
+ LIBRARY_DEFINES['MOZ_HAS_MOZGLUE'] = True
+ 
+ if CONFIG['OS_TARGET'] == 'Darwin':
+@@ -121,16 +116,9 @@ if CONFIG['OS_TARGET'] == 'Darwin':
+     # symbol resolution for symbols that jemalloc itself uses. While it
+     # might be possible to find a way to avoid all such symbol resolutions,
+     # it's currently not possible because at the very least there's a call
+     # to pthread_self from tsd_init_check_recursion, which is necessary
+     # because somehow clang doesn't want to accept the __thread keyword
+     # for TLS.
+     LDFLAGS += ['-Wl,-bind_at_load']
+ 
+-if CONFIG['MOZ_LINKER'] and CONFIG['TARGET_CPU'] == 'arm':
+-    LDFLAGS += ['-Wl,-version-script,%s/arm-eabi-filter' % SRCDIR]
+-
+-# Bug 1477081
+-# -if CONFIG['MOZ_LINKER'] and CONFIG['TARGET_CPU'] == 'arm':
+-# +if CONFIG['MOZ_LINKER'] and CONFIG['CPU_ARCH'] == 'arm':
+-
+ DIST_INSTALL = True
+diff --git a/mozglue/linker/BaseElf.cpp b/mozglue/linker/BaseElf.cpp
+deleted file mode 100644
+--- a/mozglue/linker/BaseElf.cpp
++++ /dev/null
+@@ -1,216 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#include "BaseElf.h"
+-#include "Elfxx.h"
+-#include "Logging.h"
+-#include "mozilla/IntegerPrintfMacros.h"
+-#include "mozilla/RefPtr.h"
+-
+-using namespace Elf;
+-using namespace mozilla;
+-
+-unsigned long
+-BaseElf::Hash(const char *symbol)
+-{
+-  const unsigned char *sym = reinterpret_cast<const unsigned char *>(symbol);
+-  unsigned long h = 0, g;
+-  while (*sym) {
+-    h = (h << 4) + *sym++;
+-    g = h & 0xf0000000;
+-    h ^= g;
+-    h ^= g >> 24;
+-  }
+-  return h;
+-}
+-
+-void *
+-BaseElf::GetSymbolPtr(const char *symbol) const
+-{
+-  return GetSymbolPtr(symbol, Hash(symbol));
+-}
+-
+-void *
+-BaseElf::GetSymbolPtr(const char *symbol, unsigned long hash) const
+-{
+-  const Sym *sym = GetSymbol(symbol, hash);
+-  void *ptr = nullptr;
+-  if (sym && sym->st_shndx != SHN_UNDEF)
+-    ptr = GetPtr(sym->st_value);
+-  DEBUG_LOG("BaseElf::GetSymbolPtr(%p [\"%s\"], \"%s\") = %p",
+-            reinterpret_cast<const void *>(this), GetPath(), symbol, ptr);
+-  return ptr;
+-}
+-
+-const Sym *
+-BaseElf::GetSymbol(const char *symbol, unsigned long hash) const
+-{
+-  /* Search symbol with the buckets and chains tables.
+-   * The hash computed from the symbol name gives an index in the buckets
+-   * table. The corresponding value in the bucket table is an index in the
+-   * symbols table and in the chains table.
+-   * If the corresponding symbol in the symbols table matches, we're done.
+-   * Otherwise, the corresponding value in the chains table is a new index
+-   * in both tables, which corresponding symbol is tested and so on and so
+-   * forth */
+-  size_t bucket = hash % buckets.numElements();
+-  for (size_t y = buckets[bucket]; y != STN_UNDEF; y = chains[y]) {
+-    if (strcmp(symbol, strtab.GetStringAt(symtab[y].st_name)))
+-      continue;
+-    return &symtab[y];
+-  }
+-  return nullptr;
+-}
+-
+-bool
+-BaseElf::Contains(void *addr) const
+-{
+-  return base.Contains(addr);
+-}
+-
+-#ifdef __ARM_EABI__
+-const void *
+-BaseElf::FindExidx(int *pcount) const
+-{
+-  if (arm_exidx) {
+-    *pcount = arm_exidx.numElements();
+-    return arm_exidx;
+-  }
+-  *pcount = 0;
+-  return nullptr;
+-}
+-#endif
+-
+-already_AddRefed<LibHandle>
+-LoadedElf::Create(const char *path, void *base_addr)
+-{
+-  DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = ...", path, base_addr);
+-
+-  uint8_t mapped;
+-  /* If the page is not mapped, mincore returns an error. If base_addr is
+-   * nullptr, as would happen if the corresponding binary is prelinked with
+-   * the prelink look (but not with the android apriori tool), no page being
+-   * mapped there (right?), mincore returns an error, too, which makes
+-   * prelinked libraries on glibc unsupported. This is not an interesting
+-   * use case for now, so don't try supporting that case.
+-   */
+-  if (mincore(const_cast<void*>(base_addr), PageSize(), &mapped))
+-    return nullptr;
+-
+-  RefPtr<LoadedElf> elf = new LoadedElf(path);
+-
+-  const Ehdr *ehdr = Ehdr::validate(base_addr);
+-  if (!ehdr)
+-    return nullptr;
+-
+-  Addr min_vaddr = (Addr) -1; // We want to find the lowest and biggest
+-  Addr max_vaddr = 0;         // virtual address used by this Elf.
+-  const Phdr *dyn = nullptr;
+-#ifdef __ARM_EABI__
+-  const Phdr *arm_exidx_phdr = nullptr;
+-#endif
+-
+-  Array<Phdr> phdrs(reinterpret_cast<const char *>(ehdr) + ehdr->e_phoff,
+-                    ehdr->e_phnum);
+-  for (auto phdr = phdrs.begin(); phdr < phdrs.end(); ++phdr) {
+-    switch (phdr->p_type) {
+-      case PT_LOAD:
+-        if (phdr->p_vaddr < min_vaddr)
+-          min_vaddr = phdr->p_vaddr;
+-        if (max_vaddr < phdr->p_vaddr + phdr->p_memsz)
+-          max_vaddr = phdr->p_vaddr + phdr->p_memsz;
+-        break;
+-      case PT_DYNAMIC:
+-        dyn = &*phdr;
+-        break;
+-#ifdef __ARM_EABI__
+-      case PT_ARM_EXIDX:
+-        /* We cannot initialize arm_exidx here
+-           because we don't have a base yet */
+-        arm_exidx_phdr = &*phdr;
+-        break;
+-#endif
+-    }
+-  }
+-
+-  /* If the lowest PT_LOAD virtual address in headers is not 0, then the ELF
+-   * is either prelinked or a non-PIE executable. The former case is not
+-   * possible, because base_addr would be nullptr and the mincore test above
+-   * would already have made us return.
+-   * For a non-PIE executable, PT_LOADs contain absolute addresses, so in
+-   * practice, this means min_vaddr should be equal to base_addr. max_vaddr
+-   * can thus be adjusted accordingly.
+-   */
+-  if (min_vaddr != 0) {
+-    void *min_vaddr_ptr = reinterpret_cast<void *>(
+-      static_cast<uintptr_t>(min_vaddr));
+-    if (min_vaddr_ptr != base_addr) {
+-      LOG("%s: %p != %p", elf->GetPath(), min_vaddr_ptr, base_addr);
+-      return nullptr;
+-    }
+-    max_vaddr -= min_vaddr;
+-  }
+-  if (!dyn) {
+-    LOG("%s: No PT_DYNAMIC segment found", elf->GetPath());
+-    return nullptr;
+-  }
+-
+-  elf->base.Assign(base_addr, max_vaddr);
+-
+-  if (!elf->InitDyn(dyn))
+-    return nullptr;
+-
+-#ifdef __ARM_EABI__
+-  if (arm_exidx_phdr)
+-    elf->arm_exidx.InitSize(elf->GetPtr(arm_exidx_phdr->p_vaddr),
+-                            arm_exidx_phdr->p_memsz);
+-#endif
+-
+-  DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = %p", path, base_addr,
+-    static_cast<void *>(elf));
+-
+-  ElfLoader::Singleton.Register(elf);
+-  return elf.forget();
+-}
+-
+-bool
+-LoadedElf::InitDyn(const Phdr *pt_dyn)
+-{
+-  Array<Dyn> dyns;
+-  dyns.InitSize(GetPtr<Dyn>(pt_dyn->p_vaddr), pt_dyn->p_filesz);
+-
+-  size_t symnum = 0;
+-  for (auto dyn = dyns.begin(); dyn < dyns.end() && dyn->d_tag; ++dyn) {
+-    switch (dyn->d_tag) {
+-      case DT_HASH:
+-        {
+-          DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_HASH", uintptr_t(dyn->d_un.d_val));
+-          const Elf::Word *hash_table_header = \
+-            GetPtr<Elf::Word>(dyn->d_un.d_ptr);
+-          symnum = hash_table_header[1];
+-          buckets.Init(&hash_table_header[2], hash_table_header[0]);
+-          chains.Init(&*buckets.end());
+-        }
+-        break;
+-      case DT_STRTAB:
+-        DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_STRTAB", uintptr_t(dyn->d_un.d_val));
+-        strtab.Init(GetPtr(dyn->d_un.d_ptr));
+-        break;
+-      case DT_SYMTAB:
+-        DEBUG_LOG("%s 0x%08" PRIxPTR, "DT_SYMTAB", uintptr_t(dyn->d_un.d_val));
+-        symtab.Init(GetPtr(dyn->d_un.d_ptr));
+-        break;
+-    }
+-  }
+-  if (!buckets || !symnum) {
+-    ERROR("%s: Missing or broken DT_HASH", GetPath());
+-  } else if (!strtab) {
+-    ERROR("%s: Missing DT_STRTAB", GetPath());
+-  } else if (!symtab) {
+-    ERROR("%s: Missing DT_SYMTAB", GetPath());
+-  } else {
+-    return true;
+-  }
+-  return false;
+-}
+diff --git a/mozglue/linker/BaseElf.h b/mozglue/linker/BaseElf.h
+deleted file mode 100644
+--- a/mozglue/linker/BaseElf.h
++++ /dev/null
+@@ -1,141 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#ifndef BaseElf_h
+-#define BaseElf_h
+-
+-#include "ElfLoader.h"
+-#include "Elfxx.h"
+-
+-
+-/**
+- * Base class for ELF libraries. This class includes things that will be
+- * common between SystemElfs and CustomElfs.
+- */
+-class BaseElf: public LibHandle
+-{
+-public:
+-  /**
+-   * Hash function for symbol lookup, as defined in ELF standard for System V.
+-   */
+-  static unsigned long Hash(const char *symbol);
+-
+-  /**
+-   * Returns the address corresponding to the given symbol name (with a
+-   * pre-computed hash).
+-   */
+-  void *GetSymbolPtr(const char *symbol, unsigned long hash) const;
+-
+-  /**
+-   * Returns a pointer to the Elf Symbol in the Dynamic Symbol table
+-   * corresponding to the given symbol name (with a pre-computed hash).
+-   */
+-  const Elf::Sym *GetSymbol(const char *symbol, unsigned long hash) const;
+-
+-  BaseElf(const char *path, Mappable *mappable = nullptr)
+-  : LibHandle(path)
+-  , mappable(mappable)
+-  {
+-  }
+-
+-protected:
+-   /**
+-    * Inherited from LibHandle. Those are temporary and are not supposed to
+-    * be used.
+-    */
+-   virtual void *GetSymbolPtr(const char *symbol) const;
+-   virtual bool Contains(void *addr) const;
+-   virtual void *GetBase() const { return GetPtr(0); }
+-
+-#ifdef __ARM_EABI__
+-  virtual const void *FindExidx(int *pcount) const;
+-#endif
+-
+-  virtual Mappable *GetMappable() const { return NULL; };
+-
+-public:
+-/* private: */
+-  /**
+-   * Returns a pointer relative to the base address where the library is
+-   * loaded.
+-   */
+-  void *GetPtr(const Elf::Addr offset) const
+-  {
+-    if (reinterpret_cast<void *>(offset) > base)
+-      return reinterpret_cast<void *>(offset);
+-    return base + offset;
+-  }
+-
+-  /**
+-   * Like the above, but returns a typed (const) pointer
+-   */
+-  template <typename T>
+-  const T *GetPtr(const Elf::Addr offset) const
+-  {
+-    if (reinterpret_cast<void *>(offset) > base)
+-      return reinterpret_cast<const T *>(offset);
+-    return reinterpret_cast<const T *>(base + offset);
+-  }
+-
+-  /* Appropriated Mappable */
+-  /* /!\ we rely on this being nullptr for BaseElf instances, but not
+-   * CustomElf instances. */
+-  RefPtr<Mappable> mappable;
+-
+-  /* Base address where the library is loaded */
+-  MappedPtr base;
+-
+-  /* Buckets and chains for the System V symbol hash table */
+-  Array<Elf::Word> buckets;
+-  UnsizedArray<Elf::Word> chains;
+-
+-/* protected: */
+-  /* String table */
+-  Elf::Strtab strtab;
+-
+-  /* Symbol table */
+-  UnsizedArray<Elf::Sym> symtab;
+-
+-#ifdef __ARM_EABI__
+-  /* ARM.exidx information used by FindExidx */
+-  Array<uint32_t[2]> arm_exidx;
+-#endif
+-};
+-
+-
+-/**
+- * Class for ELF libraries that already loaded in memory.
+- */
+-class LoadedElf: public BaseElf
+-{
+-public:
+-  /**
+-   * Returns a LoadedElf corresponding to the already loaded ELF
+-   * at the given base address.
+-   */
+-  static already_AddRefed<LibHandle> Create(const char *path,
+-                                                 void *base_addr);
+-
+-private:
+-  LoadedElf(const char *path)
+-  : BaseElf(path) { }
+-
+-  ~LoadedElf()
+-  {
+-    /* Avoid base's destructor unmapping something that doesn't actually
+-     * belong to it. */
+-    base.release();
+-    ElfLoader::Singleton.Forget(this);
+-  }
+-
+-  /**
+-   * Initializes the library according to information found in the given
+-   * PT_DYNAMIC header.
+-   * Returns whether this succeeded or failed.
+-   */
+-  bool InitDyn(const Elf::Phdr *pt_dyn);
+-};
+-
+-
+-#endif /* BaseElf_h */
+diff --git a/mozglue/linker/CustomElf.cpp b/mozglue/linker/CustomElf.cpp
+deleted file mode 100644
+--- a/mozglue/linker/CustomElf.cpp
++++ /dev/null
+@@ -1,749 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#include <cstring>
+-#include <sys/mman.h>
+-#include <vector>
+-#include <dlfcn.h>
+-#include <signal.h>
+-#include <string.h>
+-#include "CustomElf.h"
+-#include "BaseElf.h"
+-#include "Mappable.h"
+-#include "Logging.h"
+-#include "mozilla/IntegerPrintfMacros.h"
+-
+-using namespace Elf;
+-using namespace mozilla;
+-
+-/* TODO: Fill ElfLoader::Singleton.lastError on errors. */
+-
+-/* Function used to report library mappings from the custom linker to Gecko
+- * crash reporter */
+-#ifdef ANDROID
+-extern "C" {
+-  void report_mapping(char *name, void *base, uint32_t len, uint32_t offset);
+-  void delete_mapping(const char *name);
+-}
+-#else
+-#define report_mapping(...)
+-#define delete_mapping(...)
+-#endif
+-
+-const Ehdr *Ehdr::validate(const void *buf)
+-{
+-  if (!buf || buf == MAP_FAILED)
+-    return nullptr;
+-
+-  const Ehdr *ehdr = reinterpret_cast<const Ehdr *>(buf);
+-
+-  /* Only support ELF executables or libraries for the host system */
+-  if (memcmp(ELFMAG, &ehdr->e_ident, SELFMAG) ||
+-      ehdr->e_ident[EI_CLASS] != ELFCLASS ||
+-      ehdr->e_ident[EI_DATA] != ELFDATA ||
+-      ehdr->e_ident[EI_VERSION] != 1 ||
+-      (ehdr->e_ident[EI_OSABI] != ELFOSABI && ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) ||
+-#ifdef EI_ABIVERSION
+-      ehdr->e_ident[EI_ABIVERSION] != ELFABIVERSION ||
+-#endif
+-      (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) ||
+-      ehdr->e_machine != ELFMACHINE ||
+-      ehdr->e_version != 1 ||
+-      ehdr->e_phentsize != sizeof(Phdr))
+-    return nullptr;
+-
+-  return ehdr;
+-}
+-
+-namespace {
+-
+-void debug_phdr(const char *type, const Phdr *phdr)
+-{
+-  DEBUG_LOG("%s @0x%08" PRIxPTR " ("
+-            "filesz: 0x%08" PRIxPTR ", "
+-            "memsz: 0x%08" PRIxPTR ", "
+-            "offset: 0x%08" PRIxPTR ", "
+-            "flags: %c%c%c)",
+-            type, uintptr_t(phdr->p_vaddr), uintptr_t(phdr->p_filesz),
+-            uintptr_t(phdr->p_memsz), uintptr_t(phdr->p_offset),
+-            phdr->p_flags & PF_R ? 'r' : '-',
+-            phdr->p_flags & PF_W ? 'w' : '-', phdr->p_flags & PF_X ? 'x' : '-');
+-}
+-
+-static int p_flags_to_mprot(Word flags)
+-{
+-  return ((flags & PF_X) ? PROT_EXEC : 0) |
+-         ((flags & PF_W) ? PROT_WRITE : 0) |
+-         ((flags & PF_R) ? PROT_READ : 0);
+-}
+-
+-} /* anonymous namespace */
+-
+-/**
+- * RAII wrapper for a mapping of the first page off a Mappable object.
+- * This calls Mappable::munmap instead of system munmap.
+- */
+-class Mappable1stPagePtr: public GenericMappedPtr<Mappable1stPagePtr> {
+-public:
+-  Mappable1stPagePtr(Mappable *mappable)
+-  : GenericMappedPtr<Mappable1stPagePtr>(
+-      mappable->mmap(nullptr, PageSize(), PROT_READ, MAP_PRIVATE, 0))
+-  , mappable(mappable)
+-  {
+-  }
+-
+-private:
+-  friend class GenericMappedPtr<Mappable1stPagePtr>;
+-  void munmap(void *buf, size_t length) {
+-    mappable->munmap(buf, length);
+-  }
+-
+-  RefPtr<Mappable> mappable;
+-};
+-
+-
+-already_AddRefed<LibHandle>
+-CustomElf::Load(Mappable *mappable, const char *path, int flags)
+-{
+-  DEBUG_LOG("CustomElf::Load(\"%s\", 0x%x) = ...", path, flags);
+-  if (!mappable)
+-    return nullptr;
+-  /* Keeping a RefPtr of the CustomElf is going to free the appropriate
+-   * resources when returning nullptr */
+-  RefPtr<CustomElf> elf = new CustomElf(mappable, path);
+-  /* Map the first page of the Elf object to access Elf and program headers */
+-  Mappable1stPagePtr ehdr_raw(mappable);
+-  if (ehdr_raw == MAP_FAILED)
+-    return nullptr;
+-
+-  const Ehdr *ehdr = Ehdr::validate(ehdr_raw);
+-  if (!ehdr)
+-    return nullptr;
+-
+-  /* Scan Elf Program Headers and gather some information about them */
+-  std::vector<const Phdr *> pt_loads;
+-  Addr min_vaddr = (Addr) -1; // We want to find the lowest and biggest
+-  Addr max_vaddr = 0;         // virtual address used by this Elf.
+-  const Phdr *dyn = nullptr;
+-
+-  const Phdr *first_phdr = reinterpret_cast<const Phdr *>(
+-                           reinterpret_cast<const char *>(ehdr) + ehdr->e_phoff);
+-  const Phdr *end_phdr = &first_phdr[ehdr->e_phnum];
+-#ifdef __ARM_EABI__
+-  const Phdr *arm_exidx_phdr = nullptr;
+-#endif
+-
+-  for (const Phdr *phdr = first_phdr; phdr < end_phdr; phdr++) {
+-    switch (phdr->p_type) {
+-      case PT_LOAD:
+-        debug_phdr("PT_LOAD", phdr);
+-        pt_loads.push_back(phdr);
+-        if (phdr->p_vaddr < min_vaddr)
+-          min_vaddr = phdr->p_vaddr;
+-        if (max_vaddr < phdr->p_vaddr + phdr->p_memsz)
+-          max_vaddr = phdr->p_vaddr + phdr->p_memsz;
+-        break;
+-      case PT_DYNAMIC:
+-        debug_phdr("PT_DYNAMIC", phdr);
+-        if (!dyn) {
+-          dyn = phdr;
+-        } else {
+-          ERROR("%s: Multiple PT_DYNAMIC segments detected", elf->GetPath());
+-          return nullptr;
+-        }
+-        break;
+-      case PT_TLS:
+-        debug_phdr("PT_TLS", phdr);
+-        if (phdr->p_memsz) {
+-          ERROR("%s: TLS is not supported", elf->GetPath());
+-          return nullptr;
+-        }
+-        break;
+-      case PT_GNU_STACK:
+-        debug_phdr("PT_GNU_STACK", phdr);
+-// Skip on Android until bug 706116 is fixed
+-#ifndef ANDROID
+-        if (phdr->p_flags & PF_X) {
+-          ERROR("%s: Executable stack is not supported", elf->GetPath());
+-          return nullptr;
+-        }
+-#endif
+-        break;
+-#ifdef __ARM_EABI__
+-      case PT_ARM_EXIDX:
+-        /* We cannot initialize arm_exidx here
+-           because we don't have a base yet */
+-        arm_exidx_phdr = phdr;
+-        break;
+-#endif
+-      default:
+-        DEBUG_LOG("%s: Program header type #%d not handled",
+-                  elf->GetPath(), phdr->p_type);
+-    }
+-  }
+-
+-  if (min_vaddr != 0) {
+-    ERROR("%s: Unsupported minimal virtual address: 0x%08" PRIxPTR,
+-        elf->GetPath(), uintptr_t(min_vaddr));
+-    return nullptr;
+-  }
+-  if (!dyn) {
+-    ERROR("%s: No PT_DYNAMIC segment found", elf->GetPath());
+-    return nullptr;
+-  }
+-
+-  /* Reserve enough memory to map the complete virtual address space for this
+-   * library.
+-   * As we are using the base address from here to mmap something else with
+-   * MAP_FIXED | MAP_SHARED, we need to make sure these mmaps will work. For
+-   * instance, on armv6, MAP_SHARED mappings require a 16k alignment, but mmap
+-   * MAP_PRIVATE only returns a 4k aligned address. So we first get a base
+-   * address with MAP_SHARED, which guarantees the kernel returns an address
+-   * that we'll be able to use with MAP_FIXED, and then remap MAP_PRIVATE at
+-   * the same address, because of some bad side effects of keeping it as
+-   * MAP_SHARED. */
+-  elf->base.Assign(MemoryRange::mmap(nullptr, max_vaddr, PROT_NONE,
+-                                     MAP_SHARED | MAP_ANONYMOUS, -1, 0));
+-  if ((elf->base == MAP_FAILED) ||
+-      (mmap(elf->base, max_vaddr, PROT_NONE,
+-            MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != elf->base)) {
+-    ERROR("%s: Failed to mmap", elf->GetPath());
+-    return nullptr;
+-  }
+-
+-  /* Load and initialize library */
+-  for (std::vector<const Phdr *>::iterator it = pt_loads.begin();
+-       it < pt_loads.end(); ++it)
+-    if (!elf->LoadSegment(*it))
+-      return nullptr;
+-
+-  /* We're not going to mmap anymore */
+-  mappable->finalize();
+-
+-  report_mapping(const_cast<char *>(elf->GetName()), elf->base,
+-                 (max_vaddr + PAGE_SIZE - 1) & PAGE_MASK, 0);
+-
+-  elf->l_addr = elf->base;
+-  elf->l_name = elf->GetPath();
+-  elf->l_ld = elf->GetPtr<Dyn>(dyn->p_vaddr);
+-  ElfLoader::Singleton.Register(elf);
+-
+-  if (!elf->InitDyn(dyn))
+-    return nullptr;
+-
+-  if (elf->has_text_relocs) {
+-    for (std::vector<const Phdr *>::iterator it = pt_loads.begin();
+-         it < pt_loads.end(); ++it)
+-      mprotect(PageAlignedPtr(elf->GetPtr((*it)->p_vaddr)),
+-               PageAlignedEndPtr((*it)->p_memsz),
+-               p_flags_to_mprot((*it)->p_flags) | PROT_WRITE);
+-  }
+-
+-  if (!elf->Relocate() || !elf->RelocateJumps())
+-    return nullptr;
+-
+-  if (elf->has_text_relocs) {
+-    for (std::vector<const Phdr *>::iterator it = pt_loads.begin();
+-         it < pt_loads.end(); ++it)
+-      mprotect(PageAlignedPtr(elf->GetPtr((*it)->p_vaddr)),
+-               PageAlignedEndPtr((*it)->p_memsz),
+-               p_flags_to_mprot((*it)->p_flags));
+-  }
+-
+-  if (!elf->CallInit())
+-    return nullptr;
+-
+-#ifdef __ARM_EABI__
+-  if (arm_exidx_phdr)
+-    elf->arm_exidx.InitSize(elf->GetPtr(arm_exidx_phdr->p_vaddr),
+-                            arm_exidx_phdr->p_memsz);
+-#endif
+-
+-  DEBUG_LOG("CustomElf::Load(\"%s\", 0x%x) = %p", path, flags,
+-            static_cast<void *>(elf));
+-  return elf.forget();
+-}
+-
+-CustomElf::~CustomElf()
+-{
+-  DEBUG_LOG("CustomElf::~CustomElf(%p [\"%s\"])",
+-            reinterpret_cast<void *>(this), GetPath());
+-  CallFini();
+-  /* Normally, __cxa_finalize is called by the .fini function. However,
+-   * Android NDK before r6b doesn't do that. Our wrapped cxa_finalize only
+-   * calls destructors once, so call it in all cases. */
+-  ElfLoader::__wrap_cxa_finalize(this);
+-  ElfLoader::Singleton.Forget(this);
+-  delete_mapping(GetName());
+-}
+-
+-void *
+-CustomElf::GetSymbolPtrInDeps(const char *symbol) const
+-{
+-  /* Resolve dlopen and related functions to point to ours */
+-  if (symbol[0] == 'd' && symbol[1] == 'l') {
+-    if (strcmp(symbol + 2, "open") == 0)
+-      return FunctionPtr(__wrap_dlopen);
+-    if (strcmp(symbol + 2, "error") == 0)
+-      return FunctionPtr(__wrap_dlerror);
+-    if (strcmp(symbol + 2, "close") == 0)
+-      return FunctionPtr(__wrap_dlclose);
+-    if (strcmp(symbol + 2, "sym") == 0)
+-      return FunctionPtr(__wrap_dlsym);
+-    if (strcmp(symbol + 2, "addr") == 0)
+-      return FunctionPtr(__wrap_dladdr);
+-    if (strcmp(symbol + 2, "_iterate_phdr") == 0)
+-      return FunctionPtr(__wrap_dl_iterate_phdr);
+-  } else if (symbol[0] == '_' && symbol[1] == '_') {
+-  /* Resolve a few C++ ABI specific functions to point to ours */
+-#ifdef __ARM_EABI__
+-    if (strcmp(symbol + 2, "aeabi_atexit") == 0)
+-      return FunctionPtr(&ElfLoader::__wrap_aeabi_atexit);
+-#else
+-    if (strcmp(symbol + 2, "cxa_atexit") == 0)
+-      return FunctionPtr(&ElfLoader::__wrap_cxa_atexit);
+-#endif
+-    if (strcmp(symbol + 2, "cxa_finalize") == 0)
+-      return FunctionPtr(&ElfLoader::__wrap_cxa_finalize);
+-    if (strcmp(symbol + 2, "dso_handle") == 0)
+-      return const_cast<CustomElf *>(this);
+-#ifdef __ARM_EABI__
+-    if (strcmp(symbol + 2, "gnu_Unwind_Find_exidx") == 0)
+-      return FunctionPtr(__wrap___gnu_Unwind_Find_exidx);
+-#endif
+-  } else if (symbol[0] == 's' && symbol[1] == 'i') {
+-    if (strcmp(symbol + 2, "gnal") == 0)
+-      return FunctionPtr(signal);
+-    if (strcmp(symbol + 2, "gaction") == 0)
+-      return FunctionPtr(sigaction);
+-  }
+-
+-  void *sym;
+-
+-  unsigned long hash = Hash(symbol);
+-
+-  /* self_elf should never be NULL, but better safe than sorry. */
+-  if (ElfLoader::Singleton.self_elf) {
+-    /* We consider the library containing this code a permanent LD_PRELOAD,
+-     * so, check if the symbol exists here first. */
+-    sym = static_cast<BaseElf *>(
+-      ElfLoader::Singleton.self_elf.get())->GetSymbolPtr(symbol, hash);
+-    if (sym)
+-      return sym;
+-  }
+-
+-  /* Then search the symbol in our dependencies. Since we already searched in
+-   * libraries the system linker loaded, skip those (on glibc systems). We
+-   * also assume the symbol is to be found in one of the dependent libraries
+-   * directly, not in their own dependent libraries. Building libraries with
+-   * --no-allow-shlib-undefined ensures such indirect symbol dependency don't
+-   * happen. */
+-  for (std::vector<RefPtr<LibHandle> >::const_iterator it = dependencies.begin();
+-       it < dependencies.end(); ++it) {
+-    /* Skip if it's the library containing this code, since we've already
+-     * looked at it above. */
+-    if (*it == ElfLoader::Singleton.self_elf)
+-      continue;
+-    if (BaseElf *be = (*it)->AsBaseElf()) {
+-      sym = be->GetSymbolPtr(symbol, hash);
+-    } else {
+-      sym = (*it)->GetSymbolPtr(symbol);
+-    }
+-    if (sym)
+-      return sym;
+-  }
+-  return nullptr;
+-}
+-
+-bool
+-CustomElf::LoadSegment(const Phdr *pt_load) const
+-{
+-  if (pt_load->p_type != PT_LOAD) {
+-    DEBUG_LOG("%s: Elf::LoadSegment only takes PT_LOAD program headers", GetPath());
+-    return false;;
+-  }
+-
+-  int prot = p_flags_to_mprot(pt_load->p_flags);
+-
+-  /* Mmap at page boundary */
+-  Addr align = PageSize();
+-  Addr align_offset;
+-  void *mapped, *where;
+-  do {
+-    align_offset = pt_load->p_vaddr - AlignedPtr(pt_load->p_vaddr, align);
+-    where = GetPtr(pt_load->p_vaddr - align_offset);
+-    DEBUG_LOG("%s: Loading segment @%p %c%c%c", GetPath(), where,
+-                                                prot & PROT_READ ? 'r' : '-',
+-                                                prot & PROT_WRITE ? 'w' : '-',
+-                                                prot & PROT_EXEC ? 'x' : '-');
+-    mapped = mappable->mmap(where, pt_load->p_filesz + align_offset,
+-                            prot, MAP_PRIVATE | MAP_FIXED,
+-                            pt_load->p_offset - align_offset);
+-    if ((mapped != MAP_FAILED) || (pt_load->p_vaddr == 0) ||
+-        (pt_load->p_align == align))
+-      break;
+-    /* The virtual address space for the library is properly aligned at
+-     * 16k on ARMv6 (see CustomElf::Load), and so is the first segment
+-     * (p_vaddr == 0). But subsequent segments may not be 16k aligned
+-     * and fail to mmap. In such case, try to mmap again at the p_align
+-     * boundary instead of page boundary. */
+-    DEBUG_LOG("%s: Failed to mmap, retrying", GetPath());
+-    align = pt_load->p_align;
+-  } while (1);
+-
+-  if (mapped != where) {
+-    if (mapped == MAP_FAILED) {
+-      ERROR("%s: Failed to mmap", GetPath());
+-    } else {
+-      ERROR("%s: Didn't map at the expected location (wanted: %p, got: %p)",
+-          GetPath(), where, mapped);
+-    }
+-    return false;
+-  }
+-
+-  /* When p_memsz is greater than p_filesz, we need to have nulled out memory
+-   * after p_filesz and before p_memsz.
+-   * Above the end of the last page, and up to p_memsz, we already have nulled
+-   * out memory because we mapped anonymous memory on the whole library virtual
+-   * address space. We just need to adjust this anonymous memory protection
+-   * flags. */
+-  if (pt_load->p_memsz > pt_load->p_filesz) {
+-    Addr file_end = pt_load->p_vaddr + pt_load->p_filesz;
+-    Addr mem_end = pt_load->p_vaddr + pt_load->p_memsz;
+-    Addr next_page = PageAlignedEndPtr(file_end);
+-    if (next_page > file_end) {
+-      void *ptr = GetPtr(file_end);
+-      memset(ptr, 0, next_page - file_end);
+-    }
+-    if (mem_end > next_page) {
+-      if (mprotect(GetPtr(next_page), mem_end - next_page, prot) < 0) {
+-        ERROR("%s: Failed to mprotect", GetPath());
+-        return false;
+-      }
+-    }
+-  }
+-  return true;
+-}
+-
+-namespace {
+-
+-void debug_dyn(const char *type, const Dyn *dyn)
+-{
+-  DEBUG_LOG("%s 0x%08" PRIxPTR, type, uintptr_t(dyn->d_un.d_val));
+-}
+-
+-} /* anonymous namespace */
+-
+-bool
+-CustomElf::InitDyn(const Phdr *pt_dyn)
+-{
+-  /* Scan PT_DYNAMIC segment and gather some information */
+-  const Dyn *first_dyn = GetPtr<Dyn>(pt_dyn->p_vaddr);
+-  const Dyn *end_dyn = GetPtr<Dyn>(pt_dyn->p_vaddr + pt_dyn->p_filesz);
+-  std::vector<Word> dt_needed;
+-  size_t symnum = 0;
+-  for (const Dyn *dyn = first_dyn; dyn < end_dyn && dyn->d_tag; dyn++) {
+-    switch (dyn->d_tag) {
+-      case DT_NEEDED:
+-        debug_dyn("DT_NEEDED", dyn);
+-        dt_needed.push_back(dyn->d_un.d_val);
+-        break;
+-      case DT_HASH:
+-        {
+-          debug_dyn("DT_HASH", dyn);
+-          const Word *hash_table_header = GetPtr<Word>(dyn->d_un.d_ptr);
+-          symnum = hash_table_header[1];
+-          buckets.Init(&hash_table_header[2], hash_table_header[0]);
+-          chains.Init(&*buckets.end());
+-        }
+-        break;
+-      case DT_STRTAB:
+-        debug_dyn("DT_STRTAB", dyn);
+-        strtab.Init(GetPtr(dyn->d_un.d_ptr));
+-        break;
+-      case DT_SYMTAB:
+-        debug_dyn("DT_SYMTAB", dyn);
+-        symtab.Init(GetPtr(dyn->d_un.d_ptr));
+-        break;
+-      case DT_SYMENT:
+-        debug_dyn("DT_SYMENT", dyn);
+-        if (dyn->d_un.d_val != sizeof(Sym)) {
+-          ERROR("%s: Unsupported DT_SYMENT", GetPath());
+-          return false;
+-        }
+-        break;
+-      case DT_TEXTREL:
+-        if (strcmp("libflashplayer.so", GetName()) == 0) {
+-          has_text_relocs = true;
+-        } else {
+-          ERROR("%s: Text relocations are not supported", GetPath());
+-          return false;
+-        }
+-        break;
+-      case DT_STRSZ: /* Ignored */
+-        debug_dyn("DT_STRSZ", dyn);
+-        break;
+-      case UNSUPPORTED_RELOC():
+-      case UNSUPPORTED_RELOC(SZ):
+-      case UNSUPPORTED_RELOC(ENT):
+-        ERROR("%s: Unsupported relocations", GetPath());
+-        return false;
+-      case RELOC():
+-        debug_dyn(STR_RELOC(), dyn);
+-        relocations.Init(GetPtr(dyn->d_un.d_ptr));
+-        break;
+-      case RELOC(SZ):
+-        debug_dyn(STR_RELOC(SZ), dyn);
+-        relocations.InitSize(dyn->d_un.d_val);
+-        break;
+-      case RELOC(ENT):
+-        debug_dyn(STR_RELOC(ENT), dyn);
+-        if (dyn->d_un.d_val != sizeof(Reloc)) {
+-          ERROR("%s: Unsupported DT_RELENT", GetPath());
+-          return false;
+-        }
+-        break;
+-      case DT_JMPREL:
+-        debug_dyn("DT_JMPREL", dyn);
+-        jumprels.Init(GetPtr(dyn->d_un.d_ptr));
+-        break;
+-      case DT_PLTRELSZ:
+-        debug_dyn("DT_PLTRELSZ", dyn);
+-        jumprels.InitSize(dyn->d_un.d_val);
+-        break;
+-      case DT_PLTGOT:
+-        debug_dyn("DT_PLTGOT", dyn);
+-        break;
+-      case DT_INIT:
+-        debug_dyn("DT_INIT", dyn);
+-        init = dyn->d_un.d_ptr;
+-        break;
+-      case DT_INIT_ARRAY:
+-        debug_dyn("DT_INIT_ARRAY", dyn);
+-        init_array.Init(GetPtr(dyn->d_un.d_ptr));
+-        break;
+-      case DT_INIT_ARRAYSZ:
+-        debug_dyn("DT_INIT_ARRAYSZ", dyn);
+-        init_array.InitSize(dyn->d_un.d_val);
+-        break;
+-      case DT_FINI:
+-        debug_dyn("DT_FINI", dyn);
+-        fini = dyn->d_un.d_ptr;
+-        break;
+-      case DT_FINI_ARRAY:
+-        debug_dyn("DT_FINI_ARRAY", dyn);
+-        fini_array.Init(GetPtr(dyn->d_un.d_ptr));
+-        break;
+-      case DT_FINI_ARRAYSZ:
+-        debug_dyn("DT_FINI_ARRAYSZ", dyn);
+-        fini_array.InitSize(dyn->d_un.d_val);
+-        break;
+-      case DT_PLTREL:
+-        if (dyn->d_un.d_val != RELOC()) {
+-          ERROR("%s: Error: DT_PLTREL is not " STR_RELOC(), GetPath());
+-          return false;
+-        }
+-        break;
+-      case DT_FLAGS:
+-        {
+-           Addr flags = dyn->d_un.d_val;
+-           /* Treat as a DT_TEXTREL tag */
+-           if (flags & DF_TEXTREL) {
+-             if (strcmp("libflashplayer.so", GetName()) == 0) {
+-               has_text_relocs = true;
+-             } else {
+-               ERROR("%s: Text relocations are not supported", GetPath());
+-               return false;
+-             }
+-           }
+-           /* we can treat this like having a DT_SYMBOLIC tag */
+-           flags &= ~DF_SYMBOLIC;
+-           if (flags)
+-             WARN("%s: unhandled flags #%" PRIxPTR" not handled",
+-                 GetPath(), uintptr_t(flags));
+-        }
+-        break;
+-      case DT_SONAME: /* Should match GetName(), but doesn't matter */
+-      case DT_SYMBOLIC: /* Indicates internal symbols should be looked up in
+-                         * the library itself first instead of the executable,
+-                         * which is actually what this linker does by default */
+-      case RELOC(COUNT): /* Indicates how many relocations are relative, which
+-                          * is usually used to skip relocations on prelinked
+-                          * libraries. They are not supported anyways. */
+-      case UNSUPPORTED_RELOC(COUNT): /* This should error out, but it doesn't
+-                                      * really matter. */
+-      case DT_FLAGS_1: /* Additional linker-internal flags that we don't care about. See
+-                        * DF_1_* values in src/include/elf/common.h in binutils. */
+-      case DT_VERSYM: /* DT_VER* entries are used for symbol versioning, which */
+-      case DT_VERDEF: /* this linker doesn't support yet. */
+-      case DT_VERDEFNUM:
+-      case DT_VERNEED:
+-      case DT_VERNEEDNUM:
+-        /* Ignored */
+-        break;
+-      default:
+-        WARN("%s: dynamic header type #%" PRIxPTR" not handled",
+-            GetPath(), uintptr_t(dyn->d_tag));
+-    }
+-  }
+-
+-  if (!buckets || !symnum) {
+-    ERROR("%s: Missing or broken DT_HASH", GetPath());
+-    return false;
+-  }
+-  if (!strtab) {
+-    ERROR("%s: Missing DT_STRTAB", GetPath());
+-    return false;
+-  }
+-  if (!symtab) {
+-    ERROR("%s: Missing DT_SYMTAB", GetPath());
+-    return false;
+-  }
+-
+-  /* Load dependent libraries */
+-  for (size_t i = 0; i < dt_needed.size(); i++) {
+-    const char *name = strtab.GetStringAt(dt_needed[i]);
+-    RefPtr<LibHandle> handle =
+-      ElfLoader::Singleton.Load(name, RTLD_GLOBAL | RTLD_LAZY, this);
+-    if (!handle)
+-      return false;
+-    dependencies.push_back(handle);
+-  }
+-
+-  return true;
+-}
+-
+-bool
+-CustomElf::Relocate()
+-{
+-  DEBUG_LOG("Relocate %s @%p", GetPath(), static_cast<void *>(base));
+-  uint32_t symtab_index = (uint32_t) -1;
+-  void *symptr = nullptr;
+-  for (Array<Reloc>::iterator rel = relocations.begin();
+-       rel < relocations.end(); ++rel) {
+-    /* Location of the relocation */
+-    void *ptr = GetPtr(rel->r_offset);
+-
+-    /* R_*_RELATIVE relocations apply directly at the given location */
+-    if (ELF_R_TYPE(rel->r_info) == R_RELATIVE) {
+-      *(void **) ptr = GetPtr(rel->GetAddend(base));
+-      continue;
+-    }
+-    /* Other relocation types need a symbol resolution */
+-    /* Avoid symbol resolution when it's the same symbol as last iteration */
+-    if (symtab_index != ELF_R_SYM(rel->r_info)) {
+-      symtab_index = ELF_R_SYM(rel->r_info);
+-      const Sym sym = symtab[symtab_index];
+-      if (sym.st_shndx != SHN_UNDEF) {
+-        symptr = GetPtr(sym.st_value);
+-      } else {
+-        /* TODO: handle symbol resolving to nullptr vs. being undefined. */
+-        symptr = GetSymbolPtrInDeps(strtab.GetStringAt(sym.st_name));
+-      }
+-    }
+-
+-    if (symptr == nullptr)
+-      WARN("%s: Relocation to NULL @0x%08" PRIxPTR,
+-          GetPath(), uintptr_t(rel->r_offset));
+-
+-    /* Apply relocation */
+-    switch (ELF_R_TYPE(rel->r_info)) {
+-    case R_GLOB_DAT:
+-      /* R_*_GLOB_DAT relocations simply use the symbol value */
+-      *(void **) ptr = symptr;
+-      break;
+-    case R_ABS:
+-      /* R_*_ABS* relocations add the relocation added to the symbol value */
+-      *(const char **) ptr = (const char *)symptr + rel->GetAddend(base);
+-      break;
+-    default:
+-      ERROR("%s: Unsupported relocation type: 0x%" PRIxPTR,
+-          GetPath(), uintptr_t(ELF_R_TYPE(rel->r_info)));
+-      return false;
+-    }
+-  }
+-  return true;
+-}
+-
+-bool
+-CustomElf::RelocateJumps()
+-{
+-  /* TODO: Dynamic symbol resolution */
+-  for (Array<Reloc>::iterator rel = jumprels.begin();
+-       rel < jumprels.end(); ++rel) {
+-    /* Location of the relocation */
+-    void *ptr = GetPtr(rel->r_offset);
+-
+-    /* Only R_*_JMP_SLOT relocations are expected */
+-    if (ELF_R_TYPE(rel->r_info) != R_JMP_SLOT) {
+-      ERROR("%s: Jump relocation type mismatch", GetPath());
+-      return false;
+-    }
+-
+-    /* TODO: Avoid code duplication with the relocations above */
+-    const Sym sym = symtab[ELF_R_SYM(rel->r_info)];
+-    void *symptr;
+-    if (sym.st_shndx != SHN_UNDEF)
+-      symptr = GetPtr(sym.st_value);
+-    else
+-      symptr = GetSymbolPtrInDeps(strtab.GetStringAt(sym.st_name));
+-
+-    if (symptr == nullptr) {
+-      if (ELF_ST_BIND(sym.st_info) == STB_WEAK) {
+-        WARN("%s: Relocation to NULL @0x%08" PRIxPTR " for symbol \"%s\"",
+-            GetPath(),
+-            uintptr_t(rel->r_offset), strtab.GetStringAt(sym.st_name));
+-      } else {
+-        ERROR("%s: Relocation to NULL @0x%08" PRIxPTR " for symbol \"%s\"",
+-            GetPath(),
+-            uintptr_t(rel->r_offset), strtab.GetStringAt(sym.st_name));
+-        return false;
+-      }
+-    }
+-    /* Apply relocation */
+-    *(void **) ptr = symptr;
+-  }
+-  return true;
+-}
+-
+-bool
+-CustomElf::CallInit()
+-{
+-  if (init)
+-    CallFunction(init);
+-
+-  for (Array<void *>::iterator it = init_array.begin();
+-       it < init_array.end(); ++it) {
+-    /* Android x86 NDK wrongly puts 0xffffffff in INIT_ARRAY */
+-    if (*it && *it != reinterpret_cast<void *>(-1))
+-      CallFunction(*it);
+-  }
+-  initialized = true;
+-  return true;
+-}
+-
+-void
+-CustomElf::CallFini()
+-{
+-  if (!initialized)
+-    return;
+-  for (Array<void *>::reverse_iterator it = fini_array.rbegin();
+-       it < fini_array.rend(); ++it) {
+-    /* Android x86 NDK wrongly puts 0xffffffff in FINI_ARRAY */
+-    if (*it && *it != reinterpret_cast<void *>(-1))
+-      CallFunction(*it);
+-  }
+-  if (fini)
+-    CallFunction(fini);
+-}
+-
+-Mappable *
+-CustomElf::GetMappable() const
+-{
+-  if (!mappable)
+-    return nullptr;
+-  if (mappable->GetKind() == Mappable::MAPPABLE_EXTRACT_FILE)
+-    return mappable;
+-  return ElfLoader::GetMappableFromPath(GetPath());
+-}
+diff --git a/mozglue/linker/CustomElf.h b/mozglue/linker/CustomElf.h
+deleted file mode 100644
+--- a/mozglue/linker/CustomElf.h
++++ /dev/null
+@@ -1,152 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#ifndef CustomElf_h
+-#define CustomElf_h
+-
+-#include "ElfLoader.h"
+-#include "BaseElf.h"
+-#include "Logging.h"
+-#include "Elfxx.h"
+-
+-/**
+- * Library Handle class for ELF libraries we don't let the system linker
+- * handle.
+- */
+-class CustomElf: public BaseElf, private ElfLoader::link_map
+-{
+-  friend class ElfLoader;
+-  friend class SEGVHandler;
+-public:
+-  /**
+-   * Returns a new CustomElf using the given file descriptor to map ELF
+-   * content. The file descriptor ownership is stolen, and it will be closed
+-   * in CustomElf's destructor if an instance is created, or by the Load
+-   * method otherwise. The path corresponds to the file descriptor, and flags
+-   * are the same kind of flags that would be given to dlopen(), though
+-   * currently, none are supported and the behaviour is more or less that of
+-   * RTLD_GLOBAL | RTLD_BIND_NOW.
+-   */
+-  static already_AddRefed<LibHandle> Load(Mappable *mappable,
+-                                               const char *path, int flags);
+-
+-  /**
+-   * Inherited from LibHandle/BaseElf
+-   */
+-  virtual ~CustomElf();
+-
+-protected:
+-  virtual Mappable *GetMappable() const;
+-
+-public:
+-  /**
+-   * Returns the instance, casted as BaseElf. (short of a better way to do
+-   * this without RTTI)
+-   */
+-  virtual BaseElf *AsBaseElf() { return this; }
+-
+-private:
+-  /**
+-   * Scan dependent libraries to find the address corresponding to the
+-   * given symbol name. This is used to find symbols that are undefined
+-   * in the Elf object.
+-   */
+-  void *GetSymbolPtrInDeps(const char *symbol) const;
+-
+-  /**
+-   * Private constructor
+-   */
+-  CustomElf(Mappable *mappable, const char *path)
+-  : BaseElf(path, mappable)
+-  , link_map()
+-  , init(0)
+-  , fini(0)
+-  , initialized(false)
+-  , has_text_relocs(false)
+-  { }
+-
+-  /**
+-   * Loads an Elf segment defined by the given PT_LOAD header.
+-   * Returns whether this succeeded or failed.
+-   */
+-  bool LoadSegment(const Elf::Phdr *pt_load) const;
+-
+-  /**
+-   * Initializes the library according to information found in the given
+-   * PT_DYNAMIC header.
+-   * Returns whether this succeeded or failed.
+-   */
+-  bool InitDyn(const Elf::Phdr *pt_dyn);
+-
+-  /**
+-   * Apply .rel.dyn/.rela.dyn relocations.
+-   * Returns whether this succeeded or failed.
+-   */
+-  bool Relocate();
+-
+-  /**
+-   * Apply .rel.plt/.rela.plt relocations.
+-   * Returns whether this succeeded or failed.
+-   */
+-  bool RelocateJumps();
+-
+-  /**
+-   * Call initialization functions (.init/.init_array)
+-   * Returns true;
+-   */
+-  bool CallInit();
+-
+-  /**
+-   * Call destructor functions (.fini_array/.fini)
+-   * Returns whether this succeeded or failed.
+-   */
+-  void CallFini();
+-
+-  /**
+-   * Call a function given a pointer to its location.
+-   */
+-  void CallFunction(void *ptr) const
+-  {
+-    /* C++ doesn't allow direct conversion between pointer-to-object
+-     * and pointer-to-function. */
+-    union {
+-      void *ptr;
+-      void (*func)(void);
+-    } f;
+-    f.ptr = ptr;
+-    DEBUG_LOG("%s: Calling function @%p", GetPath(), ptr);
+-    f.func();
+-  }
+-
+-  /**
+-   * Call a function given a an address relative to the library base
+-   */
+-  void CallFunction(Elf::Addr addr) const
+-  {
+-    return CallFunction(GetPtr(addr));
+-  }
+-
+-  /* List of dependent libraries */
+-  std::vector<RefPtr<LibHandle> > dependencies;
+-
+-  /* List of .rel.dyn/.rela.dyn relocations */
+-  Array<Elf::Reloc> relocations;
+-
+-  /* List of .rel.plt/.rela.plt relocation */
+-  Array<Elf::Reloc> jumprels;
+-
+-  /* Relative address of the initialization and destruction functions
+-   * (.init/.fini) */
+-  Elf::Addr init, fini;
+-
+-  /* List of initialization and destruction functions
+-   * (.init_array/.fini_array) */
+-  Array<void *> init_array, fini_array;
+-
+-  bool initialized;
+-
+-  bool has_text_relocs;
+-};
+-
+-#endif /* CustomElf_h */
+diff --git a/mozglue/linker/ElfLoader.cpp b/mozglue/linker/ElfLoader.cpp
+deleted file mode 100644
+--- a/mozglue/linker/ElfLoader.cpp
++++ /dev/null
+@@ -1,1301 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#include <string>
+-#include <cstring>
+-#include <cstdlib>
+-#include <cstdio>
+-#include <dlfcn.h>
+-#include <unistd.h>
+-#include <errno.h>
+-#include <algorithm>
+-#include <fcntl.h>
+-#include "ElfLoader.h"
+-#include "BaseElf.h"
+-#include "CustomElf.h"
+-#include "Mappable.h"
+-#include "Logging.h"
+-#include <inttypes.h>
+-
+-#if defined(ANDROID)
+-#include <sys/syscall.h>
+-#include <math.h>
+-
+-#include <android/api-level.h>
+-#if __ANDROID_API__ < 8
+-/* Android API < 8 doesn't provide sigaltstack */
+-
+-extern "C" {
+-
+-inline int sigaltstack(const stack_t *ss, stack_t *oss) {
+-  return syscall(__NR_sigaltstack, ss, oss);
+-}
+-
+-} /* extern "C" */
+-#endif /* __ANDROID_API__ */
+-#endif /* ANDROID */
+-
+-#ifdef __ARM_EABI__
+-extern "C" MOZ_EXPORT const void *
+-__gnu_Unwind_Find_exidx(void *pc, int *pcount) __attribute__((weak));
+-#endif
+-
+-/* Pointer to the PT_DYNAMIC section of the executable or library
+- * containing this code. */
+-extern "C" Elf::Dyn _DYNAMIC[];
+-
+-using namespace mozilla;
+-
+-/**
+- * dlfcn.h replacements functions
+- */
+-
+-void *
+-__wrap_dlopen(const char *path, int flags)
+-{
+-  RefPtr<LibHandle> handle = ElfLoader::Singleton.Load(path, flags);
+-  if (handle)
+-    handle->AddDirectRef();
+-  return handle;
+-}
+-
+-const char *
+-__wrap_dlerror(void)
+-{
+-  const char *error = ElfLoader::Singleton.lastError;
+-  ElfLoader::Singleton.lastError = nullptr;
+-  return error;
+-}
+-
+-void *
+-__wrap_dlsym(void *handle, const char *symbol)
+-{
+-  if (!handle) {
+-    ElfLoader::Singleton.lastError = "dlsym(NULL, sym) unsupported";
+-    return nullptr;
+-  }
+-  if (handle != RTLD_DEFAULT && handle != RTLD_NEXT) {
+-    LibHandle *h = reinterpret_cast<LibHandle *>(handle);
+-    return h->GetSymbolPtr(symbol);
+-  }
+-  return dlsym(handle, symbol);
+-}
+-
+-int
+-__wrap_dlclose(void *handle)
+-{
+-  if (!handle) {
+-    ElfLoader::Singleton.lastError = "No handle given to dlclose()";
+-    return -1;
+-  }
+-  reinterpret_cast<LibHandle *>(handle)->ReleaseDirectRef();
+-  return 0;
+-}
+-
+-int
+-__wrap_dladdr(void *addr, Dl_info *info)
+-{
+-  RefPtr<LibHandle> handle = ElfLoader::Singleton.GetHandleByPtr(addr);
+-  if (!handle) {
+-    return dladdr(addr, info);
+-  }
+-  info->dli_fname = handle->GetPath();
+-  info->dli_fbase = handle->GetBase();
+-  return 1;
+-}
+-
+-int
+-__wrap_dl_iterate_phdr(dl_phdr_cb callback, void *data)
+-{
+-  if (!ElfLoader::Singleton.dbg)
+-    return -1;
+-
+-  int pipefd[2];
+-  bool valid_pipe = (pipe(pipefd) == 0);
+-  AutoCloseFD read_fd(pipefd[0]);
+-  AutoCloseFD write_fd(pipefd[1]);
+-
+-  for (ElfLoader::DebuggerHelper::iterator it = ElfLoader::Singleton.dbg.begin();
+-       it < ElfLoader::Singleton.dbg.end(); ++it) {
+-    dl_phdr_info info;
+-    info.dlpi_addr = reinterpret_cast<Elf::Addr>(it->l_addr);
+-    info.dlpi_name = it->l_name;
+-    info.dlpi_phdr = nullptr;
+-    info.dlpi_phnum = 0;
+-
+-    // Assuming l_addr points to Elf headers (in most cases, this is true),
+-    // get the Phdr location from there.
+-    // Unfortunately, when l_addr doesn't point to Elf headers, it may point
+-    // to unmapped memory, or worse, unreadable memory. The only way to detect
+-    // the latter without causing a SIGSEGV is to use the pointer in a system
+-    // call that will try to read from there, and return an EFAULT error if
+-    // it can't. One such system call is write(). It used to be possible to
+-    // use a file descriptor on /dev/null for these kind of things, but recent
+-    // Linux kernels never return an EFAULT error when using /dev/null.
+-    // So instead, we use a self pipe. We do however need to read() from the
+-    // read end of the pipe as well so as to not fill up the pipe buffer and
+-    // block on subsequent writes.
+-    // In the unlikely event reads from or write to the pipe fail for some
+-    // other reason than EFAULT, we don't try any further and just skip setting
+-    // the Phdr location for all subsequent libraries, rather than trying to
+-    // start over with a new pipe.
+-    int can_read = true;
+-    if (valid_pipe) {
+-      int ret;
+-      char raw_ehdr[sizeof(Elf::Ehdr)];
+-      static_assert(sizeof(raw_ehdr) < PIPE_BUF, "PIPE_BUF is too small");
+-      do {
+-        // writes are atomic when smaller than PIPE_BUF, per POSIX.1-2008.
+-        ret = write(write_fd, it->l_addr, sizeof(raw_ehdr));
+-      } while (ret == -1 && errno == EINTR);
+-      if (ret != sizeof(raw_ehdr)) {
+-        if (ret == -1 && errno == EFAULT) {
+-          can_read = false;
+-        } else {
+-          valid_pipe = false;
+-        }
+-      } else {
+-        size_t nbytes = 0;
+-        do {
+-          // Per POSIX.1-2008, interrupted reads can return a length smaller
+-          // than the given one instead of failing with errno EINTR.
+-          ret = read(read_fd, raw_ehdr + nbytes, sizeof(raw_ehdr) - nbytes);
+-          if (ret > 0)
+-              nbytes += ret;
+-        } while ((nbytes != sizeof(raw_ehdr) && ret > 0) ||
+-                 (ret == -1 && errno == EINTR));
+-        if (nbytes != sizeof(raw_ehdr)) {
+-          valid_pipe = false;
+-        }
+-      }
+-    }
+-
+-    if (valid_pipe && can_read) {
+-      const Elf::Ehdr *ehdr = Elf::Ehdr::validate(it->l_addr);
+-      if (ehdr) {
+-        info.dlpi_phdr = reinterpret_cast<const Elf::Phdr *>(
+-                         reinterpret_cast<const char *>(ehdr) + ehdr->e_phoff);
+-        info.dlpi_phnum = ehdr->e_phnum;
+-      }
+-    }
+-
+-    int ret = callback(&info, sizeof(dl_phdr_info), data);
+-    if (ret)
+-      return ret;
+-  }
+-  return 0;
+-}
+-
+-#ifdef __ARM_EABI__
+-const void *
+-__wrap___gnu_Unwind_Find_exidx(void *pc, int *pcount)
+-{
+-  RefPtr<LibHandle> handle = ElfLoader::Singleton.GetHandleByPtr(pc);
+-  if (handle)
+-    return handle->FindExidx(pcount);
+-  if (__gnu_Unwind_Find_exidx)
+-    return __gnu_Unwind_Find_exidx(pc, pcount);
+-  *pcount = 0;
+-  return nullptr;
+-}
+-#endif
+-
+-/**
+- * faulty.lib public API
+- */
+-
+-MFBT_API size_t
+-__dl_get_mappable_length(void *handle) {
+-  if (!handle)
+-    return 0;
+-  return reinterpret_cast<LibHandle *>(handle)->GetMappableLength();
+-}
+-
+-MFBT_API void *
+-__dl_mmap(void *handle, void *addr, size_t length, off_t offset)
+-{
+-  if (!handle)
+-    return nullptr;
+-  return reinterpret_cast<LibHandle *>(handle)->MappableMMap(addr, length,
+-                                                             offset);
+-}
+-
+-MFBT_API void
+-__dl_munmap(void *handle, void *addr, size_t length)
+-{
+-  if (!handle)
+-    return;
+-  return reinterpret_cast<LibHandle *>(handle)->MappableMUnmap(addr, length);
+-}
+-
+-MFBT_API bool
+-IsSignalHandlingBroken()
+-{
+-  return ElfLoader::Singleton.isSignalHandlingBroken();
+-}
+-
+-namespace {
+-
+-/**
+- * Returns the part after the last '/' for the given path
+- */
+-const char *
+-LeafName(const char *path)
+-{
+-  const char *lastSlash = strrchr(path, '/');
+-  if (lastSlash)
+-    return lastSlash + 1;
+-  return path;
+-}
+-
+-} /* Anonymous namespace */
+-
+-/**
+- * LibHandle
+- */
+-LibHandle::~LibHandle()
+-{
+-  free(path);
+-}
+-
+-const char *
+-LibHandle::GetName() const
+-{
+-  return path ? LeafName(path) : nullptr;
+-}
+-
+-size_t
+-LibHandle::GetMappableLength() const
+-{
+-  if (!mappable)
+-    mappable = GetMappable();
+-  if (!mappable)
+-    return 0;
+-  return mappable->GetLength();
+-}
+-
+-void *
+-LibHandle::MappableMMap(void *addr, size_t length, off_t offset) const
+-{
+-  if (!mappable)
+-    mappable = GetMappable();
+-  if (!mappable)
+-    return MAP_FAILED;
+-  void* mapped = mappable->mmap(addr, length, PROT_READ, MAP_PRIVATE, offset);
+-  return mapped;
+-}
+-
+-void
+-LibHandle::MappableMUnmap(void *addr, size_t length) const
+-{
+-  if (mappable)
+-    mappable->munmap(addr, length);
+-}
+-
+-/**
+- * SystemElf
+- */
+-already_AddRefed<LibHandle>
+-SystemElf::Load(const char *path, int flags)
+-{
+-  /* The Android linker returns a handle when the file name matches an
+-   * already loaded library, even when the full path doesn't exist */
+-  if (path && path[0] == '/' && (access(path, F_OK) == -1)){
+-    DEBUG_LOG("dlopen(\"%s\", 0x%x) = %p", path, flags, (void *)nullptr);
+-    return nullptr;
+-  }
+-
+-  void *handle = dlopen(path, flags);
+-  DEBUG_LOG("dlopen(\"%s\", 0x%x) = %p", path, flags, handle);
+-  ElfLoader::Singleton.lastError = dlerror();
+-  if (handle) {
+-    SystemElf *elf = new SystemElf(path, handle);
+-    ElfLoader::Singleton.Register(elf);
+-    RefPtr<LibHandle> lib(elf);
+-    return lib.forget();
+-  }
+-  return nullptr;
+-}
+-
+-SystemElf::~SystemElf()
+-{
+-  if (!dlhandle)
+-    return;
+-  DEBUG_LOG("dlclose(%p [\"%s\"])", dlhandle, GetPath());
+-  dlclose(dlhandle);
+-  ElfLoader::Singleton.lastError = dlerror();
+-  ElfLoader::Singleton.Forget(this);
+-}
+-
+-void *
+-SystemElf::GetSymbolPtr(const char *symbol) const
+-{
+-  void *sym = dlsym(dlhandle, symbol);
+-  DEBUG_LOG("dlsym(%p [\"%s\"], \"%s\") = %p", dlhandle, GetPath(), symbol, sym);
+-  ElfLoader::Singleton.lastError = dlerror();
+-  return sym;
+-}
+-
+-Mappable *
+-SystemElf::GetMappable() const
+-{
+-  const char *path = GetPath();
+-  if (!path)
+-    return nullptr;
+-#ifdef ANDROID
+-  /* On Android, if we don't have the full path, try in /system/lib */
+-  const char *name = LeafName(path);
+-  std::string systemPath;
+-  if (name == path) {
+-    systemPath = "/system/lib/";
+-    systemPath += path;
+-    path = systemPath.c_str();
+-  }
+-#endif
+-
+-  return MappableFile::Create(path);
+-}
+-
+-#ifdef __ARM_EABI__
+-const void *
+-SystemElf::FindExidx(int *pcount) const
+-{
+-  /* TODO: properly implement when ElfLoader::GetHandleByPtr
+-     does return SystemElf handles */
+-  *pcount = 0;
+-  return nullptr;
+-}
+-#endif
+-
+-/**
+- * ElfLoader
+- */
+-
+-/* Unique ElfLoader instance */
+-ElfLoader ElfLoader::Singleton;
+-
+-already_AddRefed<LibHandle>
+-ElfLoader::Load(const char *path, int flags, LibHandle *parent)
+-{
+-  /* Ensure logging is initialized or refresh if environment changed. */
+-  Logging::Init();
+-
+-  /* Ensure self_elf initialization. */
+-  if (!self_elf)
+-    Init();
+-
+-  RefPtr<LibHandle> handle;
+-
+-  /* Handle dlopen(nullptr) directly. */
+-  if (!path) {
+-    handle = SystemElf::Load(nullptr, flags);
+-    return handle.forget();
+-  }
+-
+-  /* TODO: Handle relative paths correctly */
+-  const char *name = LeafName(path);
+-
+-  /* Search the list of handles we already have for a match. When the given
+-   * path is not absolute, compare file names, otherwise compare full paths. */
+-  if (name == path) {
+-    AutoLock lock(&handlesMutex);
+-    for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it)
+-      if ((*it)->GetName() && (strcmp((*it)->GetName(), name) == 0)) {
+-        handle = *it;
+-        return handle.forget();
+-      }
+-  } else {
+-    AutoLock lock(&handlesMutex);
+-    for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it)
+-      if ((*it)->GetPath() && (strcmp((*it)->GetPath(), path) == 0)) {
+-        handle = *it;
+-        return handle.forget();
+-      }
+-  }
+-
+-  char *abs_path = nullptr;
+-  const char *requested_path = path;
+-
+-  /* When the path is not absolute and the library is being loaded for
+-   * another, first try to load the library from the directory containing
+-   * that parent library. */
+-  if ((name == path) && parent) {
+-    const char *parentPath = parent->GetPath();
+-    abs_path = new char[strlen(parentPath) + strlen(path)];
+-    strcpy(abs_path, parentPath);
+-    char *slash = strrchr(abs_path, '/');
+-    strcpy(slash + 1, path);
+-    path = abs_path;
+-  }
+-
+-  Mappable *mappable = GetMappableFromPath(path);
+-
+-  /* Try loading with the custom linker if we have a Mappable */
+-  if (mappable)
+-    handle = CustomElf::Load(mappable, path, flags);
+-
+-  /* Try loading with the system linker if everything above failed */
+-  if (!handle)
+-    handle = SystemElf::Load(path, flags);
+-
+-  /* If we didn't have an absolute path and haven't been able to load
+-   * a library yet, try in the system search path */
+-  if (!handle && abs_path)
+-    handle = SystemElf::Load(name, flags);
+-
+-  delete [] abs_path;
+-  DEBUG_LOG("ElfLoader::Load(\"%s\", 0x%x, %p [\"%s\"]) = %p", requested_path, flags,
+-            reinterpret_cast<void *>(parent), parent ? parent->GetPath() : "",
+-            static_cast<void *>(handle));
+-
+-  return handle.forget();
+-}
+-
+-already_AddRefed<LibHandle>
+-ElfLoader::GetHandleByPtr(void *addr)
+-{
+-  AutoLock lock(&handlesMutex);
+-  /* Scan the list of handles we already have for a match */
+-  for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it) {
+-    if ((*it)->Contains(addr)) {
+-      RefPtr<LibHandle> lib = *it;
+-      return lib.forget();
+-    }
+-  }
+-  return nullptr;
+-}
+-
+-Mappable *
+-ElfLoader::GetMappableFromPath(const char *path)
+-{
+-  const char *name = LeafName(path);
+-  Mappable *mappable = nullptr;
+-  RefPtr<Zip> zip;
+-  const char *subpath;
+-  if ((subpath = strchr(path, '!'))) {
+-    char *zip_path = strndup(path, subpath - path);
+-    while (*(++subpath) == '/') { }
+-    zip = ZipCollection::GetZip(zip_path);
+-    free(zip_path);
+-    Zip::Stream s;
+-    if (zip && zip->GetStream(subpath, &s)) {
+-      /* When the MOZ_LINKER_EXTRACT environment variable is set to "1",
+-       * compressed libraries are going to be (temporarily) extracted as
+-       * files, in the directory pointed by the MOZ_LINKER_CACHE
+-       * environment variable. */
+-      const char *extract = getenv("MOZ_LINKER_EXTRACT");
+-      if (extract && !strncmp(extract, "1", 2 /* Including '\0' */))
+-        mappable = MappableExtractFile::Create(name, zip, &s);
+-      if (!mappable) {
+-        if (s.GetType() == Zip::Stream::DEFLATE) {
+-          mappable = MappableDeflate::Create(name, zip, &s);
+-        }
+-      }
+-    }
+-  }
+-  /* If we couldn't load above, try with a MappableFile */
+-  if (!mappable && !zip)
+-    mappable = MappableFile::Create(path);
+-
+-  return mappable;
+-}
+-
+-void
+-ElfLoader::Register(LibHandle *handle)
+-{
+-  AutoLock lock(&handlesMutex);
+-  handles.push_back(handle);
+-}
+-
+-void
+-ElfLoader::Register(CustomElf *handle)
+-{
+-  Register(static_cast<LibHandle *>(handle));
+-  if (dbg) {
+-    dbg.Add(handle);
+-  }
+-}
+-
+-void
+-ElfLoader::Forget(LibHandle *handle)
+-{
+-  /* Ensure logging is initialized or refresh if environment changed. */
+-  Logging::Init();
+-
+-  AutoLock lock(&handlesMutex);
+-  LibHandleList::iterator it = std::find(handles.begin(), handles.end(), handle);
+-  if (it != handles.end()) {
+-    DEBUG_LOG("ElfLoader::Forget(%p [\"%s\"])", reinterpret_cast<void *>(handle),
+-                                                handle->GetPath());
+-    handles.erase(it);
+-  } else {
+-    DEBUG_LOG("ElfLoader::Forget(%p [\"%s\"]): Handle not found",
+-              reinterpret_cast<void *>(handle), handle->GetPath());
+-  }
+-}
+-
+-void
+-ElfLoader::Forget(CustomElf *handle)
+-{
+-  Forget(static_cast<LibHandle *>(handle));
+-  if (dbg) {
+-    dbg.Remove(handle);
+-  }
+-}
+-
+-void
+-ElfLoader::Init()
+-{
+-  Dl_info info;
+-  /* On Android < 4.1 can't reenter dl* functions. So when the library
+-   * containing this code is dlopen()ed, it can't call dladdr from a
+-   * static initializer. */
+-  if (dladdr(_DYNAMIC, &info) != 0) {
+-    self_elf = LoadedElf::Create(info.dli_fname, info.dli_fbase);
+-  }
+-#if defined(ANDROID)
+-  if (dladdr(FunctionPtr(syscall), &info) != 0) {
+-    libc = LoadedElf::Create(info.dli_fname, info.dli_fbase);
+-  }
+-  if (dladdr(FunctionPtr<int (*)(double)>(isnan), &info) != 0) {
+-    libm = LoadedElf::Create(info.dli_fname, info.dli_fbase);
+-  }
+-#endif
+-}
+-
+-ElfLoader::~ElfLoader()
+-{
+-  LibHandleList list;
+-
+-  if (!Singleton.IsShutdownExpected()) {
+-    MOZ_CRASH("Unexpected shutdown");
+-  }
+-
+-  /* Release self_elf and libc */
+-  self_elf = nullptr;
+-#if defined(ANDROID)
+-  libc = nullptr;
+-  libm = nullptr;
+-#endif
+-
+-  AutoLock lock(&handlesMutex);
+-  /* Build up a list of all library handles with direct (external) references.
+-   * We actually skip system library handles because we want to keep at least
+-   * some of these open. Most notably, Mozilla codebase keeps a few libgnome
+-   * libraries deliberately open because of the mess that libORBit destruction
+-   * is. dlclose()ing these libraries actually leads to problems. */
+-  for (LibHandleList::reverse_iterator it = handles.rbegin();
+-       it < handles.rend(); ++it) {
+-    if ((*it)->DirectRefCount()) {
+-      if (SystemElf *se = (*it)->AsSystemElf()) {
+-        se->Forget();
+-      } else {
+-        list.push_back(*it);
+-      }
+-    }
+-  }
+-  /* Force release all external references to the handles collected above */
+-  for (LibHandleList::iterator it = list.begin(); it < list.end(); ++it) {
+-    while ((*it)->ReleaseDirectRef()) { }
+-  }
+-  /* Remove the remaining system handles. */
+-  if (handles.size()) {
+-    list = handles;
+-    for (LibHandleList::reverse_iterator it = list.rbegin();
+-         it < list.rend(); ++it) {
+-      if ((*it)->AsSystemElf()) {
+-        DEBUG_LOG("ElfLoader::~ElfLoader(): Remaining handle for \"%s\" "
+-                  "[%" PRIdPTR " direct refs, %" PRIdPTR " refs total]",
+-                  (*it)->GetPath(), (*it)->DirectRefCount(), (*it)->refCount());
+-      } else {
+-        DEBUG_LOG("ElfLoader::~ElfLoader(): Unexpected remaining handle for \"%s\" "
+-                  "[%" PRIdPTR " direct refs, %" PRIdPTR " refs total]",
+-                  (*it)->GetPath(), (*it)->DirectRefCount(), (*it)->refCount());
+-        /* Not removing, since it could have references to other libraries,
+-         * destroying them as a side effect, and possibly leaving dangling
+-         * pointers in the handle list we're scanning */
+-      }
+-    }
+-  }
+-  pthread_mutex_destroy(&handlesMutex);
+-}
+-
+-#ifdef __ARM_EABI__
+-int
+-ElfLoader::__wrap_aeabi_atexit(void *that, ElfLoader::Destructor destructor,
+-                               void *dso_handle)
+-{
+-  Singleton.destructors.push_back(
+-    DestructorCaller(destructor, that, dso_handle));
+-  return 0;
+-}
+-#else
+-int
+-ElfLoader::__wrap_cxa_atexit(ElfLoader::Destructor destructor, void *that,
+-                             void *dso_handle)
+-{
+-  Singleton.destructors.push_back(
+-    DestructorCaller(destructor, that, dso_handle));
+-  return 0;
+-}
+-#endif
+-
+-void
+-ElfLoader::__wrap_cxa_finalize(void *dso_handle)
+-{
+-  /* Call all destructors for the given DSO handle in reverse order they were
+-   * registered. */
+-  std::vector<DestructorCaller>::reverse_iterator it;
+-  for (it = Singleton.destructors.rbegin();
+-       it < Singleton.destructors.rend(); ++it) {
+-    if (it->IsForHandle(dso_handle)) {
+-      it->Call();
+-    }
+-  }
+-}
+-
+-void
+-ElfLoader::DestructorCaller::Call()
+-{
+-  if (destructor) {
+-    DEBUG_LOG("ElfLoader::DestructorCaller::Call(%p, %p, %p)",
+-              FunctionPtr(destructor), object, dso_handle);
+-    destructor(object);
+-    destructor = nullptr;
+-  }
+-}
+-
+-ElfLoader::DebuggerHelper::DebuggerHelper(): dbg(nullptr), firstAdded(nullptr)
+-{
+-  /* Find ELF auxiliary vectors.
+-   *
+-   * The kernel stores the following data on the stack when starting a
+-   * program:
+-   *   argc
+-   *   argv[0] (pointer into argv strings defined below)
+-   *   argv[1] (likewise)
+-   *   ...
+-   *   argv[argc - 1] (likewise)
+-   *   nullptr
+-   *   envp[0] (pointer into environment strings defined below)
+-   *   envp[1] (likewise)
+-   *   ...
+-   *   envp[n] (likewise)
+-   *   nullptr
+-   *   ... (more NULLs on some platforms such as Android 4.3)
+-   *   auxv[0] (first ELF auxiliary vector)
+-   *   auxv[1] (second ELF auxiliary vector)
+-   *   ...
+-   *   auxv[p] (last ELF auxiliary vector)
+-   *   (AT_NULL, nullptr)
+-   *   padding
+-   *   argv strings, separated with '\0'
+-   *   environment strings, separated with '\0'
+-   *   nullptr
+-   *
+-   * What we are after are the auxv values defined by the following struct.
+-   */
+-  struct AuxVector {
+-    Elf::Addr type;
+-    Elf::Addr value;
+-  };
+-
+-  /* Pointer to the environment variables list */
+-  extern char **environ;
+-
+-  /* The environment may have changed since the program started, in which
+-   * case the environ variables list isn't the list the kernel put on stack
+-   * anymore. But in this new list, variables that didn't change still point
+-   * to the strings the kernel put on stack. It is quite unlikely that two
+-   * modified environment variables point to two consecutive strings in memory,
+-   * so we assume that if two consecutive environment variables point to two
+-   * consecutive strings, we found strings the kernel put on stack. */
+-  char **env;
+-  for (env = environ; *env; env++)
+-    if (*env + strlen(*env) + 1 == env[1])
+-      break;
+-  if (!*env)
+-    return;
+-
+-  /* Next, we scan the stack backwards to find a pointer to one of those
+-   * strings we found above, which will give us the location of the original
+-   * envp list. As we are looking for pointers, we need to look at 32-bits or
+-   * 64-bits aligned values, depening on the architecture. */
+-  char **scan = reinterpret_cast<char **>(
+-                reinterpret_cast<uintptr_t>(*env) & ~(sizeof(void *) - 1));
+-  while (*env != *scan)
+-    scan--;
+-
+-  /* Finally, scan forward to find the last environment variable pointer and
+-   * thus the first auxiliary vector. */
+-  while (*scan++);
+-
+-  /* Some platforms have more NULLs here, so skip them if we encounter them */
+-  while (!*scan)
+-    scan++;
+-
+-  AuxVector *auxv = reinterpret_cast<AuxVector *>(scan);
+-
+-  /* The two values of interest in the auxiliary vectors are AT_PHDR and
+-   * AT_PHNUM, which gives us the the location and size of the ELF program
+-   * headers. */
+-  Array<Elf::Phdr> phdrs;
+-  char *base = nullptr;
+-  while (auxv->type) {
+-    if (auxv->type == AT_PHDR) {
+-      phdrs.Init(reinterpret_cast<Elf::Phdr*>(auxv->value));
+-      /* Assume the base address is the first byte of the same page */
+-      base = reinterpret_cast<char *>(PageAlignedPtr(auxv->value));
+-    }
+-    if (auxv->type == AT_PHNUM)
+-      phdrs.Init(auxv->value);
+-    auxv++;
+-  }
+-
+-  if (!phdrs) {
+-    DEBUG_LOG("Couldn't find program headers");
+-    return;
+-  }
+-
+-  /* In some cases, the address for the program headers we get from the
+-   * auxiliary vectors is not mapped, because of the PT_LOAD segments
+-   * definitions in the program executable. Trying to map anonymous memory
+-   * with a hint giving the base address will return a different address
+-   * if something is mapped there, and the base address otherwise. */
+-  MappedPtr mem(MemoryRange::mmap(base, PageSize(), PROT_NONE,
+-                                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+-  if (mem == base) {
+-    /* If program headers aren't mapped, try to map them */
+-    int fd = open("/proc/self/exe", O_RDONLY);
+-    if (fd == -1) {
+-      DEBUG_LOG("Failed to open /proc/self/exe");
+-      return;
+-    }
+-    mem.Assign(MemoryRange::mmap(base, PageSize(), PROT_READ, MAP_PRIVATE,
+-                                 fd, 0));
+-    /* If we don't manage to map at the right address, just give up. */
+-    if (mem != base) {
+-      DEBUG_LOG("Couldn't read program headers");
+-      return;
+-    }
+-  }
+-  /* Sanity check: the first bytes at the base address should be an ELF
+-   * header. */
+-  if (!Elf::Ehdr::validate(base)) {
+-     DEBUG_LOG("Couldn't find program base");
+-     return;
+-  }
+-
+-  /* Search for the program PT_DYNAMIC segment */
+-  Array<Elf::Dyn> dyns;
+-  for (Array<Elf::Phdr>::iterator phdr = phdrs.begin(); phdr < phdrs.end();
+-       ++phdr) {
+-    /* While the program headers are expected within the first mapped page of
+-     * the program executable, the executable PT_LOADs may actually make them
+-     * loaded at an address that is not the wanted base address of the
+-     * library. We thus need to adjust the base address, compensating for the
+-     * virtual address of the PT_LOAD segment corresponding to offset 0. */
+-    if (phdr->p_type == PT_LOAD && phdr->p_offset == 0)
+-      base -= phdr->p_vaddr;
+-    if (phdr->p_type == PT_DYNAMIC)
+-      dyns.Init(base + phdr->p_vaddr, phdr->p_filesz);
+-  }
+-  if (!dyns) {
+-    DEBUG_LOG("Failed to find PT_DYNAMIC section in program");
+-    return;
+-  }
+-
+-  /* Search for the DT_DEBUG information */
+-  for (Array<Elf::Dyn>::iterator dyn = dyns.begin(); dyn < dyns.end(); ++dyn) {
+-    if (dyn->d_tag == DT_DEBUG) {
+-      dbg = reinterpret_cast<r_debug *>(dyn->d_un.d_ptr);
+-      break;
+-    }
+-  }
+-  DEBUG_LOG("DT_DEBUG points at %p", static_cast<void *>(dbg));
+-}
+-
+-/**
+- * Helper class to ensure the given pointer is writable within the scope of
+- * an instance. Permissions to the memory page where the pointer lies are
+- * restored to their original value when the instance is destroyed.
+- */
+-class EnsureWritable
+-{
+-public:
+-  template <typename T>
+-  EnsureWritable(T *ptr, size_t length_ = sizeof(T))
+-  {
+-    MOZ_ASSERT(length_ < PageSize());
+-    prot = -1;
+-    page = MAP_FAILED;
+-
+-    char *firstPage = PageAlignedPtr(reinterpret_cast<char *>(ptr));
+-    char *lastPageEnd = PageAlignedEndPtr(reinterpret_cast<char *>(ptr) + length_);
+-    length = lastPageEnd - firstPage;
+-    uintptr_t start = reinterpret_cast<uintptr_t>(firstPage);
+-    uintptr_t end;
+-
+-    prot = getProt(start, &end);
+-    if (prot == -1 || (start + length) > end)
+-      MOZ_CRASH();
+-
+-    if (prot & PROT_WRITE)
+-      return;
+-
+-    page = firstPage;
+-    mprotect(page, length, prot | PROT_WRITE);
+-  }
+-
+-  ~EnsureWritable()
+-  {
+-    if (page != MAP_FAILED) {
+-      mprotect(page, length, prot);
+-}
+-  }
+-
+-private:
+-  int getProt(uintptr_t addr, uintptr_t *end)
+-  {
+-    /* The interesting part of the /proc/self/maps format looks like:
+-     * startAddr-endAddr rwxp */
+-    int result = 0;
+-    AutoCloseFILE f(fopen("/proc/self/maps", "r"));
+-    while (f) {
+-      unsigned long long startAddr, endAddr;
+-      char perms[5];
+-      if (fscanf(f, "%llx-%llx %4s %*1024[^\n] ", &startAddr, &endAddr, perms) != 3)
+-        return -1;
+-      if (addr < startAddr || addr >= endAddr)
+-        continue;
+-      if (perms[0] == 'r')
+-        result |= PROT_READ;
+-      else if (perms[0] != '-')
+-        return -1;
+-      if (perms[1] == 'w')
+-        result |= PROT_WRITE;
+-      else if (perms[1] != '-')
+-        return -1;
+-      if (perms[2] == 'x')
+-        result |= PROT_EXEC;
+-      else if (perms[2] != '-')
+-        return -1;
+-      *end = endAddr;
+-      return result;
+-    }
+-    return -1;
+-  }
+-
+-  int prot;
+-  void *page;
+-  size_t length;
+-};
+-
+-/**
+- * The system linker maintains a doubly linked list of library it loads
+- * for use by the debugger. Unfortunately, it also uses the list pointers
+- * in a lot of operations and adding our data in the list is likely to
+- * trigger crashes when the linker tries to use data we don't provide or
+- * that fall off the amount data we allocated. Fortunately, the linker only
+- * traverses the list forward and accesses the head of the list from a
+- * private pointer instead of using the value in the r_debug structure.
+- * This means we can safely add members at the beginning of the list.
+- * Unfortunately, gdb checks the coherency of l_prev values, so we have
+- * to adjust the l_prev value for the first element the system linker
+- * knows about. Fortunately, it doesn't use l_prev, and the first element
+- * is not ever going to be released before our elements, since it is the
+- * program executable, so the system linker should not be changing
+- * r_debug::r_map.
+- */
+-void
+-ElfLoader::DebuggerHelper::Add(ElfLoader::link_map *map)
+-{
+-  if (!dbg->r_brk)
+-    return;
+-  dbg->r_state = r_debug::RT_ADD;
+-  dbg->r_brk();
+-  map->l_prev = nullptr;
+-  map->l_next = dbg->r_map;
+-  if (!firstAdded) {
+-    firstAdded = map;
+-    /* When adding a library for the first time, r_map points to data
+-     * handled by the system linker, and that data may be read-only */
+-    EnsureWritable w(&dbg->r_map->l_prev);
+-    dbg->r_map->l_prev = map;
+-  } else
+-    dbg->r_map->l_prev = map;
+-  dbg->r_map = map;
+-  dbg->r_state = r_debug::RT_CONSISTENT;
+-  dbg->r_brk();
+-}
+-
+-void
+-ElfLoader::DebuggerHelper::Remove(ElfLoader::link_map *map)
+-{
+-  if (!dbg->r_brk)
+-    return;
+-  dbg->r_state = r_debug::RT_DELETE;
+-  dbg->r_brk();
+-  if (dbg->r_map == map)
+-    dbg->r_map = map->l_next;
+-  else if (map->l_prev) {
+-    map->l_prev->l_next = map->l_next;
+-  }
+-  if (map == firstAdded) {
+-    firstAdded = map->l_prev;
+-    /* When removing the first added library, its l_next is going to be
+-     * data handled by the system linker, and that data may be read-only */
+-    EnsureWritable w(&map->l_next->l_prev);
+-    map->l_next->l_prev = map->l_prev;
+-  } else if (map->l_next) {
+-    map->l_next->l_prev = map->l_prev;
+-  }
+-  dbg->r_state = r_debug::RT_CONSISTENT;
+-  dbg->r_brk();
+-}
+-
+-#if defined(ANDROID) && defined(__NR_sigaction)
+-/* As some system libraries may be calling signal() or sigaction() to
+- * set a SIGSEGV handler, effectively breaking MappableSeekableZStream,
+- * or worse, restore our SIGSEGV handler with wrong flags (which using
+- * signal() will do), we want to hook into the system's sigaction() to
+- * replace it with our own wrapper instead, so that our handler is never
+- * replaced. We used to only do that with libraries this linker loads,
+- * but it turns out at least one system library does call signal() and
+- * breaks us (libsc-a3xx.so on the Samsung Galaxy S4).
+- * As libc's signal (bsd_signal/sysv_signal, really) calls sigaction
+- * under the hood, instead of calling the signal system call directly,
+- * we only need to hook sigaction. This is true for both bionic and
+- * glibc.
+- */
+-
+-/* libc's sigaction */
+-extern "C" int
+-sigaction(int signum, const struct sigaction *act,
+-          struct sigaction *oldact);
+-
+-/* Simple reimplementation of sigaction. This is roughly equivalent
+- * to the assembly that comes in bionic, but not quite equivalent to
+- * glibc's implementation, so we only use this on Android. */
+-int
+-sys_sigaction(int signum, const struct sigaction *act,
+-              struct sigaction *oldact)
+-{
+-  return syscall(__NR_sigaction, signum, act, oldact);
+-}
+-
+-/* Replace the first instructions of the given function with a jump
+- * to the given new function. */
+-template <typename T>
+-static bool
+-Divert(T func, T new_func)
+-{
+-  void *ptr = FunctionPtr(func);
+-  uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
+-
+-#if defined(__i386__)
+-  // A 32-bit jump is a 5 bytes instruction.
+-  EnsureWritable w(ptr, 5);
+-  *reinterpret_cast<unsigned char *>(addr) = 0xe9; // jmp
+-  *reinterpret_cast<intptr_t *>(addr + 1) =
+-    reinterpret_cast<uintptr_t>(new_func) - addr - 5; // target displacement
+-  return true;
+-#elif defined(__arm__) || defined(__aarch64__)
+-  const unsigned char trampoline[] = {
+-# ifdef __arm__
+-                            // .thumb
+-    0x46, 0x04,             // nop
+-    0x78, 0x47,             // bx pc
+-    0x46, 0x04,             // nop
+-                            // .arm
+-    0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4]
+-                            // .word <new_func>
+-# else // __aarch64__
+-    0x50, 0x00, 0x00, 0x58, // ldr x16, [pc, #8]   ; x16 (aka ip0) is the first
+-    0x00, 0x02, 0x1f, 0xd6, // br x16              ; intra-procedure-call
+-                            // .word <new_func.lo> ; scratch register.
+-                            // .word <new_func.hi>
+-# endif
+-  };
+-  const unsigned char *start;
+-# ifdef __arm__
+-  if (addr & 0x01) {
+-    /* Function is thumb, the actual address of the code is without the
+-     * least significant bit. */
+-    addr--;
+-    /* The arm part of the trampoline needs to be 32-bit aligned */
+-    if (addr & 0x02)
+-      start = trampoline;
+-    else
+-      start = trampoline + 2;
+-  } else {
+-    /* Function is arm, we only need the arm part of the trampoline */
+-    start = trampoline + 6;
+-  }
+-# else // __aarch64__
+-  start = trampoline;
+-#endif
+-
+-  size_t len = sizeof(trampoline) - (start - trampoline);
+-  EnsureWritable w(reinterpret_cast<void *>(addr), len + sizeof(void *));
+-  memcpy(reinterpret_cast<void *>(addr), start, len);
+-  *reinterpret_cast<void **>(addr + len) = FunctionPtr(new_func);
+-  __builtin___clear_cache(reinterpret_cast<char*>(addr),
+-                          reinterpret_cast<char*>(addr + len + sizeof(void *)));
+-  return true;
+-#else
+-  return false;
+-#endif
+-}
+-#else
+-#define sys_sigaction sigaction
+-template <typename T>
+-static bool
+-Divert(T func, T new_func)
+-{
+-  return false;
+-}
+-#endif
+-
+-namespace {
+-
+-/* Clock that only accounts for time spent in the current process. */
+-static uint64_t ProcessTimeStamp_Now()
+-{
+-  struct timespec ts;
+-  int rv = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
+-
+-  if (rv != 0) {
+-    return 0;
+-  }
+-
+-  uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000;
+-  return baseNs + (uint64_t)ts.tv_nsec;
+-}
+-
+-}
+-
+-/* Data structure used to pass data to the temporary signal handler,
+- * as well as triggering a test crash. */
+-struct TmpData {
+-  volatile int crash_int;
+-  volatile uint64_t crash_timestamp;
+-};
+-
+-SEGVHandler::SEGVHandler()
+-: initialized(false), registeredHandler(false), signalHandlingBroken(true)
+-, signalHandlingSlow(true)
+-{
+-  /* Ensure logging is initialized before the DEBUG_LOG in the test_handler.
+-   * As this constructor runs before the ElfLoader constructor (by effect
+-   * of ElfLoader inheriting from this class), this also initializes on behalf
+-   * of ElfLoader and DebuggerHelper. */
+-  Logging::Init();
+-
+-  /* Initialize oldStack.ss_flags to an invalid value when used to set
+-   * an alternative stack, meaning we haven't got information about the
+-   * original alternative stack and thus don't mean to restore it in
+-   * the destructor. */
+-  oldStack.ss_flags = SS_ONSTACK;
+-
+-  /* Get the current segfault signal handler. */
+-  struct sigaction old_action;
+-  sys_sigaction(SIGSEGV, nullptr, &old_action);
+-
+-  /* Some devices don't provide useful information to their SIGSEGV handlers,
+-   * making it impossible for on-demand decompression to work. To check if
+-   * we're on such a device, setup a temporary handler and deliberately
+-   * trigger a segfault. The handler will set signalHandlingBroken if the
+-   * provided information is bogus.
+-   * Some other devices have a kernel option enabled that makes SIGSEGV handler
+-   * have an overhead so high that it affects how on-demand decompression
+-   * performs. The handler will also set signalHandlingSlow if the triggered
+-   * SIGSEGV took too much time. */
+-  struct sigaction action;
+-  action.sa_sigaction = &SEGVHandler::test_handler;
+-  sigemptyset(&action.sa_mask);
+-  action.sa_flags = SA_SIGINFO | SA_NODEFER;
+-  action.sa_restorer = nullptr;
+-  stackPtr.Assign(MemoryRange::mmap(nullptr, PageSize(),
+-                                    PROT_READ | PROT_WRITE,
+-                                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+-  if (stackPtr.get() == MAP_FAILED)
+-    return;
+-  if (sys_sigaction(SIGSEGV, &action, nullptr))
+-    return;
+-
+-  TmpData *data = reinterpret_cast<TmpData*>(stackPtr.get());
+-  data->crash_timestamp = ProcessTimeStamp_Now();
+-  mprotect(stackPtr, stackPtr.GetLength(), PROT_NONE);
+-  data->crash_int = 123;
+-  /* Restore the original segfault signal handler. */
+-  sys_sigaction(SIGSEGV, &old_action, nullptr);
+-  stackPtr.Assign(MAP_FAILED, 0);
+-}
+-
+-void
+-SEGVHandler::FinishInitialization()
+-{
+-  /* Ideally, we'd need some locking here, but in practice, we're not
+-   * going to race with another thread. */
+-  initialized = true;
+-
+-  if (signalHandlingBroken || signalHandlingSlow)
+-    return;
+-
+-  typedef int (*sigaction_func)(int, const struct sigaction *,
+-                                struct sigaction *);
+-
+-  sigaction_func libc_sigaction;
+-
+-#if defined(ANDROID)
+-  /* Android > 4.4 comes with a sigaction wrapper in a LD_PRELOADed library
+-   * (libsigchain) for ART. That wrapper kind of does the same trick as we
+-   * do, so we need extra care in handling it.
+-   * - Divert the libc's sigaction, assuming the LD_PRELOADed library uses
+-   *   it under the hood (which is more or less true according to the source
+-   *   of that library, since it's doing a lookup in RTLD_NEXT)
+-   * - With the LD_PRELOADed library in place, all calls to sigaction from
+-   *   from system libraries will go to the LD_PRELOADed library.
+-   * - The LD_PRELOADed library calls to sigaction go to our __wrap_sigaction.
+-   * - The calls to sigaction from libraries faulty.lib loads are sent to
+-   *   the LD_PRELOADed library.
+-   * In practice, for signal handling, this means:
+-   * - The signal handler registered to the kernel is ours.
+-   * - Our handler redispatches to the LD_PRELOADed library's if there's a
+-   *   segfault we don't handle.
+-   * - The LD_PRELOADed library redispatches according to whatever system
+-   *   library or faulty.lib-loaded library set with sigaction.
+-   *
+-   * When there is no sigaction wrapper in place:
+-   * - Divert the libc's sigaction.
+-   * - Calls to sigaction from system library and faulty.lib-loaded libraries
+-   *   all go to the libc's sigaction, which end up in our __wrap_sigaction.
+-   * - The signal handler registered to the kernel is ours.
+-   * - Our handler redispatches according to whatever system library or
+-   *   faulty.lib-loaded library set with sigaction.
+-   */
+-  void *libc = dlopen("libc.so", RTLD_GLOBAL | RTLD_LAZY);
+-  if (libc) {
+-    /*
+-     * Lollipop bionic only has a small trampoline in sigaction, with the real
+-     * work happening in __sigaction. Divert there instead of sigaction if it exists.
+-     * Bug 1154803
+-     */
+-    libc_sigaction = reinterpret_cast<sigaction_func>(dlsym(libc, "__sigaction"));
+-
+-    if (!libc_sigaction) {
+-      libc_sigaction =
+-        reinterpret_cast<sigaction_func>(dlsym(libc, "sigaction"));
+-    }
+-  } else
+-#endif
+-  {
+-    libc_sigaction = sigaction;
+-  }
+-
+-  if (!Divert(libc_sigaction, __wrap_sigaction))
+-    return;
+-
+-  /* Setup an alternative stack if the already existing one is not big
+-   * enough, or if there is none. */
+-  if (sigaltstack(nullptr, &oldStack) == 0) {
+-    if (oldStack.ss_flags == SS_ONSTACK)
+-      oldStack.ss_flags = 0;
+-    if (!oldStack.ss_sp || oldStack.ss_size < stackSize) {
+-      stackPtr.Assign(MemoryRange::mmap(nullptr, stackSize,
+-                                        PROT_READ | PROT_WRITE,
+-                                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+-      if (stackPtr.get() == MAP_FAILED)
+-        return;
+-      stack_t stack;
+-      stack.ss_sp = stackPtr;
+-      stack.ss_size = stackSize;
+-      stack.ss_flags = 0;
+-      if (sigaltstack(&stack, nullptr) != 0)
+-        return;
+-    }
+-  }
+-  /* Register our own handler, and store the already registered one in
+-   * SEGVHandler's struct sigaction member */
+-  action.sa_sigaction = &SEGVHandler::handler;
+-  action.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
+-  registeredHandler = !sys_sigaction(SIGSEGV, &action, &this->action);
+-}
+-
+-SEGVHandler::~SEGVHandler()
+-{
+-  /* Restore alternative stack for signals */
+-  if (oldStack.ss_flags != SS_ONSTACK)
+-    sigaltstack(&oldStack, nullptr);
+-  /* Restore original signal handler */
+-  if (registeredHandler)
+-    sys_sigaction(SIGSEGV, &this->action, nullptr);
+-}
+-
+-/* Test handler for a deliberately triggered SIGSEGV that determines whether
+- * useful information is provided to signal handlers, particularly whether
+- * si_addr is filled in properly, and whether the segfault handler is called
+- * quickly enough. */
+-void SEGVHandler::test_handler(int signum, siginfo_t *info, void *context)
+-{
+-  SEGVHandler &that = ElfLoader::Singleton;
+-  if (signum == SIGSEGV && info &&
+-      info->si_addr == that.stackPtr.get())
+-    that.signalHandlingBroken = false;
+-  mprotect(that.stackPtr, that.stackPtr.GetLength(), PROT_READ | PROT_WRITE);
+-  TmpData *data = reinterpret_cast<TmpData*>(that.stackPtr.get());
+-  uint64_t latency = ProcessTimeStamp_Now() - data->crash_timestamp;
+-  DEBUG_LOG("SEGVHandler latency: %" PRIu64, latency);
+-  /* See bug 886736 for timings on different devices, 150 µs is reasonably above
+-   * the latency on "working" devices and seems to be short enough to not incur
+-   * a huge overhead to on-demand decompression. */
+-  if (latency <= 150000)
+-    that.signalHandlingSlow = false;
+-}
+-
+-/* TODO: "properly" handle signal masks and flags */
+-void SEGVHandler::handler(int signum, siginfo_t *info, void *context)
+-{
+-  //ASSERT(signum == SIGSEGV);
+-  DEBUG_LOG("Caught segmentation fault @%p", info->si_addr);
+-
+-  /* Redispatch to the registered handler */
+-  SEGVHandler &that = ElfLoader::Singleton;
+-  if (that.action.sa_flags & SA_SIGINFO) {
+-    DEBUG_LOG("Redispatching to registered handler @%p",
+-              FunctionPtr(that.action.sa_sigaction));
+-    that.action.sa_sigaction(signum, info, context);
+-  } else if (that.action.sa_handler == SIG_DFL) {
+-    DEBUG_LOG("Redispatching to default handler");
+-    /* Reset the handler to the default one, and trigger it. */
+-    sys_sigaction(signum, &that.action, nullptr);
+-    raise(signum);
+-  } else if (that.action.sa_handler != SIG_IGN) {
+-    DEBUG_LOG("Redispatching to registered handler @%p",
+-              FunctionPtr(that.action.sa_handler));
+-    that.action.sa_handler(signum);
+-  } else {
+-    DEBUG_LOG("Ignoring");
+-  }
+-}
+-
+-int
+-SEGVHandler::__wrap_sigaction(int signum, const struct sigaction *act,
+-                              struct sigaction *oldact)
+-{
+-  SEGVHandler &that = ElfLoader::Singleton;
+-
+-  /* Use system sigaction() function for all but SIGSEGV signals. */
+-  if (!that.registeredHandler || (signum != SIGSEGV))
+-    return sys_sigaction(signum, act, oldact);
+-
+-  if (oldact)
+-    *oldact = that.action;
+-  if (act)
+-    that.action = *act;
+-  return 0;
+-}
+-
+-Logging Logging::Singleton;
+diff --git a/mozglue/linker/ElfLoader.h b/mozglue/linker/ElfLoader.h
+deleted file mode 100644
+--- a/mozglue/linker/ElfLoader.h
++++ /dev/null
+@@ -1,669 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#ifndef ElfLoader_h
+-#define ElfLoader_h
+-
+-#include <vector>
+-#include <dlfcn.h>
+-#include <signal.h>
+-#include "mozilla/RefCounted.h"
+-#include "mozilla/RefPtr.h"
+-#include "mozilla/UniquePtr.h"
+-#include "Zip.h"
+-#include "Elfxx.h"
+-#include "Mappable.h"
+-
+-/**
+- * dlfcn.h replacement functions
+- */
+-extern "C" {
+-  void *__wrap_dlopen(const char *path, int flags);
+-  const char *__wrap_dlerror(void);
+-  void *__wrap_dlsym(void *handle, const char *symbol);
+-  int __wrap_dlclose(void *handle);
+-
+-#ifndef HAVE_DLADDR
+-  typedef struct {
+-    const char *dli_fname;
+-    void *dli_fbase;
+-    const char *dli_sname;
+-    void *dli_saddr;
+-  } Dl_info;
+-#endif
+-  int __wrap_dladdr(void *addr, Dl_info *info);
+-
+-  struct dl_phdr_info {
+-    Elf::Addr dlpi_addr;
+-    const char *dlpi_name;
+-    const Elf::Phdr *dlpi_phdr;
+-    Elf::Half dlpi_phnum;
+-  };
+-
+-  typedef int (*dl_phdr_cb)(struct dl_phdr_info *, size_t, void *);
+-  int __wrap_dl_iterate_phdr(dl_phdr_cb callback, void *data);
+-
+-#ifdef __ARM_EABI__
+-  const void *__wrap___gnu_Unwind_Find_exidx(void *pc, int *pcount);
+-#endif
+-
+-/**
+- * faulty.lib public API
+- */
+-MFBT_API size_t
+-__dl_get_mappable_length(void *handle);
+-
+-MFBT_API void *
+-__dl_mmap(void *handle, void *addr, size_t length, off_t offset);
+-
+-MFBT_API void
+-__dl_munmap(void *handle, void *addr, size_t length);
+-
+-MFBT_API bool
+-IsSignalHandlingBroken();
+-
+-}
+-
+-/* Forward declarations for use in LibHandle */
+-class BaseElf;
+-class CustomElf;
+-class SystemElf;
+-
+-/**
+- * Specialize RefCounted template for LibHandle. We may get references to
+- * LibHandles during the execution of their destructor, so we need
+- * RefCounted<LibHandle>::Release to support some reentrancy. See further
+- * below.
+- */
+-class LibHandle;
+-
+-namespace mozilla {
+-namespace detail {
+-
+-template <> inline void RefCounted<LibHandle, AtomicRefCount>::Release() const;
+-
+-template <> inline RefCounted<LibHandle, AtomicRefCount>::~RefCounted()
+-{
+-  MOZ_ASSERT(mRefCnt == 0x7fffdead);
+-}
+-
+-} /* namespace detail */
+-} /* namespace mozilla */
+-
+-/**
+- * Abstract class for loaded libraries. Libraries may be loaded through the
+- * system linker or this linker, both cases will be derived from this class.
+- */
+-class LibHandle: public mozilla::external::AtomicRefCounted<LibHandle>
+-{
+-public:
+-  MOZ_DECLARE_REFCOUNTED_TYPENAME(LibHandle)
+-  /**
+-   * Constructor. Takes the path of the loaded library and will store a copy
+-   * of the leaf name.
+-   */
+-  LibHandle(const char *path)
+-  : directRefCnt(0), path(path ? strdup(path) : nullptr), mappable(nullptr)
+-  {
+-  }
+-
+-  /**
+-   * Destructor.
+-   */
+-  virtual ~LibHandle();
+-
+-  /**
+-   * Returns the pointer to the address to which the given symbol resolves
+-   * inside the library. It is not supposed to resolve the symbol in other
+-   * libraries, although in practice, it will for system libraries.
+-   */
+-  virtual void *GetSymbolPtr(const char *symbol) const = 0;
+-
+-  /**
+-   * Returns whether the given address is part of the virtual address space
+-   * covered by the loaded library.
+-   */
+-  virtual bool Contains(void *addr) const = 0;
+-
+-  /**
+-   * Returns the base address of the loaded library.
+-   */
+-  virtual void *GetBase() const = 0;
+-
+-  /**
+-   * Returns the file name of the library without the containing directory.
+-   */
+-  const char *GetName() const;
+-
+-  /**
+-   * Returns the full path of the library, when available. Otherwise, returns
+-   * the file name.
+-   */
+-  const char *GetPath() const
+-  {
+-    return path;
+-  }
+-
+-  /**
+-   * Library handles can be referenced from other library handles or
+-   * externally (when dlopen()ing using this linker). We need to be
+-   * able to distinguish between the two kind of referencing for better
+-   * bookkeeping.
+-   */
+-  void AddDirectRef()
+-  {
+-    mozilla::external::AtomicRefCounted<LibHandle>::AddRef();
+-    ++directRefCnt;
+-  }
+-
+-  /**
+-   * Releases a direct reference, and returns whether there are any direct
+-   * references left.
+-   */
+-  bool ReleaseDirectRef()
+-  {
+-    const MozRefCountType count = --directRefCnt;
+-    MOZ_ASSERT(count + 1 > 0);
+-    MOZ_ASSERT(count + 1 <=
+-               mozilla::external::AtomicRefCounted<LibHandle>::refCount());
+-    mozilla::external::AtomicRefCounted<LibHandle>::Release();
+-    return !!count;
+-  }
+-
+-  /**
+-   * Returns the number of direct references
+-   */
+-  MozRefCountType DirectRefCount()
+-  {
+-    return directRefCnt;
+-  }
+-
+-  /**
+-   * Returns the complete size of the file or stream behind the library
+-   * handle.
+-   */
+-  size_t GetMappableLength() const;
+-
+-  /**
+-   * Returns a memory mapping of the file or stream behind the library
+-   * handle.
+-   */
+-  void *MappableMMap(void *addr, size_t length, off_t offset) const;
+-
+-  /**
+-   * Unmaps a memory mapping of the file or stream behind the library
+-   * handle.
+-   */
+-  void MappableMUnmap(void *addr, size_t length) const;
+-
+-#ifdef __ARM_EABI__
+-  /**
+-   * Find the address and entry count of the ARM.exidx section
+-   * associated with the library
+-   */
+-  virtual const void *FindExidx(int *pcount) const = 0;
+-#endif
+-
+-protected:
+-  /**
+-   * Returns a mappable object for use by MappableMMap and related functions.
+-   */
+-  virtual Mappable *GetMappable() const = 0;
+-
+-  /**
+-   * Returns the instance, casted as the wanted type. Returns nullptr if
+-   * that's not the actual type. (short of a better way to do this without
+-   * RTTI)
+-   */
+-  friend class ElfLoader;
+-  friend class CustomElf;
+-  friend class SEGVHandler;
+-  virtual BaseElf *AsBaseElf() { return nullptr; }
+-  virtual SystemElf *AsSystemElf() { return nullptr; }
+-
+-private:
+-  mozilla::Atomic<MozRefCountType> directRefCnt;
+-  char *path;
+-
+-  /* Mappable object keeping the result of GetMappable() */
+-  mutable RefPtr<Mappable> mappable;
+-};
+-
+-/**
+- * Specialized RefCounted<LibHandle>::Release. Under normal operation, when
+- * mRefCnt reaches 0, the LibHandle is deleted. Its mRefCnt is however
+- * increased to 1 on normal builds, and 0x7fffdead on debug builds so that the
+- * LibHandle can still be referenced while the destructor is executing. The
+- * mRefCnt is allowed to grow > 0x7fffdead, but not to decrease under that
+- * value, which would mean too many Releases from within the destructor.
+- */
+-namespace mozilla {
+-namespace detail {
+-
+-template <> inline void RefCounted<LibHandle, AtomicRefCount>::Release() const {
+-#ifdef DEBUG
+-  if (mRefCnt > 0x7fff0000)
+-    MOZ_ASSERT(mRefCnt > 0x7fffdead);
+-#endif
+-  MOZ_ASSERT(mRefCnt > 0);
+-  if (mRefCnt > 0) {
+-    if (0 == --mRefCnt) {
+-#ifdef DEBUG
+-      mRefCnt = 0x7fffdead;
+-#else
+-      mRefCnt = 1;
+-#endif
+-      delete static_cast<const LibHandle*>(this);
+-    }
+-  }
+-}
+-
+-} /* namespace detail */
+-} /* namespace mozilla */
+-
+-/**
+- * Class handling libraries loaded by the system linker
+- */
+-class SystemElf: public LibHandle
+-{
+-public:
+-  /**
+-   * Returns a new SystemElf for the given path. The given flags are passed
+-   * to dlopen().
+-   */
+-  static already_AddRefed<LibHandle> Load(const char *path, int flags);
+-
+-  /**
+-   * Inherited from LibHandle
+-   */
+-  virtual ~SystemElf();
+-  virtual void *GetSymbolPtr(const char *symbol) const;
+-  virtual bool Contains(void *addr) const { return false; /* UNIMPLEMENTED */ }
+-  virtual void *GetBase() const { return nullptr; /* UNIMPLEMENTED */ }
+-
+-#ifdef __ARM_EABI__
+-  virtual const void *FindExidx(int *pcount) const;
+-#endif
+-
+-protected:
+-  virtual Mappable *GetMappable() const;
+-
+-  /**
+-   * Returns the instance, casted as SystemElf. (short of a better way to do
+-   * this without RTTI)
+-   */
+-  friend class ElfLoader;
+-  virtual SystemElf *AsSystemElf() { return this; }
+-
+-  /**
+-   * Remove the reference to the system linker handle. This avoids dlclose()
+-   * being called when the instance is destroyed.
+-   */
+-  void Forget()
+-  {
+-    dlhandle = nullptr;
+-  }
+-
+-private:
+-  /**
+-   * Private constructor
+-   */
+-  SystemElf(const char *path, void *handle)
+-  : LibHandle(path), dlhandle(handle) { }
+-
+-  /* Handle as returned by system dlopen() */
+-  void *dlhandle;
+-};
+-
+-/**
+- * The ElfLoader registers its own SIGSEGV handler to handle segmentation
+- * faults within the address space of the loaded libraries. It however
+- * allows a handler to be set for faults in other places, and redispatches
+- * to the handler set through signal() or sigaction().
+- */
+-class SEGVHandler
+-{
+-public:
+-  bool hasRegisteredHandler() {
+-    if (! initialized)
+-      FinishInitialization();
+-    return registeredHandler;
+-  }
+-
+-  bool isSignalHandlingBroken() {
+-    return signalHandlingBroken;
+-  }
+-
+-  static int __wrap_sigaction(int signum, const struct sigaction *act,
+-                              struct sigaction *oldact);
+-
+-protected:
+-  SEGVHandler();
+-  ~SEGVHandler();
+-
+-private:
+-
+-  /**
+-   * The constructor doesn't do all initialization, and the tail is done
+-   * at a later time.
+-   */
+-  void FinishInitialization();
+-
+-  /**
+-   * SIGSEGV handler registered with __wrap_signal or __wrap_sigaction.
+-   */
+-  struct sigaction action;
+-
+-  /**
+-   * ElfLoader SIGSEGV handler.
+-   */
+-  static void handler(int signum, siginfo_t *info, void *context);
+-
+-  /**
+-   * Temporary test handler.
+-   */
+-  static void test_handler(int signum, siginfo_t *info, void *context);
+-
+-  /**
+-   * Size of the alternative stack. The printf family requires more than 8KB
+-   * of stack, and our signal handler may print a few things.
+-   */
+-  static const size_t stackSize = 12 * 1024;
+-
+-  /**
+-   * Alternative stack information used before initialization.
+-   */
+-  stack_t oldStack;
+-
+-  /**
+-   * Pointer to an alternative stack for signals. Only set if oldStack is
+-   * not set or not big enough.
+-   */
+-  MappedPtr stackPtr;
+-
+-  bool initialized;
+-  bool registeredHandler;
+-  bool signalHandlingBroken;
+-  bool signalHandlingSlow;
+-};
+-
+-/**
+- * Elf Loader class in charge of loading and bookkeeping libraries.
+- */
+-class ElfLoader: public SEGVHandler
+-{
+-public:
+-  /**
+-   * The Elf Loader instance
+-   */
+-  static ElfLoader Singleton;
+-
+-  /**
+-   * Loads the given library with the given flags. Equivalent to dlopen()
+-   * The extra "parent" argument optionally gives the handle of the library
+-   * requesting the given library to be loaded. The loader may look in the
+-   * directory containing that parent library for the library to load.
+-   */
+-  already_AddRefed<LibHandle> Load(const char *path, int flags,
+-                                        LibHandle *parent = nullptr);
+-
+-  /**
+-   * Returns the handle of the library containing the given address in
+-   * its virtual address space, i.e. the library handle for which
+-   * LibHandle::Contains returns true. Its purpose is to allow to
+-   * implement dladdr().
+-   */
+-  already_AddRefed<LibHandle> GetHandleByPtr(void *addr);
+-
+-  /**
+-   * Returns a Mappable object for the path. Paths in the form
+-   *   /foo/bar/baz/archive!/directory/lib.so
+-   * try to load the directory/lib.so in /foo/bar/baz/archive, provided
+-   * that file is a Zip archive.
+-   */
+-  static Mappable *GetMappableFromPath(const char *path);
+-
+-  void ExpectShutdown(bool val) { expect_shutdown = val; }
+-  bool IsShutdownExpected() { return expect_shutdown; }
+-
+-private:
+-  bool expect_shutdown;
+-
+-protected:
+-  /**
+-   * Registers the given handle. This method is meant to be called by
+-   * LibHandle subclass creators.
+-   */
+-  void Register(LibHandle *handle);
+-  void Register(CustomElf *handle);
+-
+-  /**
+-   * Forget about the given handle. This method is meant to be called by
+-   * LibHandle subclass destructors.
+-   */
+-  void Forget(LibHandle *handle);
+-  void Forget(CustomElf *handle);
+-
+-  /* Last error. Used for dlerror() */
+-  friend class SystemElf;
+-  friend const char *__wrap_dlerror(void);
+-  friend void *__wrap_dlsym(void *handle, const char *symbol);
+-  friend int __wrap_dlclose(void *handle);
+-  const char *lastError;
+-
+-private:
+-  ElfLoader() : expect_shutdown(true)
+-  {
+-    pthread_mutex_init(&handlesMutex, nullptr);
+-  }
+-
+-  ~ElfLoader();
+-
+-  /* Initialization code that can't run during static initialization. */
+-  void Init();
+-
+-  /* System loader handle for the library/program containing our code. This
+-   * is used to resolve wrapped functions. */
+-  RefPtr<LibHandle> self_elf;
+-
+-#if defined(ANDROID)
+-  /* System loader handle for the libc. This is used to resolve weak symbols
+-   * that some libcs contain that the Android linker won't dlsym(). Normally,
+-   * we wouldn't treat non-Android differently, but glibc uses versioned
+-   * symbols which this linker doesn't support. */
+-  RefPtr<LibHandle> libc;
+-
+-  /* And for libm. */
+-  RefPtr<LibHandle> libm;
+-#endif
+-
+-  /* Bookkeeping */
+-  typedef std::vector<LibHandle *> LibHandleList;
+-  LibHandleList handles;
+-
+-  pthread_mutex_t handlesMutex;
+-
+-protected:
+-  friend class CustomElf;
+-  friend class LoadedElf;
+-
+-  /* Definition of static destructors as to be used for C++ ABI compatibility */
+-  typedef void (*Destructor)(void *object);
+-
+-  /**
+-   * C++ ABI makes static initializers register destructors through a specific
+-   * atexit interface. On glibc/linux systems, the dso_handle is a pointer
+-   * within a given library. On bionic/android systems, it is an undefined
+-   * symbol. Making sense of the value is not really important, and all that
+-   * is really important is that it is different for each loaded library, so
+-   * that they can be discriminated when shutting down. For convenience, on
+-   * systems where the dso handle is a symbol, that symbol is resolved to
+-   * point at corresponding CustomElf.
+-   *
+-   * Destructors are registered with __*_atexit with an associated object to
+-   * be passed as argument when it is called.
+-   *
+-   * When __cxa_finalize is called, destructors registered for the given
+-   * DSO handle are called in the reverse order they were registered.
+-   */
+-#ifdef __ARM_EABI__
+-  static int __wrap_aeabi_atexit(void *that, Destructor destructor,
+-                                 void *dso_handle);
+-#else
+-  static int __wrap_cxa_atexit(Destructor destructor, void *that,
+-                               void *dso_handle);
+-#endif
+-
+-  static void __wrap_cxa_finalize(void *dso_handle);
+-
+-  /**
+-   * Registered destructor. Keeps track of the destructor function pointer,
+-   * associated object to call it with, and DSO handle.
+-   */
+-  class DestructorCaller {
+-  public:
+-    DestructorCaller(Destructor destructor, void *object, void *dso_handle)
+-    : destructor(destructor), object(object), dso_handle(dso_handle) { }
+-
+-    /**
+-     * Call the destructor function with the associated object.
+-     * Call only once, see CustomElf::~CustomElf.
+-     */
+-    void Call();
+-
+-    /**
+-     * Returns whether the destructor is associated to the given DSO handle
+-     */
+-    bool IsForHandle(void *handle) const
+-    {
+-      return handle == dso_handle;
+-    }
+-
+-  private:
+-    Destructor destructor;
+-    void *object;
+-    void *dso_handle;
+-  };
+-
+-private:
+-  /* Keep track of all registered destructors */
+-  std::vector<DestructorCaller> destructors;
+-
+-  /* Forward declaration, see further below */
+-  class DebuggerHelper;
+-public:
+-  /* Loaded object descriptor for the debugger interface below*/
+-  struct link_map {
+-    /* Base address of the loaded object. */
+-    const void *l_addr;
+-    /* File name */
+-    const char *l_name;
+-    /* Address of the PT_DYNAMIC segment. */
+-    const void *l_ld;
+-
+-  private:
+-    friend class ElfLoader::DebuggerHelper;
+-    /* Double linked list of loaded objects. */
+-    link_map *l_next, *l_prev;
+-  };
+-
+-private:
+-  /* Data structure used by the linker to give details about shared objects it
+-   * loaded to debuggers. This is normally defined in link.h, but Android
+-   * headers lack this file. */
+-  struct r_debug {
+-    /* Version number of the protocol. */
+-    int r_version;
+-
+-    /* Head of the linked list of loaded objects. */
+-    link_map *r_map;
+-
+-    /* Function to be called when updates to the linked list of loaded objects
+-     * are going to occur. The function is to be called before and after
+-     * changes. */
+-    void (*r_brk)(void);
+-
+-    /* Indicates to the debugger what state the linked list of loaded objects
+-     * is in when the function above is called. */
+-    enum {
+-      RT_CONSISTENT, /* Changes are complete */
+-      RT_ADD,        /* Beginning to add a new object */
+-      RT_DELETE      /* Beginning to remove an object */
+-    } r_state;
+-  };
+-
+-  /* Memory representation of ELF Auxiliary Vectors */
+-  struct AuxVector {
+-    Elf::Addr type;
+-    Elf::Addr value;
+-  };
+-
+-  /* Helper class used to integrate libraries loaded by this linker in
+-   * r_debug */
+-  class DebuggerHelper
+-  {
+-  public:
+-    DebuggerHelper();
+-
+-    void Init(AuxVector *auvx);
+-
+-    operator bool()
+-    {
+-      return dbg;
+-    }
+-
+-    /* Make the debugger aware of a new loaded object */
+-    void Add(link_map *map);
+-
+-    /* Make the debugger aware of the unloading of an object */
+-    void Remove(link_map *map);
+-
+-    /* Iterates over all link_maps */
+-    class iterator
+-    {
+-    public:
+-      const link_map *operator ->() const
+-      {
+-        return item;
+-      }
+-
+-      const link_map &operator ++()
+-      {
+-        item = item->l_next;
+-        return *item;
+-      }
+-
+-      bool operator<(const iterator &other) const
+-      {
+-        if (other.item == nullptr)
+-          return item ? true : false;
+-        MOZ_CRASH("DebuggerHelper::iterator::operator< called with something else than DebuggerHelper::end()");
+-      }
+-    protected:
+-      friend class DebuggerHelper;
+-      iterator(const link_map *item): item(item) { }
+-
+-    private:
+-      const link_map *item;
+-    };
+-
+-    iterator begin() const
+-    {
+-      return iterator(dbg ? dbg->r_map : nullptr);
+-    }
+-
+-    iterator end() const
+-    {
+-      return iterator(nullptr);
+-    }
+-
+-  private:
+-    r_debug *dbg;
+-    link_map *firstAdded;
+-  };
+-  friend int __wrap_dl_iterate_phdr(dl_phdr_cb callback, void *data);
+-  DebuggerHelper dbg;
+-};
+-
+-#endif /* ElfLoader_h */
+diff --git a/mozglue/linker/Elfxx.h b/mozglue/linker/Elfxx.h
+deleted file mode 100644
+--- a/mozglue/linker/Elfxx.h
++++ /dev/null
+@@ -1,253 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#ifndef Elfxx_h
+-#define Elfxx_h
+-
+-/**
+- * Android system headers have two different elf.h file. The one under linux/
+- * is the most complete on older Android API versions without unified headers.
+- */
+-#if defined(ANDROID) && __ANDROID_API__ < 21 && !defined(__ANDROID_API_L__)
+-#include <linux/elf.h>
+-#else
+-#include <elf.h>
+-#endif
+-#include <endian.h>
+-
+-#if defined(__ARM_EABI__) && !defined(PT_ARM_EXIDX)
+-#define PT_ARM_EXIDX 0x70000001
+-#endif
+-
+-/**
+- * Generic ELF macros for the target system
+- */
+-#ifdef __LP64__
+-#define Elf_(type) Elf64_ ## type
+-#define ELFCLASS ELFCLASS64
+-#define ELF_R_TYPE ELF64_R_TYPE
+-#define ELF_R_SYM ELF64_R_SYM
+-#ifndef ELF_ST_BIND
+-#define ELF_ST_BIND ELF64_ST_BIND
+-#endif
+-#else
+-#define Elf_(type) Elf32_ ## type
+-#define ELFCLASS ELFCLASS32
+-#define ELF_R_TYPE ELF32_R_TYPE
+-#define ELF_R_SYM ELF32_R_SYM
+-#ifndef ELF_ST_BIND
+-#define ELF_ST_BIND ELF32_ST_BIND
+-#endif
+-#endif
+-
+-#ifndef __BYTE_ORDER
+-#error Cannot find endianness
+-#endif
+-
+-#if __BYTE_ORDER == __LITTLE_ENDIAN
+-#define ELFDATA ELFDATA2LSB
+-#elif __BYTE_ORDER == __BIG_ENDIAN
+-#define ELFDATA ELFDATA2MSB
+-#endif
+-
+-#ifdef __linux__
+-#define ELFOSABI ELFOSABI_LINUX
+-#ifdef EI_ABIVERSION
+-#define ELFABIVERSION 0
+-#endif
+-#else
+-#error Unknown ELF OSABI
+-#endif
+-
+-#if defined(__i386__)
+-#define ELFMACHINE EM_386
+-
+-// Doing this way probably doesn't scale to other architectures
+-#define R_ABS R_386_32
+-#define R_GLOB_DAT R_386_GLOB_DAT
+-#define R_JMP_SLOT R_386_JMP_SLOT
+-#define R_RELATIVE R_386_RELATIVE
+-#define RELOC(n) DT_REL ## n
+-#define UNSUPPORTED_RELOC(n) DT_RELA ## n
+-#define STR_RELOC(n) "DT_REL" # n
+-#define Reloc Rel
+-
+-#elif defined(__x86_64__)
+-#define ELFMACHINE EM_X86_64
+-
+-#define R_ABS R_X86_64_64
+-#define R_GLOB_DAT R_X86_64_GLOB_DAT
+-#define R_JMP_SLOT R_X86_64_JUMP_SLOT
+-#define R_RELATIVE R_X86_64_RELATIVE
+-#define RELOC(n) DT_RELA ## n
+-#define UNSUPPORTED_RELOC(n) DT_REL ## n
+-#define STR_RELOC(n) "DT_RELA" # n
+-#define Reloc Rela
+-
+-#elif defined(__arm__)
+-#define ELFMACHINE EM_ARM
+-
+-#ifndef R_ARM_ABS32
+-#define R_ARM_ABS32 2
+-#endif
+-#ifndef R_ARM_GLOB_DAT
+-#define R_ARM_GLOB_DAT 21
+-#endif
+-#ifndef R_ARM_JUMP_SLOT
+-#define R_ARM_JUMP_SLOT 22
+-#endif
+-#ifndef R_ARM_RELATIVE
+-#define R_ARM_RELATIVE 23
+-#endif
+-
+-#define R_ABS R_ARM_ABS32
+-#define R_GLOB_DAT R_ARM_GLOB_DAT
+-#define R_JMP_SLOT R_ARM_JUMP_SLOT
+-#define R_RELATIVE R_ARM_RELATIVE
+-#define RELOC(n) DT_REL ## n
+-#define UNSUPPORTED_RELOC(n) DT_RELA ## n
+-#define STR_RELOC(n) "DT_REL" # n
+-#define Reloc Rel
+-
+-#elif defined(__aarch64__)
+-#define ELFMACHINE EM_AARCH64
+-
+-#define R_ABS R_AARCH64_ABS64
+-#define R_GLOB_DAT R_AARCH64_GLOB_DAT
+-#define R_JMP_SLOT R_AARCH64_JUMP_SLOT
+-#define R_RELATIVE R_AARCH64_RELATIVE
+-#define RELOC(n) DT_RELA ## n
+-#define UNSUPPORTED_RELOC(n) DT_REL ## n
+-#define STR_RELOC(n) "DT_RELA" # n
+-#define Reloc Rela
+-
+-#else
+-#error Unknown ELF machine type
+-#endif
+-
+-/**
+- * Android system headers don't have all definitions
+- */
+-#ifndef STN_UNDEF
+-#define STN_UNDEF 0
+-#endif
+-#ifndef DT_INIT_ARRAY
+-#define DT_INIT_ARRAY 25
+-#endif
+-#ifndef DT_FINI_ARRAY
+-#define DT_FINI_ARRAY 26
+-#endif
+-#ifndef DT_INIT_ARRAYSZ
+-#define DT_INIT_ARRAYSZ 27
+-#endif
+-#ifndef DT_FINI_ARRAYSZ
+-#define DT_FINI_ARRAYSZ 28
+-#endif
+-#ifndef DT_RELACOUNT
+-#define DT_RELACOUNT 0x6ffffff9
+-#endif
+-#ifndef DT_RELCOUNT
+-#define DT_RELCOUNT 0x6ffffffa
+-#endif
+-#ifndef DT_VERSYM
+-#define DT_VERSYM 0x6ffffff0
+-#endif
+-#ifndef DT_VERDEF
+-#define DT_VERDEF 0x6ffffffc
+-#endif
+-#ifndef DT_VERDEFNUM
+-#define DT_VERDEFNUM 0x6ffffffd
+-#endif
+-#ifndef DT_VERNEED
+-#define DT_VERNEED 0x6ffffffe
+-#endif
+-#ifndef DT_VERNEEDNUM
+-#define DT_VERNEEDNUM 0x6fffffff
+-#endif
+-#ifndef DT_FLAGS_1
+-#define DT_FLAGS_1 0x6ffffffb
+-#endif
+-#ifndef DT_FLAGS
+-#define DT_FLAGS 30
+-#endif
+-#ifndef DF_SYMBOLIC
+-#define DF_SYMBOLIC 0x00000002
+-#endif
+-#ifndef DF_TEXTREL
+-#define DF_TEXTREL 0x00000004
+-#endif
+-
+-namespace Elf {
+-
+-/**
+- * Define a few basic Elf Types
+- */
+-typedef Elf_(Phdr) Phdr;
+-typedef Elf_(Dyn) Dyn;
+-typedef Elf_(Sym) Sym;
+-typedef Elf_(Addr) Addr;
+-typedef Elf_(Word) Word;
+-typedef Elf_(Half) Half;
+-
+-/**
+- * Helper class around the standard Elf header struct
+- */
+-struct Ehdr: public Elf_(Ehdr)
+-{
+-  /**
+-   * Equivalent to reinterpret_cast<const Ehdr *>(buf), but additionally
+-   * checking that this is indeed an Elf header and that the Elf type
+-   * corresponds to that of the system
+-   */
+-  static const Ehdr *validate(const void *buf);
+-};
+-
+-/**
+- * Elf String table
+- */
+-class Strtab: public UnsizedArray<const char>
+-{
+-public:
+-  /**
+-   * Returns the string at the given index in the table
+-   */
+-  const char *GetStringAt(off_t index) const
+-  {
+-    return &UnsizedArray<const char>::operator[](index);
+-  }
+-};
+-
+-/**
+- * Helper class around Elf relocation.
+- */
+-struct Rel: public Elf_(Rel)
+-{
+-  /**
+-   * Returns the addend for the relocation, which is the value stored
+-   * at r_offset.
+-   */
+-  Addr GetAddend(void *base) const
+-  {
+-    return *(reinterpret_cast<const Addr *>(
+-                   reinterpret_cast<const char *>(base) + r_offset));
+-  }
+-};
+-
+-/**
+- * Helper class around Elf relocation with addend.
+- */
+-struct Rela: public Elf_(Rela)
+-{
+-  /**
+-   * Returns the addend for the relocation.
+-   */
+-  Addr GetAddend(void *base) const
+-  {
+-    return r_addend;
+-  }
+-};
+-
+-} /* namespace Elf */
+-
+-#endif /* Elfxx_h */
+diff --git a/mozglue/linker/Logging.h b/mozglue/linker/Logging.h
+deleted file mode 100644
+--- a/mozglue/linker/Logging.h
++++ /dev/null
+@@ -1,75 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#ifndef Logging_h
+-#define Logging_h
+-
+-#include "mozilla/Likely.h"
+-
+-#ifdef ANDROID
+-#include <android/log.h>
+-#define LOG(...) __android_log_print(ANDROID_LOG_INFO, "GeckoLinker", __VA_ARGS__)
+-#define WARN(...) __android_log_print(ANDROID_LOG_WARN, "GeckoLinker", __VA_ARGS__)
+-#define ERROR(...) __android_log_print(ANDROID_LOG_ERROR, "GeckoLinker", __VA_ARGS__)
+-#else
+-#include <cstdio>
+-
+-/* Expand to 1 or m depending on whether there is one argument or more
+- * given. */
+-#define MOZ_ONE_OR_MORE_ARGS_IMPL2(_1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) \
+-  N
+-#define MOZ_ONE_OR_MORE_ARGS_IMPL(args) MOZ_ONE_OR_MORE_ARGS_IMPL2 args
+-#define MOZ_ONE_OR_MORE_ARGS(...) \
+-  MOZ_ONE_OR_MORE_ARGS_IMPL((__VA_ARGS__, m, m, m, m, m, m, m, m, 1, 0))
+-
+-#define MOZ_MACRO_GLUE(a, b) a b
+-#define MOZ_CONCAT2(a, b) a ## b
+-#define MOZ_CONCAT1(a, b) MOZ_CONCAT2(a, b)
+-#define MOZ_CONCAT(a, b) MOZ_CONCAT1(a, b)
+-
+-/* Some magic to choose between LOG1 and LOGm depending on the number of
+- * arguments */
+-#define MOZ_CHOOSE_LOG(...) \
+-  MOZ_MACRO_GLUE(MOZ_CONCAT(LOG, MOZ_ONE_OR_MORE_ARGS(__VA_ARGS__)), \
+-                 (__VA_ARGS__))
+-
+-#define LOG1(format) fprintf(stderr, format "\n")
+-#define LOGm(format, ...) fprintf(stderr, format "\n", __VA_ARGS__)
+-#define LOG(...) MOZ_CHOOSE_LOG(__VA_ARGS__)
+-#define WARN(...) MOZ_CHOOSE_LOG("Warning: " __VA_ARGS__)
+-#define ERROR(...) MOZ_CHOOSE_LOG("Error: " __VA_ARGS__)
+-
+-#endif
+-
+-class Logging
+-{
+-public:
+-  static bool isVerbose()
+-  {
+-    return Singleton.verbose;
+-  }
+-
+-private:
+-  bool verbose;
+-
+-public:
+-  static void Init()
+-  {
+-    const char *env = getenv("MOZ_DEBUG_LINKER");
+-    if (env && *env == '1')
+-      Singleton.verbose = true;
+-  }
+-
+-private:
+-  static Logging Singleton;
+-};
+-
+-#define DEBUG_LOG(...)   \
+-  do {                   \
+-    if (MOZ_UNLIKELY(Logging::isVerbose())) {  \
+-      LOG(__VA_ARGS__);  \
+-    }                    \
+-  } while(0)
+-
+-#endif /* Logging_h */
+diff --git a/mozglue/linker/Mappable.cpp b/mozglue/linker/Mappable.cpp
+deleted file mode 100644
+--- a/mozglue/linker/Mappable.cpp
++++ /dev/null
+@@ -1,454 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#include <fcntl.h>
+-#include <unistd.h>
+-#include <sys/mman.h>
+-#include <sys/stat.h>
+-#include <cstring>
+-#include <cstdlib>
+-#include <cstdio>
+-#include <string>
+-
+-#include "Mappable.h"
+-
+-#include "mozilla/IntegerPrintfMacros.h"
+-#include "mozilla/UniquePtr.h"
+-
+-#ifdef ANDROID
+-#include <linux/ashmem.h>
+-#endif
+-#include <sys/stat.h>
+-#include <errno.h>
+-#include "ElfLoader.h"
+-#include "XZStream.h"
+-#include "Logging.h"
+-
+-using mozilla::MakeUnique;
+-using mozilla::UniquePtr;
+-
+-class CacheValidator
+-{
+-public:
+-  CacheValidator(const char* aCachedLibPath, Zip* aZip, Zip::Stream* aStream)
+-    : mCachedLibPath(aCachedLibPath)
+-  {
+-    static const char kChecksumSuffix[] = ".crc";
+-
+-    mCachedChecksumPath =
+-      MakeUnique<char[]>(strlen(aCachedLibPath) + sizeof(kChecksumSuffix));
+-    sprintf(mCachedChecksumPath.get(), "%s%s", aCachedLibPath, kChecksumSuffix);
+-    DEBUG_LOG("mCachedChecksumPath: %s", mCachedChecksumPath.get());
+-
+-    mChecksum = aStream->GetCRC32();
+-    DEBUG_LOG("mChecksum: %x", mChecksum);
+-  }
+-
+-  // Returns whether the cache is valid and up-to-date.
+-  bool IsValid() const
+-  {
+-    // Validate based on checksum.
+-    RefPtr<Mappable> checksumMap = MappableFile::Create(mCachedChecksumPath.get());
+-    if (!checksumMap) {
+-      // Force caching if checksum is missing in cache.
+-      return false;
+-    }
+-
+-    DEBUG_LOG("Comparing %x with %s", mChecksum, mCachedChecksumPath.get());
+-    MappedPtr checksumBuf = checksumMap->mmap(nullptr, checksumMap->GetLength(),
+-                                              PROT_READ, MAP_PRIVATE, 0);
+-    if (checksumBuf == MAP_FAILED) {
+-      WARN("Couldn't map %s to validate checksum", mCachedChecksumPath.get());
+-      return false;
+-    }
+-    if (memcmp(checksumBuf, &mChecksum, sizeof(mChecksum))) {
+-      return false;
+-    }
+-    return !access(mCachedLibPath.c_str(), R_OK);
+-  }
+-
+-  // Caches the APK-provided checksum used in future cache validations.
+-  void CacheChecksum() const
+-  {
+-    AutoCloseFD fd(open(mCachedChecksumPath.get(),
+-                        O_TRUNC | O_RDWR | O_CREAT | O_NOATIME,
+-                        S_IRUSR | S_IWUSR));
+-    if (fd == -1) {
+-      WARN("Couldn't open %s to update checksum", mCachedChecksumPath.get());
+-      return;
+-    }
+-
+-    DEBUG_LOG("Updating checksum %s", mCachedChecksumPath.get());
+-
+-    const size_t size = sizeof(mChecksum);
+-    size_t written = 0;
+-    while (written < size) {
+-      ssize_t ret = write(fd,
+-                          reinterpret_cast<const uint8_t*>(&mChecksum) + written,
+-                          size - written);
+-      if (ret >= 0) {
+-        written += ret;
+-      } else if (errno != EINTR) {
+-        WARN("Writing checksum %s failed with errno %d",
+-             mCachedChecksumPath.get(), errno);
+-        break;
+-      }
+-    }
+-  }
+-
+-private:
+-  const std::string mCachedLibPath;
+-  UniquePtr<char[]> mCachedChecksumPath;
+-  uint32_t mChecksum;
+-};
+-
+-Mappable *
+-MappableFile::Create(const char *path)
+-{
+-  int fd = open(path, O_RDONLY);
+-  if (fd != -1)
+-    return new MappableFile(fd);
+-  return nullptr;
+-}
+-
+-MemoryRange
+-MappableFile::mmap(const void *addr, size_t length, int prot, int flags,
+-                   off_t offset)
+-{
+-  MOZ_ASSERT(fd != -1);
+-  MOZ_ASSERT(!(flags & MAP_SHARED));
+-  flags |= MAP_PRIVATE;
+-
+-  return MemoryRange::mmap(const_cast<void *>(addr), length, prot, flags,
+-                           fd, offset);
+-}
+-
+-void
+-MappableFile::finalize()
+-{
+-  /* Close file ; equivalent to close(fd.forget()) */
+-  fd = -1;
+-}
+-
+-size_t
+-MappableFile::GetLength() const
+-{
+-  struct stat st;
+-  return fstat(fd, &st) ? 0 : st.st_size;
+-}
+-
+-Mappable *
+-MappableExtractFile::Create(const char *name, Zip *zip, Zip::Stream *stream)
+-{
+-  MOZ_ASSERT(zip && stream);
+-
+-  const char *cachePath = getenv("MOZ_LINKER_CACHE");
+-  if (!cachePath || !*cachePath) {
+-    WARN("MOZ_LINKER_EXTRACT is set, but not MOZ_LINKER_CACHE; "
+-        "not extracting");
+-    return nullptr;
+-  }
+-
+-  // Ensure that the cache dir is private.
+-  chmod(cachePath, 0770);
+-
+-  UniquePtr<char[]> path =
+-    MakeUnique<char[]>(strlen(cachePath) + strlen(name) + 2);
+-  sprintf(path.get(), "%s/%s", cachePath, name);
+-
+-  CacheValidator validator(path.get(), zip, stream);
+-  if (validator.IsValid()) {
+-    DEBUG_LOG("Reusing %s", static_cast<char *>(path.get()));
+-    return MappableFile::Create(path.get());
+-  }
+-  DEBUG_LOG("Extracting to %s", static_cast<char *>(path.get()));
+-  AutoCloseFD fd;
+-  fd = open(path.get(), O_TRUNC | O_RDWR | O_CREAT | O_NOATIME,
+-                        S_IRUSR | S_IWUSR);
+-  if (fd == -1) {
+-    ERROR("Couldn't open %s to decompress library", path.get());
+-    return nullptr;
+-  }
+-  AutoUnlinkFile file(path.release());
+-  if (stream->GetType() == Zip::Stream::DEFLATE) {
+-    if (ftruncate(fd, stream->GetUncompressedSize()) == -1) {
+-      ERROR("Couldn't ftruncate %s to decompress library", file.get());
+-      return nullptr;
+-    }
+-    /* Map the temporary file for use as inflate buffer */
+-    MappedPtr buffer(MemoryRange::mmap(nullptr, stream->GetUncompressedSize(),
+-                                       PROT_WRITE, MAP_SHARED, fd, 0));
+-    if (buffer == MAP_FAILED) {
+-      ERROR("Couldn't map %s to decompress library", file.get());
+-      return nullptr;
+-    }
+-
+-    zxx_stream zStream = stream->GetZStream(buffer);
+-
+-    /* Decompress */
+-    if (inflateInit2(&zStream, -MAX_WBITS) != Z_OK) {
+-      ERROR("inflateInit failed: %s", zStream.msg);
+-      return nullptr;
+-    }
+-    if (inflate(&zStream, Z_FINISH) != Z_STREAM_END) {
+-      ERROR("inflate failed: %s", zStream.msg);
+-      return nullptr;
+-    }
+-    if (inflateEnd(&zStream) != Z_OK) {
+-      ERROR("inflateEnd failed: %s", zStream.msg);
+-      return nullptr;
+-    }
+-    if (zStream.total_out != stream->GetUncompressedSize()) {
+-      ERROR("File not fully uncompressed! %ld / %d", zStream.total_out,
+-          static_cast<unsigned int>(stream->GetUncompressedSize()));
+-      return nullptr;
+-    }
+-  } else if (XZStream::IsXZ(stream->GetBuffer(), stream->GetSize())) {
+-    XZStream xzStream(stream->GetBuffer(), stream->GetSize());
+-
+-    if (!xzStream.Init()) {
+-      ERROR("Couldn't initialize XZ decoder");
+-      return nullptr;
+-    }
+-    DEBUG_LOG("XZStream created, compressed=%" PRIuPTR
+-              ", uncompressed=%" PRIuPTR,
+-              xzStream.Size(), xzStream.UncompressedSize());
+-
+-    if (ftruncate(fd, xzStream.UncompressedSize()) == -1) {
+-      ERROR("Couldn't ftruncate %s to decompress library", file.get());
+-      return nullptr;
+-    }
+-    MappedPtr buffer(MemoryRange::mmap(nullptr, xzStream.UncompressedSize(),
+-                                       PROT_WRITE, MAP_SHARED, fd, 0));
+-    if (buffer == MAP_FAILED) {
+-      ERROR("Couldn't map %s to decompress library", file.get());
+-      return nullptr;
+-    }
+-    const size_t written = xzStream.Decode(buffer, buffer.GetLength());
+-    DEBUG_LOG("XZStream decoded %" PRIuPTR, written);
+-    if (written != buffer.GetLength()) {
+-      ERROR("Error decoding XZ file %s", file.get());
+-      return nullptr;
+-    }
+-  } else {
+-    return nullptr;
+-  }
+-
+-  validator.CacheChecksum();
+-  return new MappableExtractFile(fd.forget(), file.release());
+-}
+-
+-/**
+- * _MappableBuffer is a buffer which content can be mapped at different
+- * locations in the virtual address space.
+- * On Linux, uses a (deleted) temporary file on a tmpfs for sharable content.
+- * On Android, uses ashmem.
+- */
+-class _MappableBuffer: public MappedPtr
+-{
+-public:
+-  /**
+-   * Returns a _MappableBuffer instance with the given name and the given
+-   * length.
+-   */
+-  static _MappableBuffer *Create(const char *name, size_t length)
+-  {
+-    AutoCloseFD fd;
+-#ifdef ANDROID
+-    /* On Android, initialize an ashmem region with the given length */
+-    fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
+-    if (fd == -1)
+-      return nullptr;
+-    char str[ASHMEM_NAME_LEN];
+-    strlcpy(str, name, sizeof(str));
+-    ioctl(fd, ASHMEM_SET_NAME, str);
+-    if (ioctl(fd, ASHMEM_SET_SIZE, length))
+-      return nullptr;
+-
+-    /* The Gecko crash reporter is confused by adjacent memory mappings of
+-     * the same file and chances are we're going to map from the same file
+-     * descriptor right away. To avoid problems with the crash reporter,
+-     * create an empty anonymous page before or after the ashmem mapping,
+-     * depending on how mappings grow in the address space.
+-     */
+-#if defined(__arm__)
+-    // Address increases on ARM.
+-    void *buf = ::mmap(nullptr, length + PAGE_SIZE, PROT_READ | PROT_WRITE,
+-                       MAP_SHARED, fd, 0);
+-    if (buf != MAP_FAILED) {
+-      ::mmap(AlignedEndPtr(reinterpret_cast<char *>(buf) + length, PAGE_SIZE),
+-             PAGE_SIZE, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+-      DEBUG_LOG("Decompression buffer of size 0x%" PRIxPTR
+-                " in ashmem \"%s\", mapped @%p",
+-                length, str, buf);
+-      return new _MappableBuffer(fd.forget(), buf, length);
+-    }
+-#elif defined(__i386__) || defined(__aarch64__)
+-    // Address decreases on x86 and AArch64.
+-    size_t anon_mapping_length = length + PAGE_SIZE;
+-    void *buf = ::mmap(nullptr, anon_mapping_length, PROT_NONE,
+-                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+-    if (buf != MAP_FAILED) {
+-      char *first_page = reinterpret_cast<char *>(buf);
+-      char *map_page = first_page + PAGE_SIZE;
+-
+-      void *actual_buf = ::mmap(map_page, length, PROT_READ | PROT_WRITE,
+-                                MAP_FIXED | MAP_SHARED, fd, 0);
+-      if (actual_buf == MAP_FAILED) {
+-        ::munmap(buf, anon_mapping_length);
+-        DEBUG_LOG("Fixed allocation of decompression buffer at %p failed", map_page);
+-        return nullptr;
+-      }
+-
+-      DEBUG_LOG("Decompression buffer of size 0x%" PRIxPTR
+-                " in ashmem \"%s\", mapped @%p", length, str, actual_buf);
+-      return new _MappableBuffer(fd.forget(), actual_buf, length);
+-    }
+-#else
+-#error need to add a case for your CPU
+-#endif
+-#else
+-    /* On Linux, use /dev/shm as base directory for temporary files, assuming
+-     * it's on tmpfs */
+-    /* TODO: check that /dev/shm is tmpfs */
+-    char path[256];
+-    sprintf(path, "/dev/shm/%s.XXXXXX", name);
+-    fd = mkstemp(path);
+-    if (fd == -1)
+-      return nullptr;
+-    unlink(path);
+-    ftruncate(fd, length);
+-
+-    void *buf = ::mmap(nullptr, length, PROT_READ | PROT_WRITE,
+-                       MAP_SHARED, fd, 0);
+-    if (buf != MAP_FAILED) {
+-      DEBUG_LOG("Decompression buffer of size %ld in \"%s\", mapped @%p",
+-                length, path, buf);
+-      return new _MappableBuffer(fd.forget(), buf, length);
+-    }
+-#endif
+-    return nullptr;
+-  }
+-
+-  void *mmap(const void *addr, size_t length, int prot, int flags, off_t offset)
+-  {
+-    MOZ_ASSERT(fd != -1);
+-#ifdef ANDROID
+-    /* Mapping ashmem MAP_PRIVATE is like mapping anonymous memory, even when
+-     * there is content in the ashmem */
+-    if (flags & MAP_PRIVATE) {
+-      flags &= ~MAP_PRIVATE;
+-      flags |= MAP_SHARED;
+-    }
+-#endif
+-    return ::mmap(const_cast<void *>(addr), length, prot, flags, fd, offset);
+-  }
+-
+-#ifdef ANDROID
+-  ~_MappableBuffer() {
+-    /* Free the additional page we allocated. See _MappableBuffer::Create */
+-#if defined(__arm__)
+-    ::munmap(AlignedEndPtr(*this + GetLength(), PAGE_SIZE), PAGE_SIZE);
+-#elif defined(__i386__) || defined(__aarch64__)
+-    ::munmap(*this - PAGE_SIZE, GetLength() + PAGE_SIZE);
+-#else
+-#error need to add a case for your CPU
+-#endif
+-  }
+-#endif
+-
+-private:
+-  _MappableBuffer(int fd, void *buf, size_t length)
+-  : MappedPtr(buf, length), fd(fd) { }
+-
+-  /* File descriptor for the temporary file or ashmem */
+-  AutoCloseFD fd;
+-};
+-
+-
+-Mappable *
+-MappableDeflate::Create(const char *name, Zip *zip, Zip::Stream *stream)
+-{
+-  MOZ_ASSERT(stream->GetType() == Zip::Stream::DEFLATE);
+-  _MappableBuffer *buf = _MappableBuffer::Create(name, stream->GetUncompressedSize());
+-  if (buf)
+-    return new MappableDeflate(buf, zip, stream);
+-  return nullptr;
+-}
+-
+-MappableDeflate::MappableDeflate(_MappableBuffer *buf, Zip *zip,
+-                                 Zip::Stream *stream)
+-: zip(zip), buffer(buf), zStream(stream->GetZStream(*buf)) { }
+-
+-MappableDeflate::~MappableDeflate() { }
+-
+-MemoryRange
+-MappableDeflate::mmap(const void *addr, size_t length, int prot, int flags, off_t offset)
+-{
+-  MOZ_ASSERT(buffer);
+-  MOZ_ASSERT(!(flags & MAP_SHARED));
+-  flags |= MAP_PRIVATE;
+-
+-  /* The deflate stream is uncompressed up to the required offset + length, if
+-   * it hasn't previously been uncompressed */
+-  ssize_t missing = offset + length + zStream.avail_out - buffer->GetLength();
+-  if (missing > 0) {
+-    uInt avail_out = zStream.avail_out;
+-    zStream.avail_out = missing;
+-    if ((*buffer == zStream.next_out) &&
+-        (inflateInit2(&zStream, -MAX_WBITS) != Z_OK)) {
+-      ERROR("inflateInit failed: %s", zStream.msg);
+-      return MemoryRange(MAP_FAILED, 0);
+-    }
+-    int ret = inflate(&zStream, Z_SYNC_FLUSH);
+-    if (ret < 0) {
+-      ERROR("inflate failed: %s", zStream.msg);
+-      return MemoryRange(MAP_FAILED, 0);
+-    }
+-    if (ret == Z_NEED_DICT) {
+-      ERROR("zstream requires a dictionary. %s", zStream.msg);
+-      return MemoryRange(MAP_FAILED, 0);
+-    }
+-    zStream.avail_out = avail_out - missing + zStream.avail_out;
+-    if (ret == Z_STREAM_END) {
+-      if (inflateEnd(&zStream) != Z_OK) {
+-        ERROR("inflateEnd failed: %s", zStream.msg);
+-        return MemoryRange(MAP_FAILED, 0);
+-      }
+-      if (zStream.total_out != buffer->GetLength()) {
+-        ERROR("File not fully uncompressed! %ld / %d", zStream.total_out,
+-            static_cast<unsigned int>(buffer->GetLength()));
+-        return MemoryRange(MAP_FAILED, 0);
+-      }
+-    }
+-  }
+-#if defined(ANDROID) && defined(__arm__)
+-  if (prot & PROT_EXEC) {
+-    /* We just extracted data that may be executed in the future.
+-     * We thus need to ensure Instruction and Data cache coherency. */
+-    DEBUG_LOG("cacheflush(%p, %p)", *buffer + offset, *buffer + (offset + length));
+-    cacheflush(reinterpret_cast<uintptr_t>(*buffer + offset),
+-               reinterpret_cast<uintptr_t>(*buffer + (offset + length)), 0);
+-  }
+-#endif
+-
+-  return MemoryRange(buffer->mmap(addr, length, prot, flags, offset), length);
+-}
+-
+-void
+-MappableDeflate::finalize()
+-{
+-  /* Free zlib internal buffers */
+-  inflateEnd(&zStream);
+-  /* Free decompression buffer */
+-  buffer = nullptr;
+-  /* Remove reference to Zip archive */
+-  zip = nullptr;
+-}
+-
+-size_t
+-MappableDeflate::GetLength() const
+-{
+-  return buffer->GetLength();
+-}
+diff --git a/mozglue/linker/Mappable.h b/mozglue/linker/Mappable.h
+deleted file mode 100644
+--- a/mozglue/linker/Mappable.h
++++ /dev/null
+@@ -1,163 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#ifndef Mappable_h
+-#define Mappable_h
+-
+-#include "Zip.h"
+-#include "mozilla/RefPtr.h"
+-#include "mozilla/UniquePtr.h"
+-#include "zlib.h"
+-
+-/**
+- * Abstract class to handle mmap()ing from various kind of entities, such as
+- * plain files or Zip entries. The virtual members are meant to act as the
+- * equivalent system functions, except mapped memory is always MAP_PRIVATE,
+- * even though a given implementation may use something different internally.
+- */
+-class Mappable: public mozilla::RefCounted<Mappable>
+-{
+-public:
+-  MOZ_DECLARE_REFCOUNTED_TYPENAME(Mappable)
+-  virtual ~Mappable() { }
+-
+-  virtual MemoryRange mmap(const void *addr, size_t length, int prot, int flags,
+-                           off_t offset) = 0;
+-
+-  enum Kind {
+-    MAPPABLE_FILE,
+-    MAPPABLE_EXTRACT_FILE,
+-    MAPPABLE_DEFLATE,
+-    MAPPABLE_SEEKABLE_ZSTREAM
+-  };
+-
+-  virtual Kind GetKind() const = 0;
+-
+-private:
+-  virtual void munmap(void *addr, size_t length) {
+-    ::munmap(addr, length);
+-  }
+-  /* Limit use of Mappable::munmap to classes that keep track of the address
+-   * and size of the mapping. This allows to ignore ::munmap return value. */
+-  friend class Mappable1stPagePtr;
+-  friend class LibHandle;
+-
+-public:
+-  /**
+-   * Indicate to a Mappable instance that no further mmap is going to happen.
+-   */
+-  virtual void finalize() = 0;
+-
+-  /**
+-   * Returns the maximum length that can be mapped from this Mappable for
+-   * offset = 0.
+-   */
+-  virtual size_t GetLength() const = 0;
+-};
+-
+-/**
+- * Mappable implementation for plain files
+- */
+-class MappableFile: public Mappable
+-{
+-public:
+-  ~MappableFile() { }
+-
+-  /**
+-   * Create a MappableFile instance for the given file path.
+-   */
+-  static Mappable *Create(const char *path);
+-
+-  /* Inherited from Mappable */
+-  virtual MemoryRange mmap(const void *addr, size_t length, int prot, int flags, off_t offset);
+-  virtual void finalize();
+-  virtual size_t GetLength() const;
+-
+-  virtual Kind GetKind() const { return MAPPABLE_FILE; };
+-protected:
+-  MappableFile(int fd): fd(fd) { }
+-
+-private:
+-  /* File descriptor */
+-  AutoCloseFD fd;
+-};
+-
+-/**
+- * Mappable implementation for deflated stream in a Zip archive
+- * Inflates the complete stream into a cache file.
+- */
+-class MappableExtractFile: public MappableFile
+-{
+-public:
+-  ~MappableExtractFile() = default;
+-
+-  /**
+-   * Create a MappableExtractFile instance for the given Zip stream. The name
+-   * argument is used to create the cache file in the cache directory.
+-   */
+-  static Mappable *Create(const char *name, Zip *zip, Zip::Stream *stream);
+-
+-  /* Override finalize from MappableFile */
+-  virtual void finalize() {}
+-
+-  virtual Kind GetKind() const { return MAPPABLE_EXTRACT_FILE; };
+-private:
+-  /**
+-   * AutoUnlinkFile keeps track of a file name and removes (unlinks) the file
+-   * when the instance is destroyed.
+-   */
+-  struct UnlinkFile
+-  {
+-    void operator()(char *value) {
+-      unlink(value);
+-      delete [] value;
+-    }
+-  };
+-  typedef mozilla::UniquePtr<char[], UnlinkFile> AutoUnlinkFile;
+-
+-  MappableExtractFile(int fd, const char* path)
+-  : MappableFile(fd), path(path) { }
+-
+-  /* Extracted file path */
+-  mozilla::UniquePtr<const char[]> path;
+-};
+-
+-class _MappableBuffer;
+-
+-/**
+- * Mappable implementation for deflated stream in a Zip archive.
+- * Inflates the mapped bits in a temporary buffer.
+- */
+-class MappableDeflate: public Mappable
+-{
+-public:
+-  ~MappableDeflate();
+-
+-  /**
+-   * Create a MappableDeflate instance for the given Zip stream. The name
+-   * argument is used for an appropriately named temporary file, and the Zip
+-   * instance is given for the MappableDeflate to keep a reference of it.
+-   */
+-  static Mappable *Create(const char *name, Zip *zip, Zip::Stream *stream);
+-
+-  /* Inherited from Mappable */
+-  virtual MemoryRange mmap(const void *addr, size_t length, int prot, int flags, off_t offset);
+-  virtual void finalize();
+-  virtual size_t GetLength() const;
+-
+-  virtual Kind GetKind() const { return MAPPABLE_DEFLATE; };
+-private:
+-  MappableDeflate(_MappableBuffer *buf, Zip *zip, Zip::Stream *stream);
+-
+-  /* Zip reference */
+-  RefPtr<Zip> zip;
+-
+-  /* Decompression buffer */
+-  mozilla::UniquePtr<_MappableBuffer> buffer;
+-
+-  /* Zlib data */
+-  zxx_stream zStream;
+-};
+-
+-#endif /* Mappable_h */
+diff --git a/mozglue/linker/Utils.h b/mozglue/linker/Utils.h
+deleted file mode 100644
+--- a/mozglue/linker/Utils.h
++++ /dev/null
+@@ -1,618 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#ifndef Utils_h
+-#define Utils_h
+-
+-#include <pthread.h>
+-#include <stdint.h>
+-#include <stddef.h>
+-#include <sys/mman.h>
+-#include <unistd.h>
+-#include "mozilla/Assertions.h"
+-#include "mozilla/Scoped.h"
+-
+-/**
+- * On architectures that are little endian and that support unaligned reads,
+- * we can use direct type, but on others, we want to have a special class
+- * to handle conversion and alignment issues.
+- */
+-#if !defined(DEBUG) && (defined(__i386__) || defined(__x86_64__))
+-typedef uint16_t le_uint16;
+-typedef uint32_t le_uint32;
+-#else
+-
+-/**
+- * Template that allows to find an unsigned int type from a (computed) bit size
+- */
+-template <int s> struct UInt { };
+-template <> struct UInt<16> { typedef uint16_t Type; };
+-template <> struct UInt<32> { typedef uint32_t Type; };
+-
+-/**
+- * Template to access 2 n-bit sized words as a 2*n-bit sized word, doing
+- * conversion from little endian and avoiding alignment issues.
+- */
+-template <typename T>
+-class le_to_cpu
+-{
+-public:
+-  typedef typename UInt<16 * sizeof(T)>::Type Type;
+-
+-  operator Type() const
+-  {
+-    return (b << (sizeof(T) * 8)) | a;
+-  }
+-
+-  const le_to_cpu& operator =(const Type &v)
+-  {
+-    a = v & ((1 << (sizeof(T) * 8)) - 1);
+-    b = v >> (sizeof(T) * 8);
+-    return *this;
+-  }
+-
+-  le_to_cpu() { }
+-  le_to_cpu(const Type &v)
+-  {
+-    operator =(v);
+-  }
+-
+-  const le_to_cpu& operator +=(const Type &v)
+-  {
+-    return operator =(operator Type() + v);
+-  }
+-
+-  const le_to_cpu& operator ++(int)
+-  {
+-    return operator =(operator Type() + 1);
+-  }
+-
+-private:
+-  T a, b;
+-};
+-
+-/**
+- * Type definitions
+- */
+-typedef le_to_cpu<unsigned char> le_uint16;
+-typedef le_to_cpu<le_uint16> le_uint32;
+-#endif
+-
+-
+-/**
+- * AutoCloseFD is a RAII wrapper for POSIX file descriptors
+- */
+-struct AutoCloseFDTraits
+-{
+-  typedef int type;
+-  static int empty() { return -1; }
+-  static void release(int fd) { if (fd != -1) close(fd); }
+-};
+-typedef mozilla::Scoped<AutoCloseFDTraits> AutoCloseFD;
+-
+-/**
+- * AutoCloseFILE is a RAII wrapper for POSIX streams
+- */
+-struct AutoCloseFILETraits
+-{
+-  typedef FILE *type;
+-  static FILE *empty() { return nullptr; }
+-  static void release(FILE *f) { if (f) fclose(f); }
+-};
+-typedef mozilla::Scoped<AutoCloseFILETraits> AutoCloseFILE;
+-
+-/**
+- * Page alignment helpers
+- */
+-static inline size_t PageSize()
+-{
+-  return 4096;
+-}
+-
+-static inline uintptr_t AlignedPtr(uintptr_t ptr, size_t alignment)
+-{
+-  return ptr & ~(alignment - 1);
+-}
+-
+-template <typename T>
+-static inline T *AlignedPtr(T *ptr, size_t alignment)
+-{
+-  return reinterpret_cast<T *>(
+-         AlignedPtr(reinterpret_cast<uintptr_t>(ptr), alignment));
+-}
+-
+-template <typename T>
+-static inline T PageAlignedPtr(T ptr)
+-{
+-  return AlignedPtr(ptr, PageSize());
+-}
+-
+-static inline uintptr_t AlignedEndPtr(uintptr_t ptr, size_t alignment)
+-{
+-  return AlignedPtr(ptr + alignment - 1, alignment);
+-}
+-
+-template <typename T>
+-static inline T *AlignedEndPtr(T *ptr, size_t alignment)
+-{
+-  return reinterpret_cast<T *>(
+-         AlignedEndPtr(reinterpret_cast<uintptr_t>(ptr), alignment));
+-}
+-
+-template <typename T>
+-static inline T PageAlignedEndPtr(T ptr)
+-{
+-  return AlignedEndPtr(ptr,  PageSize());
+-}
+-
+-static inline size_t AlignedSize(size_t size, size_t alignment)
+-{
+-  return (size + alignment - 1) & ~(alignment - 1);
+-}
+-
+-static inline size_t PageAlignedSize(size_t size)
+-{
+-  return AlignedSize(size, PageSize());
+-}
+-
+-static inline bool IsAlignedPtr(uintptr_t ptr, size_t alignment)
+-{
+-  return ptr % alignment == 0;
+-}
+-
+-template <typename T>
+-static inline bool IsAlignedPtr(T *ptr, size_t alignment)
+-{
+-  return IsAlignedPtr(reinterpret_cast<uintptr_t>(ptr), alignment);
+-}
+-
+-template <typename T>
+-static inline bool IsPageAlignedPtr(T ptr)
+-{
+-  return IsAlignedPtr(ptr, PageSize());
+-}
+-
+-static inline bool IsAlignedSize(size_t size, size_t alignment)
+-{
+-  return size % alignment == 0;
+-}
+-
+-static inline bool IsPageAlignedSize(size_t size)
+-{
+-  return IsAlignedSize(size, PageSize());
+-}
+-
+-static inline size_t PageNumber(size_t size)
+-{
+-  return (size + PageSize() - 1) / PageSize();
+-}
+-
+-/**
+- * MemoryRange stores a pointer, size pair.
+- */
+-class MemoryRange
+-{
+-public:
+-  MemoryRange(void *buf, size_t length): buf(buf), length(length) { }
+-
+-  void Assign(void *b, size_t len) {
+-    buf = b;
+-    length = len;
+-  }
+-
+-  void Assign(const MemoryRange& other) {
+-    buf = other.buf;
+-    length = other.length;
+-  }
+-
+-  void *get() const
+-  {
+-    return buf;
+-  }
+-
+-  operator void *() const
+-  {
+-    return buf;
+-  }
+-
+-  operator unsigned char *() const
+-  {
+-    return reinterpret_cast<unsigned char *>(buf);
+-  }
+-
+-  bool operator ==(void *ptr) const {
+-    return buf == ptr;
+-  }
+-
+-  bool operator ==(unsigned char *ptr) const {
+-    return buf == ptr;
+-  }
+-
+-  void *operator +(off_t offset) const
+-  {
+-    return reinterpret_cast<char *>(buf) + offset;
+-  }
+-
+-  /**
+-   * Returns whether the given address is within the mapped range
+-   */
+-  bool Contains(void *ptr) const
+-  {
+-    return (ptr >= buf) && (ptr < reinterpret_cast<char *>(buf) + length);
+-  }
+-
+-  /**
+-   * Returns the length of the mapped range
+-   */
+-  size_t GetLength() const
+-  {
+-    return length;
+-  }
+-
+-  static MemoryRange mmap(void *addr, size_t length, int prot, int flags,
+-                          int fd, off_t offset) {
+-    return MemoryRange(::mmap(addr, length, prot, flags, fd, offset), length);
+-  }
+-
+-private:
+-  void *buf;
+-  size_t length;
+-};
+-
+-/**
+- * MappedPtr is a RAII wrapper for mmap()ed memory. It can be used as
+- * a simple void * or unsigned char *.
+- *
+- * It is defined as a derivative of a template that allows to use a
+- * different unmapping strategy.
+- */
+-template <typename T>
+-class GenericMappedPtr: public MemoryRange
+-{
+-public:
+-  GenericMappedPtr(void *buf, size_t length): MemoryRange(buf, length) { }
+-  GenericMappedPtr(const MemoryRange& other): MemoryRange(other) { }
+-  GenericMappedPtr(): MemoryRange(MAP_FAILED, 0) { }
+-
+-  void Assign(void *b, size_t len) {
+-    if (get() != MAP_FAILED)
+-      static_cast<T *>(this)->munmap(get(), GetLength());
+-    MemoryRange::Assign(b, len);
+-  }
+-
+-  void Assign(const MemoryRange& other) {
+-    Assign(other.get(), other.GetLength());
+-  }
+-
+-  ~GenericMappedPtr()
+-  {
+-    if (get() != MAP_FAILED)
+-      static_cast<T *>(this)->munmap(get(), GetLength());
+-  }
+-
+-  void release()
+-  {
+-    MemoryRange::Assign(MAP_FAILED, 0);
+-  }
+-};
+-
+-struct MappedPtr: public GenericMappedPtr<MappedPtr>
+-{
+-  MappedPtr(void *buf, size_t length)
+-  : GenericMappedPtr<MappedPtr>(buf, length) { }
+-  MappedPtr(const MemoryRange& other)
+-  : GenericMappedPtr<MappedPtr>(other) { }
+-  MappedPtr(): GenericMappedPtr<MappedPtr>() { }
+-
+-private:
+-  friend class GenericMappedPtr<MappedPtr>;
+-  void munmap(void *buf, size_t length)
+-  {
+-    ::munmap(buf, length);
+-  }
+-};
+-
+-/**
+- * UnsizedArray is a way to access raw arrays of data in memory.
+- *
+- *   struct S { ... };
+- *   UnsizedArray<S> a(buf);
+- *   UnsizedArray<S> b; b.Init(buf);
+- *
+- * This is roughly equivalent to
+- *   const S *a = reinterpret_cast<const S *>(buf);
+- *   const S *b = nullptr; b = reinterpret_cast<const S *>(buf);
+- *
+- * An UnsizedArray has no known length, and it's up to the caller to make
+- * sure the accessed memory is mapped and makes sense.
+- */
+-template <typename T>
+-class UnsizedArray
+-{
+-public:
+-  typedef size_t idx_t;
+-
+-  /**
+-   * Constructors and Initializers
+-   */
+-  UnsizedArray(): contents(nullptr) { }
+-  UnsizedArray(const void *buf): contents(reinterpret_cast<const T *>(buf)) { }
+-
+-  void Init(const void *buf)
+-  {
+-    MOZ_ASSERT(contents == nullptr);
+-    contents = reinterpret_cast<const T *>(buf);
+-  }
+-
+-  /**
+-   * Returns the nth element of the array
+-   */
+-  const T &operator[](const idx_t index) const
+-  {
+-    MOZ_ASSERT(contents);
+-    return contents[index];
+-  }
+-
+-  operator const T *() const
+-  {
+-    return contents;
+-  }
+-  /**
+-   * Returns whether the array points somewhere
+-   */
+-  operator bool() const
+-  {
+-    return contents != nullptr;
+-  }
+-private:
+-  const T *contents;
+-};
+-
+-/**
+- * Array, like UnsizedArray, is a way to access raw arrays of data in memory.
+- * Unlike UnsizedArray, it has a known length, and is enumerable with an
+- * iterator.
+- *
+- *   struct S { ... };
+- *   Array<S> a(buf, len);
+- *   UnsizedArray<S> b; b.Init(buf, len);
+- *
+- * In the above examples, len is the number of elements in the array. It is
+- * also possible to initialize an Array with the buffer size:
+- *
+- *   Array<S> c; c.InitSize(buf, size);
+- *
+- * It is also possible to initialize an Array in two steps, only providing
+- * one data at a time:
+- *
+- *   Array<S> d;
+- *   d.Init(buf);
+- *   d.Init(len); // or d.InitSize(size);
+- *
+- */
+-template <typename T>
+-class Array: public UnsizedArray<T>
+-{
+-public:
+-  typedef typename UnsizedArray<T>::idx_t idx_t;
+-
+-  /**
+-   * Constructors and Initializers
+-   */
+-  Array(): UnsizedArray<T>(), length(0) { }
+-  Array(const void *buf, const idx_t length)
+-  : UnsizedArray<T>(buf), length(length) { }
+-
+-  void Init(const void *buf)
+-  {
+-    UnsizedArray<T>::Init(buf);
+-  }
+-
+-  void Init(const idx_t len)
+-  {
+-    MOZ_ASSERT(length == 0);
+-    length = len;
+-  }
+-
+-  void InitSize(const idx_t size)
+-  {
+-    Init(size / sizeof(T));
+-  }
+-
+-  void Init(const void *buf, const idx_t len)
+-  {
+-    UnsizedArray<T>::Init(buf);
+-    Init(len);
+-  }
+-
+-  void InitSize(const void *buf, const idx_t size)
+-  {
+-    UnsizedArray<T>::Init(buf);
+-    InitSize(size);
+-  }
+-
+-  /**
+-   * Returns the nth element of the array
+-   */
+-  const T &operator[](const idx_t index) const
+-  {
+-    MOZ_ASSERT(index < length);
+-    MOZ_ASSERT(operator bool());
+-    return UnsizedArray<T>::operator[](index);
+-  }
+-
+-  /**
+-   * Returns the number of elements in the array
+-   */
+-  idx_t numElements() const
+-  {
+-    return length;
+-  }
+-
+-  /**
+-   * Returns whether the array points somewhere and has at least one element.
+-   */
+-  operator bool() const
+-  {
+-    return (length > 0) && UnsizedArray<T>::operator bool();
+-  }
+-
+-  /**
+-   * Iterator for an Array. Use is similar to that of STL const_iterators:
+-   *
+-   *   struct S { ... };
+-   *   Array<S> a(buf, len);
+-   *   for (Array<S>::iterator it = a.begin(); it < a.end(); ++it) {
+-   *     // Do something with *it.
+-   *   }
+-   */
+-  class iterator
+-  {
+-  public:
+-    iterator(): item(nullptr) { }
+-
+-    const T &operator *() const
+-    {
+-      return *item;
+-    }
+-
+-    const T *operator ->() const
+-    {
+-      return item;
+-    }
+-
+-    iterator &operator ++()
+-    {
+-      ++item;
+-      return *this;
+-    }
+-
+-    bool operator<(const iterator &other) const
+-    {
+-      return item < other.item;
+-    }
+-  protected:
+-    friend class Array<T>;
+-    iterator(const T &item): item(&item) { }
+-
+-  private:
+-    const T *item;
+-  };
+-
+-  /**
+-   * Returns an iterator pointing at the beginning of the Array
+-   */
+-  iterator begin() const {
+-    if (length)
+-      return iterator(UnsizedArray<T>::operator[](0));
+-    return iterator();
+-  }
+-
+-  /**
+-   * Returns an iterator pointing past the end of the Array
+-   */
+-  iterator end() const {
+-    if (length)
+-      return iterator(UnsizedArray<T>::operator[](length));
+-    return iterator();
+-  }
+-
+-  /**
+-   * Reverse iterator for an Array. Use is similar to that of STL
+-   * const_reverse_iterators:
+-   *
+-   *   struct S { ... };
+-   *   Array<S> a(buf, len);
+-   *   for (Array<S>::reverse_iterator it = a.rbegin(); it < a.rend(); ++it) {
+-   *     // Do something with *it.
+-   *   }
+-   */
+-  class reverse_iterator
+-  {
+-  public:
+-    reverse_iterator(): item(nullptr) { }
+-
+-    const T &operator *() const
+-    {
+-      const T *tmp = item;
+-      return *--tmp;
+-    }
+-
+-    const T *operator ->() const
+-    {
+-      return &operator*();
+-    }
+-
+-    reverse_iterator &operator ++()
+-    {
+-      --item;
+-      return *this;
+-    }
+-
+-    bool operator<(const reverse_iterator &other) const
+-    {
+-      return item > other.item;
+-    }
+-  protected:
+-    friend class Array<T>;
+-    reverse_iterator(const T &item): item(&item) { }
+-
+-  private:
+-    const T *item;
+-  };
+-
+-  /**
+-   * Returns a reverse iterator pointing at the end of the Array
+-   */
+-  reverse_iterator rbegin() const {
+-    if (length)
+-      return reverse_iterator(UnsizedArray<T>::operator[](length));
+-    return reverse_iterator();
+-  }
+-
+-  /**
+-   * Returns a reverse iterator pointing past the beginning of the Array
+-   */
+-  reverse_iterator rend() const {
+-    if (length)
+-      return reverse_iterator(UnsizedArray<T>::operator[](0));
+-    return reverse_iterator();
+-  }
+-private:
+-  idx_t length;
+-};
+-
+-/**
+- * Transforms a pointer-to-function to a pointer-to-object pointing at the
+- * same address.
+- */
+-template <typename T>
+-void *FunctionPtr(T func)
+-{
+-  union {
+-    void *ptr;
+-    T func;
+-  } f;
+-  f.func = func;
+-  return f.ptr;
+-}
+-
+-class AutoLock {
+-public:
+-  AutoLock(pthread_mutex_t *mutex): mutex(mutex)
+-  {
+-    if (pthread_mutex_lock(mutex))
+-      MOZ_CRASH("pthread_mutex_lock failed");
+-  }
+-  ~AutoLock()
+-  {
+-    if (pthread_mutex_unlock(mutex))
+-      MOZ_CRASH("pthread_mutex_unlock failed");
+-  }
+-private:
+-  pthread_mutex_t *mutex;
+-};
+-
+-#endif /* Utils_h */
+-
+diff --git a/mozglue/linker/XZStream.cpp b/mozglue/linker/XZStream.cpp
+deleted file mode 100644
+--- a/mozglue/linker/XZStream.cpp
++++ /dev/null
+@@ -1,237 +0,0 @@
+-#include "XZStream.h"
+-
+-#include <algorithm>
+-#include "mozilla/Assertions.h"
+-#include "mozilla/CheckedInt.h"
+-#include "Logging.h"
+-
+-// LZMA dictionary size, should have a minimum size for the given compression
+-// rate, see XZ Utils docs for details.
+-static const uint32_t kDictSize = 1 << 24;
+-
+-static const size_t kFooterSize = 12;
+-
+-// Parses a variable-length integer (VLI),
+-// see http://tukaani.org/xz/xz-file-format.txt for details.
+-static size_t
+-ParseVarLenInt(const uint8_t* aBuf, size_t aBufSize, uint64_t* aValue)
+-{
+-  if (!aBufSize) {
+-    return 0;
+-  }
+-  aBufSize = std::min(size_t(9), aBufSize);
+-
+-  *aValue = aBuf[0] & 0x7F;
+-  size_t i = 0;
+-
+-  while (aBuf[i++] & 0x80) {
+-    if (i >= aBufSize || aBuf[i] == 0x0) {
+-      return 0;
+-    }
+-    *aValue |= static_cast<uint64_t>(aBuf[i] & 0x7F) << (i * 7);
+-  }
+-  return i;
+-}
+-
+-/* static */ bool
+-XZStream::IsXZ(const void* aBuf, size_t aBufSize)
+-{
+-  static const uint8_t kXzMagic[] = {0xfd, '7', 'z', 'X', 'Z', 0x0};
+-  MOZ_ASSERT(aBuf);
+-  return aBufSize > sizeof(kXzMagic) &&
+-         !memcmp(reinterpret_cast<const void*>(kXzMagic), aBuf, sizeof(kXzMagic));
+-}
+-
+-XZStream::XZStream(const void* aInBuf, size_t aInSize)
+-  : mInBuf(static_cast<const uint8_t*>(aInBuf))
+-  , mUncompSize(0)
+-  , mDec(nullptr)
+-{
+-  mBuffers.in = mInBuf;
+-  mBuffers.in_pos = 0;
+-  mBuffers.in_size = aInSize;
+-}
+-
+-XZStream::~XZStream()
+-{
+-  xz_dec_end(mDec);
+-}
+-
+-bool
+-XZStream::Init()
+-{
+-#ifdef XZ_USE_CRC64
+-  xz_crc64_init();
+-#endif
+-  xz_crc32_init();
+-
+-  mDec = xz_dec_init(XZ_DYNALLOC, kDictSize);
+-
+-  if (!mDec) {
+-    return false;
+-  }
+-
+-  mUncompSize = ParseUncompressedSize();
+-  if (!mUncompSize) {
+-    return false;
+-  }
+-
+-  return true;
+-}
+-
+-size_t
+-XZStream::Decode(void* aOutBuf, size_t aOutSize)
+-{
+-  if (!mDec) {
+-    return 0;
+-  }
+-
+-  mBuffers.out = static_cast<uint8_t*>(aOutBuf);
+-  mBuffers.out_pos = 0;
+-  mBuffers.out_size = aOutSize;
+-
+-  while (mBuffers.in_pos < mBuffers.in_size &&
+-         mBuffers.out_pos < mBuffers.out_size) {
+-    const xz_ret ret = xz_dec_run(mDec, &mBuffers);
+-
+-    switch (ret) {
+-      case XZ_STREAM_END:
+-        // Stream ended, the next loop iteration should terminate.
+-        MOZ_ASSERT(mBuffers.in_pos == mBuffers.in_size);
+-        MOZ_FALLTHROUGH;
+-#ifdef XZ_DEC_ANY_CHECK
+-      case XZ_UNSUPPORTED_CHECK:
+-        // Ignore unsupported check.
+-        MOZ_FALLTHROUGH;
+-#endif
+-      case XZ_OK:
+-        // Chunk decoded, proceed.
+-        break;
+-
+-      case XZ_MEM_ERROR:
+-        ERROR("XZ decoding: memory allocation failed");
+-        return 0;
+-
+-      case XZ_MEMLIMIT_ERROR:
+-        ERROR("XZ decoding: memory usage limit reached");
+-        return 0;
+-
+-      case XZ_FORMAT_ERROR:
+-        ERROR("XZ decoding: invalid stream format");
+-        return 0;
+-
+-      case XZ_OPTIONS_ERROR:
+-        ERROR("XZ decoding: unsupported header options");
+-        return 0;
+-
+-      case XZ_DATA_ERROR:
+-        MOZ_FALLTHROUGH;
+-      case XZ_BUF_ERROR:
+-        ERROR("XZ decoding: corrupt input stream");
+-        return 0;
+-
+-      default:
+-        MOZ_ASSERT_UNREACHABLE("XZ decoding: unknown error condition");
+-        return 0;
+-    }
+-  }
+-  return mBuffers.out_pos;
+-}
+-
+-size_t
+-XZStream::RemainingInput() const
+-{
+-  return mBuffers.in_size - mBuffers.in_pos;
+-}
+-
+-size_t
+-XZStream::Size() const
+-{
+-  return mBuffers.in_size;
+-}
+-
+-size_t
+-XZStream::UncompressedSize() const
+-{
+-  return mUncompSize;
+-}
+-
+-size_t
+-XZStream::ParseIndexSize() const
+-{
+-  static const uint8_t kFooterMagic[] = {'Y', 'Z'};
+-
+-  const uint8_t* footer = mInBuf + mBuffers.in_size - kFooterSize;
+-  // The magic bytes are at the end of the footer.
+-  if (memcmp(reinterpret_cast<const void*>(kFooterMagic),
+-             footer + kFooterSize - sizeof(kFooterMagic),
+-             sizeof(kFooterMagic))) {
+-    // Not a valid footer at stream end.
+-    ERROR("XZ parsing: Invalid footer at end of stream");
+-    return 0;
+-  }
+-  // Backward size is a 32 bit LE integer field positioned after the 32 bit CRC32
+-  // code. It encodes the index size as a multiple of 4 bytes with a minimum
+-  // size of 4 bytes.
+-  const uint32_t backwardSizeRaw = *(footer + 4);
+-  // Check for overflow.
+-  mozilla::CheckedInt<size_t> backwardSizeBytes(backwardSizeRaw);
+-  backwardSizeBytes = (backwardSizeBytes + 1) * 4;
+-  if (!backwardSizeBytes.isValid()) {
+-    ERROR("XZ parsing: Cannot parse index size");
+-    return 0;
+-  }
+-  return backwardSizeBytes.value();
+-}
+-
+-size_t
+-XZStream::ParseUncompressedSize() const
+-{
+-  static const uint8_t kIndexIndicator[] = {0x0};
+-
+-  const size_t indexSize = ParseIndexSize();
+-  if (!indexSize) {
+-    return 0;
+-  }
+-  // The footer follows directly the index, so we can use it as a reference.
+-  const uint8_t* end = mInBuf + mBuffers.in_size;
+-  const uint8_t* index = end - kFooterSize - indexSize;
+-
+-  // The xz stream index consists of three concatenated elements:
+-  //  (1) 1 byte indicator (always OxOO)
+-  //  (2) a Variable Length Integer (VLI) field for the number of records
+-  //  (3) a list of records
+-  // See https://tukaani.org/xz/xz-file-format-1.0.4.txt
+-  // Each record contains a VLI field for unpadded size followed by a var field for
+-  // uncompressed size. We only support xz streams with a single record.
+-
+-  if (memcmp(reinterpret_cast<const void*>(kIndexIndicator),
+-             index, sizeof(kIndexIndicator))) {
+-    ERROR("XZ parsing: Invalid stream index");
+-    return 0;
+-  }
+-
+-  index += sizeof(kIndexIndicator);
+-  uint64_t numRecords = 0;
+-  index += ParseVarLenInt(index, end - index, &numRecords);
+-  // Only streams with a single record are supported.
+-  if (numRecords != 1) {
+-    ERROR("XZ parsing: Multiple records not supported");
+-    return 0;
+-  }
+-  uint64_t unpaddedSize = 0;
+-  index += ParseVarLenInt(index, end - index, &unpaddedSize);
+-  if (!unpaddedSize) {
+-    ERROR("XZ parsing: Unpadded size is 0");
+-    return 0;
+-  }
+-  uint64_t uncompressedSize = 0;
+-  index += ParseVarLenInt(index, end - index, &uncompressedSize);
+-  mozilla::CheckedInt<size_t> checkedSize(uncompressedSize);
+-  if (!checkedSize.isValid()) {
+-    ERROR("XZ parsing: Uncompressed stream size is too large");
+-    return 0;
+-  }
+-
+-  return checkedSize.value();
+-}
+diff --git a/mozglue/linker/XZStream.h b/mozglue/linker/XZStream.h
+deleted file mode 100644
+--- a/mozglue/linker/XZStream.h
++++ /dev/null
+@@ -1,49 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#ifndef XZSTREAM_h
+-#define XZSTREAM_h
+-
+-#include <cstdlib>
+-#include <stdint.h>
+-
+-#define XZ_DEC_DYNALLOC
+-#include "xz.h"
+-
+-// Used to decode XZ stream buffers.
+-class XZStream
+-{
+-public:
+-  // Returns whether the provided buffer is likely a XZ stream.
+-  static bool IsXZ(const void* aBuf, size_t aBufSize);
+-
+-  // Creates a XZ stream object for the given input buffer.
+-  XZStream(const void* aInBuf, size_t aInSize);
+-  ~XZStream();
+-
+-  // Initializes the decoder and returns whether decoding may commence.
+-  bool Init();
+-  // Decodes the next chunk of input into the given output buffer.
+-  size_t Decode(void* aOutBuf, size_t aOutSize);
+-  // Returns the number of yet undecoded bytes in the input buffer.
+-  size_t RemainingInput() const;
+-  // Returns the total number of bytes in the input buffer (compressed size).
+-  size_t Size() const;
+-  // Returns the expected final number of bytes in the output buffer.
+-  // Note: will return 0 before successful Init().
+-  size_t UncompressedSize() const;
+-
+-private:
+-  // Parses the stream footer and returns the size of the index in bytes.
+-  size_t ParseIndexSize() const;
+-  // Parses the stream index and returns the expected uncompressed size in bytes.
+-  size_t ParseUncompressedSize() const;
+-
+-  const uint8_t* mInBuf;
+-  size_t mUncompSize;
+-  xz_buf mBuffers;
+-  xz_dec* mDec;
+-};
+-
+-#endif // XZSTREAM_h
+diff --git a/mozglue/linker/Zip.cpp b/mozglue/linker/Zip.cpp
+deleted file mode 100644
+--- a/mozglue/linker/Zip.cpp
++++ /dev/null
+@@ -1,230 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#include <sys/mman.h>
+-#include <sys/stat.h>
+-#include <fcntl.h>
+-#include <errno.h>
+-#include <unistd.h>
+-#include <cstdlib>
+-#include <algorithm>
+-#include "Logging.h"
+-#include "Zip.h"
+-
+-already_AddRefed<Zip>
+-Zip::Create(const char *filename)
+-{
+-  /* Open and map the file in memory */
+-  AutoCloseFD fd(open(filename, O_RDONLY));
+-  if (fd == -1) {
+-    ERROR("Error opening %s: %s", filename, strerror(errno));
+-    return nullptr;
+-  }
+-  struct stat st;
+-  if (fstat(fd, &st) == -1) {
+-    ERROR("Error stating %s: %s", filename, strerror(errno));
+-    return nullptr;
+-  }
+-  size_t size = st.st_size;
+-  if (size <= sizeof(CentralDirectoryEnd)) {
+-    ERROR("Error reading %s: too short", filename);
+-    return nullptr;
+-  }
+-  void *mapped = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
+-  if (mapped == MAP_FAILED) {
+-    ERROR("Error mmapping %s: %s", filename, strerror(errno));
+-    return nullptr;
+-  }
+-  DEBUG_LOG("Mapped %s @%p", filename, mapped);
+-
+-  return Create(filename, mapped, size);
+-}
+-
+-already_AddRefed<Zip>
+-Zip::Create(const char *filename, void *mapped, size_t size)
+-{
+-  RefPtr<Zip> zip = new Zip(filename, mapped, size);
+-
+-  // If neither the first Local File entry nor central directory entries
+-  // have been found, the zip was invalid.
+-  if (!zip->nextFile && !zip->entries) {
+-    ERROR("%s - Invalid zip", filename);
+-    return nullptr;
+-  }
+-
+-  ZipCollection::Singleton.Register(zip);
+-  return zip.forget();
+-}
+-
+-Zip::Zip(const char *filename, void *mapped, size_t size)
+-: name(filename ? strdup(filename) : nullptr)
+-, mapped(mapped)
+-, size(size)
+-, nextFile(LocalFile::validate(mapped)) // first Local File entry
+-, nextDir(nullptr)
+-, entries(nullptr)
+-{
+-  pthread_mutex_init(&mutex, nullptr);
+-  // If the first local file entry couldn't be found (which can happen
+-  // with optimized jars), check the first central directory entry.
+-  if (!nextFile)
+-    GetFirstEntry();
+-}
+-
+-Zip::~Zip()
+-{
+-  if (name) {
+-    munmap(mapped, size);
+-    DEBUG_LOG("Unmapped %s @%p", name, mapped);
+-    free(name);
+-  }
+-  pthread_mutex_destroy(&mutex);
+-}
+-
+-bool
+-Zip::GetStream(const char *path, Zip::Stream *out) const
+-{
+-  AutoLock lock(&mutex);
+-
+-  DEBUG_LOG("%s - GetFile %s", name, path);
+-  /* Fast path: if the Local File header on store matches, we can return the
+-   * corresponding stream right away.
+-   * However, the Local File header may not contain enough information, in
+-   * which case the 3rd bit on the generalFlag is set. Unfortunately, this
+-   * bit is also set in some archives even when we do have the data (most
+-   * notably the android packages as built by the Mozilla build system).
+-   * So instead of testing the generalFlag bit, only use the fast path when
+-   * we haven't read the central directory entries yet, and when the
+-   * compressed size as defined in the header is not filled (which is a
+-   * normal condition for the bit to be set). */
+-  if (nextFile && nextFile->GetName().Equals(path) &&
+-      !entries && (nextFile->compressedSize != 0)) {
+-    DEBUG_LOG("%s - %s was next file: fast path", name, path);
+-    /* Fill Stream info from Local File header content */
+-    const char *data = reinterpret_cast<const char *>(nextFile->GetData());
+-    out->compressedBuf = data;
+-    out->compressedSize = nextFile->compressedSize;
+-    out->uncompressedSize = nextFile->uncompressedSize;
+-    out->CRC32 = nextFile->CRC32;
+-    out->type = static_cast<Stream::Type>(uint16_t(nextFile->compression));
+-
+-    /* Find the next Local File header. It is usually simply following the
+-     * compressed stream, but in cases where the 3rd bit of the generalFlag
+-     * is set, there is a Data Descriptor header before. */
+-    data += nextFile->compressedSize;
+-    if ((nextFile->generalFlag & 0x8) && DataDescriptor::validate(data)) {
+-      data += sizeof(DataDescriptor);
+-    }
+-    nextFile = LocalFile::validate(data);
+-    return true;
+-  }
+-
+-  /* If the directory entry we have in store doesn't match, scan the Central
+-   * Directory for the entry corresponding to the given path */
+-  if (!nextDir || !nextDir->GetName().Equals(path)) {
+-    const DirectoryEntry *entry = GetFirstEntry();
+-    DEBUG_LOG("%s - Scan directory entries in search for %s", name, path);
+-    while (entry && !entry->GetName().Equals(path)) {
+-      entry = entry->GetNext();
+-    }
+-    nextDir = entry;
+-  }
+-  if (!nextDir) {
+-    DEBUG_LOG("%s - Couldn't find %s", name, path);
+-    return false;
+-  }
+-
+-  /* Find the Local File header corresponding to the Directory entry that
+-   * was found. */
+-  nextFile = LocalFile::validate(static_cast<const char *>(mapped)
+-                             + nextDir->offset);
+-  if (!nextFile) {
+-    ERROR("%s - Couldn't find the Local File header for %s", name, path);
+-    return false;
+-  }
+-
+-  /* Fill Stream info from Directory entry content */
+-  const char *data = reinterpret_cast<const char *>(nextFile->GetData());
+-  out->compressedBuf = data;
+-  out->compressedSize = nextDir->compressedSize;
+-  out->uncompressedSize = nextDir->uncompressedSize;
+-  out->CRC32 = nextDir->CRC32;
+-  out->type = static_cast<Stream::Type>(uint16_t(nextDir->compression));
+-
+-  /* Store the next directory entry */
+-  nextDir = nextDir->GetNext();
+-  nextFile = nullptr;
+-  return true;
+-}
+-
+-const Zip::DirectoryEntry *
+-Zip::GetFirstEntry() const
+-{
+-  if (entries)
+-    return entries;
+-
+-  const CentralDirectoryEnd *end = nullptr;
+-  const char *_end = static_cast<const char *>(mapped) + size
+-                     - sizeof(CentralDirectoryEnd);
+-
+-  /* Scan for the Central Directory End */
+-  for (; _end > mapped && !end; _end--)
+-    end = CentralDirectoryEnd::validate(_end);
+-  if (!end) {
+-    ERROR("%s - Couldn't find end of central directory record", name);
+-    return nullptr;
+-  }
+-
+-  entries = DirectoryEntry::validate(static_cast<const char *>(mapped)
+-                                 + end->offset);
+-  if (!entries) {
+-    ERROR("%s - Couldn't find central directory record", name);
+-  }
+-  return entries;
+-}
+-
+-ZipCollection ZipCollection::Singleton;
+-
+-static pthread_mutex_t sZipCollectionMutex = PTHREAD_MUTEX_INITIALIZER;
+-
+-already_AddRefed<Zip>
+-ZipCollection::GetZip(const char *path)
+-{
+-  {
+-    AutoLock lock(&sZipCollectionMutex);
+-    /* Search the list of Zips we already have for a match */
+-    for (const auto& zip: Singleton.zips) {
+-      if (zip->GetName() && (strcmp(zip->GetName(), path) == 0)) {
+-        return RefPtr<Zip>(zip).forget();
+-      }
+-    }
+-  }
+-  return Zip::Create(path);
+-}
+-
+-void
+-ZipCollection::Register(Zip *zip)
+-{
+-  AutoLock lock(&sZipCollectionMutex);
+-  DEBUG_LOG("ZipCollection::Register(\"%s\")", zip->GetName());
+-  Singleton.zips.push_back(zip);
+-}
+-
+-void
+-ZipCollection::Forget(const Zip *zip)
+-{
+-  AutoLock lock(&sZipCollectionMutex);
+-  if (zip->refCount() > 1) {
+-    // Someone has acquired a reference before we had acquired the lock,
+-    // ignore this request.
+-    return;
+-  }
+-  DEBUG_LOG("ZipCollection::Forget(\"%s\")", zip->GetName());
+-  const auto it = std::find(Singleton.zips.begin(), Singleton.zips.end(), zip);
+-  if (*it == zip) {
+-    Singleton.zips.erase(it);
+-  } else {
+-    DEBUG_LOG("ZipCollection::Forget: didn't find \"%s\" in bookkeeping", zip->GetName());
+-  }
+-}
+diff --git a/mozglue/linker/Zip.h b/mozglue/linker/Zip.h
+deleted file mode 100644
+--- a/mozglue/linker/Zip.h
++++ /dev/null
+@@ -1,536 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#ifndef Zip_h
+-#define Zip_h
+-
+-#include <cstring>
+-#include <stdint.h>
+-#include <vector>
+-#include <zlib.h>
+-#include <pthread.h>
+-#include "Utils.h"
+-#include "mozilla/Assertions.h"
+-#include "mozilla/RefCounted.h"
+-#include "mozilla/RefPtr.h"
+-
+-/**
+- * Helper class wrapping z_stream to avoid malloc() calls during
+- * inflate. Do not use for deflate.
+- * inflateInit allocates two buffers:
+- * - one for its internal state, which is "approximately 10K bytes" according
+- *   to inflate.h from zlib.
+- * - one for the compression window, which depends on the window size passed
+- *   to inflateInit2, but is never greater than 32K (1 << MAX_WBITS).
+- * Those buffers are created at instantiation time instead of when calling
+- * inflateInit2. When inflateInit2 is called, it will call zxx_stream::Alloc
+- * to get both these buffers. zxx_stream::Alloc will choose one of the
+- * pre-allocated buffers depending on the requested size.
+- */
+-class zxx_stream: public z_stream
+-{
+-public:
+-  /* Forward declaration */
+-  class StaticAllocator;
+-
+-  explicit zxx_stream(StaticAllocator *allocator_=nullptr)
+-  : allocator(allocator_)
+-  {
+-    memset(this, 0, sizeof(z_stream));
+-    zalloc = Alloc;
+-    zfree = Free;
+-    opaque = this;
+-  }
+-
+-private:
+-  static void *Alloc(void *data, uInt items, uInt size)
+-  {
+-    zxx_stream *zStream = reinterpret_cast<zxx_stream *>(data);
+-    if (zStream->allocator) {
+-      return zStream->allocator->Alloc(items, size);
+-    }
+-    size_t buf_size = items * size;
+-    return ::operator new(buf_size);
+-  }
+-
+-  static void Free(void *data, void *ptr)
+-  {
+-    zxx_stream *zStream = reinterpret_cast<zxx_stream *>(data);
+-    if (zStream->allocator) {
+-      zStream->allocator->Free(ptr);
+-    } else {
+-      ::operator delete(ptr);
+-    }
+-  }
+-
+-  /**
+-   * Helper class for each buffer in StaticAllocator.
+-   */
+-  template <size_t Size>
+-  class ZStreamBuf
+-  {
+-  public:
+-    ZStreamBuf() : inUse(false) { }
+-
+-    bool get(char*& out)
+-    {
+-      if (!inUse) {
+-        inUse = true;
+-        out = buf;
+-        return true;
+-      } else {
+-        return false;
+-      }
+-    }
+-
+-    void Release()
+-    {
+-      memset(buf, 0, Size);
+-      inUse = false;
+-    }
+-
+-    bool Equals(const void *other) { return other == buf; }
+-
+-    static const size_t size = Size;
+-
+-  private:
+-    char buf[Size];
+-    bool inUse;
+-  };
+-
+-public:
+-  /**
+-   * Special allocator that uses static buffers to allocate from.
+-   */
+-  class StaticAllocator
+-  {
+-  public:
+-    void *Alloc(uInt items, uInt size)
+-    {
+-      if (items == 1 && size <= stateBuf1.size) {
+-        char* res = nullptr;
+-        if (stateBuf1.get(res) || stateBuf2.get(res)) {
+-          return res;
+-        }
+-        MOZ_CRASH("ZStreamBuf already in use");
+-      } else if (items * size == windowBuf1.size) {
+-        char* res = nullptr;
+-        if (windowBuf1.get(res) || windowBuf2.get(res)) {
+-          return res;
+-        }
+-        MOZ_CRASH("ZStreamBuf already in use");
+-      } else {
+-        MOZ_CRASH("No ZStreamBuf for allocation");
+-      }
+-    }
+-
+-    void Free(void *ptr)
+-    {
+-      if (stateBuf1.Equals(ptr)) {
+-        stateBuf1.Release();
+-      } else if (stateBuf2.Equals(ptr)) {
+-        stateBuf2.Release();
+-      }else if (windowBuf1.Equals(ptr)) {
+-        windowBuf1.Release();
+-      } else if (windowBuf2.Equals(ptr)) {
+-        windowBuf2.Release();
+-      } else {
+-        MOZ_CRASH("Pointer doesn't match a ZStreamBuf");
+-      }
+-    }
+-
+-    // 0x3000 is an arbitrary size above 10K.
+-    ZStreamBuf<0x3000> stateBuf1, stateBuf2;
+-    ZStreamBuf<1 << MAX_WBITS> windowBuf1, windowBuf2;
+-  };
+-
+-private:
+-  StaticAllocator *allocator;
+-};
+-
+-/**
+- * Forward declaration
+- */
+-class ZipCollection;
+-
+-/**
+- * Class to handle access to Zip archive streams. The Zip archive is mapped
+- * in memory, and streams are direct references to that mapped memory.
+- * Zip files are assumed to be correctly formed. No boundary checks are
+- * performed, which means hand-crafted malicious Zip archives can make the
+- * code fail in bad ways. However, since the only intended use is to load
+- * libraries from Zip archives, there is no interest in making this code
+- * safe, since the libraries could contain malicious code anyways.
+- */
+-class Zip: public mozilla::external::AtomicRefCounted<Zip>
+-{
+-public:
+-  MOZ_DECLARE_REFCOUNTED_TYPENAME(Zip)
+-  /**
+-   * Create a Zip instance for the given file name. Returns nullptr in case
+-   * of failure.
+-   */
+-  static already_AddRefed<Zip> Create(const char *filename);
+-
+-  /**
+-   * Create a Zip instance using the given buffer.
+-   */
+-  static already_AddRefed<Zip> Create(void *buffer, size_t size) {
+-    return Create(nullptr, buffer, size);
+-  }
+-
+-private:
+-  static already_AddRefed<Zip> Create(const char *filename,
+-                                           void *buffer, size_t size);
+-
+-  /**
+-   * Private constructor
+-   */
+-  Zip(const char *filename, void *buffer, size_t size);
+-
+-public:
+-  /**
+-   * Destructor
+-   */
+-  ~Zip();
+-
+-  /**
+-   * Class used to access Zip archive item streams
+-   */
+-  class Stream
+-  {
+-  public:
+-    /**
+-     * Stream types
+-     */
+-    enum Type {
+-      STORE = 0,
+-      DEFLATE = 8
+-    };
+-
+-    /**
+-     * Constructor
+-     */
+-    Stream(): compressedBuf(nullptr), compressedSize(0), uncompressedSize(0)
+-            , CRC32(0)
+-            , type(STORE) { }
+-
+-    /**
+-     * Getters
+-     */
+-    const void *GetBuffer() { return compressedBuf; }
+-    size_t GetSize() { return compressedSize; }
+-    size_t GetUncompressedSize() { return uncompressedSize; }
+-    size_t GetCRC32() { return CRC32; }
+-    Type GetType() { return type; }
+-
+-    /**
+-     * Returns a zxx_stream for use with inflate functions using the given
+-     * buffer as inflate output. The caller is expected to allocate enough
+-     * memory for the Stream uncompressed size.
+-     */
+-    zxx_stream GetZStream(void *buf)
+-    {
+-      zxx_stream zStream;
+-      zStream.avail_in = compressedSize;
+-      zStream.next_in = reinterpret_cast<Bytef *>(
+-                        const_cast<void *>(compressedBuf));
+-      zStream.avail_out = uncompressedSize;
+-      zStream.next_out = static_cast<Bytef *>(buf);
+-      return zStream;
+-    }
+-
+-  protected:
+-    friend class Zip;
+-    const void *compressedBuf;
+-    size_t compressedSize;
+-    size_t uncompressedSize;
+-    size_t CRC32;
+-    Type type;
+-  };
+-
+-  /**
+-   * Returns a stream from the Zip archive.
+-   */
+-  bool GetStream(const char *path, Stream *out) const;
+-
+-  /**
+-   * Returns the file name of the archive
+-   */
+-  const char *GetName() const
+-  {
+-    return name;
+-  }
+-
+-private:
+-  /* File name of the archive */
+-  char *name;
+-  /* Address where the Zip archive is mapped */
+-  void *mapped;
+-  /* Size of the archive */
+-  size_t size;
+-
+-  /**
+-   * Strings (file names, comments, etc.) in the Zip headers are NOT zero
+-   * terminated. This class is a helper around them.
+-   */
+-  class StringBuf
+-  {
+-  public:
+-    /**
+-     * Constructor
+-     */
+-    StringBuf(const char *buf, size_t length): buf(buf), length(length) { }
+-
+-    /**
+-     * Returns whether the string has the same content as the given zero
+-     * terminated string.
+-     */
+-    bool Equals(const char *str) const
+-    {
+-      return (strncmp(str, buf, length) == 0 && str[length] == '\0');
+-    }
+-
+-  private:
+-    const char *buf;
+-    size_t length;
+-  };
+-
+-/* All the following types need to be packed */
+-#pragma pack(1)
+-public:
+-  /**
+-   * A Zip archive is an aggregate of entities which all start with a
+-   * signature giving their type. This template is to be used as a base
+-   * class for these entities.
+-   */
+-  template <typename T>
+-  class SignedEntity
+-  {
+-  public:
+-    /**
+-     * Equivalent to reinterpret_cast<const T *>(buf), with an additional
+-     * check of the signature.
+-     */
+-    static const T *validate(const void *buf)
+-    {
+-      const T *ret = static_cast<const T *>(buf);
+-      if (ret->signature == T::magic)
+-        return ret;
+-      return nullptr;
+-    }
+-
+-    SignedEntity(uint32_t magic): signature(magic) { }
+-  private:
+-    le_uint32 signature;
+-  };
+-
+-private:
+-  /**
+-   * Header used to describe a Local File entry. The header is followed by
+-   * the file name and an extra field, then by the data stream.
+-   */
+-  struct LocalFile: public SignedEntity<LocalFile>
+-  {
+-    /* Signature for a Local File header */
+-    static const uint32_t magic = 0x04034b50;
+-
+-    /**
+-     * Returns the file name
+-     */
+-    StringBuf GetName() const
+-    {
+-      return StringBuf(reinterpret_cast<const char *>(this) + sizeof(*this),
+-                       filenameSize);
+-    }
+-
+-    /**
+-     * Returns a pointer to the data associated with this header
+-     */
+-    const void *GetData() const
+-    {
+-      return reinterpret_cast<const char *>(this) + sizeof(*this)
+-             + filenameSize + extraFieldSize;
+-    }
+-
+-    le_uint16 minVersion;
+-    le_uint16 generalFlag;
+-    le_uint16 compression;
+-    le_uint16 lastModifiedTime;
+-    le_uint16 lastModifiedDate;
+-    le_uint32 CRC32;
+-    le_uint32 compressedSize;
+-    le_uint32 uncompressedSize;
+-    le_uint16 filenameSize;
+-    le_uint16 extraFieldSize;
+-  };
+-
+-  /**
+-   * In some cases, when a zip archive is created, compressed size and CRC
+-   * are not known when writing the Local File header. In these cases, the
+-   * 3rd bit of the general flag in the Local File header is set, and there
+-   * is an additional header following the compressed data.
+-   */
+-  struct DataDescriptor: public SignedEntity<DataDescriptor>
+-  {
+-    /* Signature for a Data Descriptor header */
+-    static const uint32_t magic = 0x08074b50;
+-
+-    le_uint32 CRC32;
+-    le_uint32 compressedSize;
+-    le_uint32 uncompressedSize;
+-  };
+-
+-  /**
+-   * Header used to describe a Central Directory Entry. The header is
+-   * followed by the file name, an extra field, and a comment.
+-   */
+-  struct DirectoryEntry: public SignedEntity<DirectoryEntry>
+-  {
+-    /* Signature for a Central Directory Entry header */
+-    static const uint32_t magic = 0x02014b50;
+-
+-    /**
+-     * Returns the file name
+-     */
+-    StringBuf GetName() const
+-    {
+-      return StringBuf(reinterpret_cast<const char *>(this) + sizeof(*this),
+-                       filenameSize);
+-    }
+-
+-    /**
+-     * Returns  the Central Directory Entry following this one.
+-     */
+-    const DirectoryEntry *GetNext() const
+-    {
+-      return validate(reinterpret_cast<const char *>(this) + sizeof(*this)
+-                      + filenameSize + extraFieldSize + fileCommentSize);
+-    }
+-
+-    le_uint16 creatorVersion;
+-    le_uint16 minVersion;
+-    le_uint16 generalFlag;
+-    le_uint16 compression;
+-    le_uint16 lastModifiedTime;
+-    le_uint16 lastModifiedDate;
+-    le_uint32 CRC32;
+-    le_uint32 compressedSize;
+-    le_uint32 uncompressedSize;
+-    le_uint16 filenameSize;
+-    le_uint16 extraFieldSize;
+-    le_uint16 fileCommentSize;
+-    le_uint16 diskNum;
+-    le_uint16 internalAttributes;
+-    le_uint32 externalAttributes;
+-    le_uint32 offset;
+-  };
+-
+-  /**
+-   * Header used to describe the End of Central Directory Record.
+-   */
+-  struct CentralDirectoryEnd: public SignedEntity<CentralDirectoryEnd>
+-  {
+-    /* Signature for the End of Central Directory Record */
+-    static const uint32_t magic = 0x06054b50;
+-
+-    le_uint16 diskNum;
+-    le_uint16 startDisk;
+-    le_uint16 recordsOnDisk;
+-    le_uint16 records;
+-    le_uint32 size;
+-    le_uint32 offset;
+-    le_uint16 commentSize;
+-  };
+-#pragma pack()
+-
+-  /**
+-   * Returns the first Directory entry
+-   */
+-  const DirectoryEntry *GetFirstEntry() const;
+-
+-  /* Pointer to the Local File Entry following the last one GetStream() used.
+-   * This is used by GetStream to avoid scanning the Directory Entries when the
+-   * requested entry is that one. */
+-  mutable const LocalFile *nextFile;
+-
+-  /* Likewise for the next Directory entry */
+-  mutable const DirectoryEntry *nextDir;
+-
+-  /* Pointer to the Directory entries */
+-  mutable const DirectoryEntry *entries;
+-
+-  mutable pthread_mutex_t mutex;
+-};
+-
+-/**
+- * Class for bookkeeping Zip instances
+- */
+-class ZipCollection
+-{
+-public:
+-  static ZipCollection Singleton;
+-
+-  /**
+-   * Get a Zip instance for the given path. If there is an existing one
+-   * already, return that one, otherwise create a new one.
+-   */
+-  static already_AddRefed<Zip> GetZip(const char *path);
+-
+-protected:
+-  friend class Zip;
+-  friend class mozilla::detail::RefCounted<Zip, mozilla::detail::AtomicRefCount>;
+-
+-  /**
+-   * Register the given Zip instance. This method is meant to be called
+-   * by Zip::Create.
+-   */
+-  static void Register(Zip *zip);
+-
+-  /**
+-   * Forget about the given Zip instance. This method is meant to be called
+-   * by the Zip destructor.
+-   */
+-  static void Forget(const Zip *zip);
+-
+-private:
+-  /* Zip instances bookkept in this collection */
+-  std::vector<RefPtr<Zip>> zips;
+-};
+-
+-namespace mozilla {
+-namespace detail {
+-
+-template<>
+-inline void
+-RefCounted<Zip, AtomicRefCount>::Release() const
+-{
+-  MOZ_ASSERT(static_cast<int32_t>(mRefCnt) > 0);
+-  const auto count = --mRefCnt;
+-  if (count == 1) {
+-    // No external references are left, attempt to remove it from the collection.
+-    // If it's successfully removed from the collection, Release() will be called
+-    // with mRefCnt = 1, which will finally delete this zip.
+-    ZipCollection::Forget(static_cast<const Zip*>(this));
+-  } else if (count == 0) {
+-#ifdef DEBUG
+-    mRefCnt = detail::DEAD;
+-#endif
+-    delete static_cast<const Zip*>(this);
+-  }
+-}
+-
+-template<>
+-inline
+-RefCounted<Zip, AtomicRefCount>::~RefCounted()
+-{
+-  MOZ_ASSERT(mRefCnt == detail::DEAD);
+-}
+-
+-} // namespace detail
+-} // namespace mozilla
+-
+-
+-
+-#endif /* Zip_h */
+diff --git a/mozglue/linker/dladdr.h b/mozglue/linker/dladdr.h
+deleted file mode 100644
+--- a/mozglue/linker/dladdr.h
++++ /dev/null
+@@ -1,16 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this
+- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#include <dlfcn.h>
+-
+-#ifndef HAVE_DLADDR
+-typedef struct {
+-  const char *dli_fname;
+-  void *dli_fbase;
+-  const char *dli_sname;
+-  void *dli_saddr;
+-} Dl_info;
+-extern int dladdr(void *addr, Dl_info *info) __attribute__((weak));
+-#define HAVE_DLADDR 1
+-#endif
+diff --git a/mozglue/linker/moz.build b/mozglue/linker/moz.build
+deleted file mode 100644
+--- a/mozglue/linker/moz.build
++++ /dev/null
+@@ -1,33 +0,0 @@
+-# -*- 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/.
+-
+-SOURCES += [
+-    'BaseElf.cpp',
+-    'CustomElf.cpp',
+-    'ElfLoader.cpp',
+-    'Mappable.cpp',
+-    'XZStream.cpp',
+-    'Zip.cpp',
+-]
+-
+-Library('linker')
+-
+-FINAL_LIBRARY = 'mozglue'
+-
+-DEFINES['IMPL_MFBT'] = True
+-
+-DisableStlWrapping()
+-
+-TEST_DIRS += ['tests']
+-
+-if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
+-    CXXFLAGS += ['-Wno-error=shadow']
+-
+-DEFINES['XZ_USE_CRC64'] = 1
+-
+-USE_LIBS += [
+-    'xz-embedded',
+-]
+diff --git a/mozglue/linker/tests/TestZip.cpp b/mozglue/linker/tests/TestZip.cpp
+deleted file mode 100644
+--- a/mozglue/linker/tests/TestZip.cpp
++++ /dev/null
+@@ -1,69 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+- * You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-#include <cstdio>
+-#include <unistd.h>
+-#include "Zip.h"
+-#include "mozilla/RefPtr.h"
+-
+-extern "C" void report_mapping() { }
+-extern "C" void delete_mapping() { }
+-
+-/**
+- * test.zip is a basic test zip file with a central directory. It contains
+- * four entries, in the following order:
+- * "foo", "bar", "baz", "qux".
+- * The entries are going to be read out of order.
+- */
+-const char *test_entries[] = {
+-  "baz", "foo", "bar", "qux"
+-};
+-
+-/**
+- * no_central_dir.zip is a hand crafted test zip with no central directory
+- * entries. The Zip reader is expected to be able to traverse these entries
+- * if requested in order, without reading a central directory
+- * - First entry is a file "a", STOREd.
+- * - Second entry is a file "b", STOREd, using a data descriptor. CRC is
+- *   unknown, but compressed and uncompressed sizes are known in the local
+- *   file header.
+- * - Third entry is a file "c", DEFLATEd, using a data descriptor. CRC,
+- *   compressed and uncompressed sizes are known in the local file header.
+- *   This is the kind of entry that can be found in a zip that went through
+- *   zipalign if it had a data descriptor originally.
+- * - Fourth entry is a file "d", STOREd.
+- */
+-const char *no_central_dir_entries[] = {
+-  "a", "b", "c", "d"
+-};
+-
+-int main(int argc, char *argv[])
+-{
+-  if (argc != 2) {
+-    fprintf(stderr, "TEST-FAIL | TestZip | Expecting the directory containing test Zips\n");
+-    return 1;
+-  }
+-  chdir(argv[1]);
+-  Zip::Stream s;
+-  RefPtr<Zip> z = ZipCollection::GetZip("test.zip");
+-  for (size_t i = 0; i < sizeof(test_entries) / sizeof(*test_entries); i++) {
+-    if (!z->GetStream(test_entries[i], &s)) {
+-      fprintf(stderr, "TEST-UNEXPECTED-FAIL | TestZip | test.zip: Couldn't get entry \"%s\"\n", test_entries[i]);
+-      return 1;
+-    }
+-  }
+-  fprintf(stderr, "TEST-PASS | TestZip | test.zip could be accessed fully\n");
+-
+-  z = ZipCollection::GetZip("no_central_dir.zip");
+-  for (size_t i = 0; i < sizeof(no_central_dir_entries)
+-                         / sizeof(*no_central_dir_entries); i++) {
+-    if (!z->GetStream(no_central_dir_entries[i], &s)) {
+-      fprintf(stderr, "TEST-UNEXPECTED-FAIL | TestZip | no_central_dir.zip: Couldn't get entry \"%s\"\n", no_central_dir_entries[i]);
+-      return 1;
+-    }
+-  }
+-  fprintf(stderr, "TEST-PASS | TestZip | no_central_dir.zip could be accessed in order\n");
+-
+-  return 0;
+-}
+diff --git a/mozglue/linker/tests/moz.build b/mozglue/linker/tests/moz.build
+deleted file mode 100644
+--- a/mozglue/linker/tests/moz.build
++++ /dev/null
+@@ -1,26 +0,0 @@
+-# -*- 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/.
+-
+-DIST_INSTALL = False
+-
+-SimplePrograms([
+-    'TestZip',
+-])
+-LOCAL_INCLUDES += ['..']
+-USE_LIBS += [
+-    'linker',
+-    'mfbt',
+-]
+-if CONFIG['ZLIB_IN_MOZGLUE']:
+-    USE_LIBS += [ 'modules_zlib_src' ]
+-else:
+-    OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
+-DisableStlWrapping()
+-
+-PYTHON_UNITTEST_MANIFESTS += ['python.ini']
+-
+-if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
+-    CXXFLAGS += ['-Wno-error=shadow']
+diff --git a/mozglue/linker/tests/no_central_dir.zip b/mozglue/linker/tests/no_central_dir.zip
+deleted file mode 100644
+index df882220d19f3da6c90426f5c6d998cf4d4ffc20..0000000000000000000000000000000000000000
+GIT binary patch
+literal 0
+Hc$@<O00001
+
+diff --git a/mozglue/linker/tests/python.ini b/mozglue/linker/tests/python.ini
+deleted file mode 100644
+--- a/mozglue/linker/tests/python.ini
++++ /dev/null
+@@ -1,4 +0,0 @@
+-[DEFAULT]
+-skip-if = python == 3
+-
+-[run_test_zip.py]
+diff --git a/mozglue/linker/tests/run_test_zip.py b/mozglue/linker/tests/run_test_zip.py
+deleted file mode 100644
+--- a/mozglue/linker/tests/run_test_zip.py
++++ /dev/null
+@@ -1,21 +0,0 @@
+-#!/usr/bin/env python
+-#
+-# Any copyright is dedicated to the Public Domain.
+-# http://creativecommons.org/publicdomain/zero/1.0/
+-
+-import buildconfig
+-import mozpack.path as mozpath
+-import mozunit
+-import subprocess
+-import unittest
+-
+-class TestZip(unittest.TestCase):
+-    def test_zip(self):
+-        srcdir = mozpath.dirname(__file__)
+-        relsrcdir = mozpath.relpath(srcdir, buildconfig.topsrcdir)
+-        test_bin = mozpath.join(buildconfig.topobjdir, relsrcdir,
+-                                'TestZip' + buildconfig.substs['BIN_SUFFIX'])
+-        self.assertEqual(0, subprocess.call([test_bin, srcdir]))
+-
+-if __name__ == '__main__':
+-    mozunit.main()
+diff --git a/mozglue/linker/tests/test.zip b/mozglue/linker/tests/test.zip
+deleted file mode 100644
+index 657835b0cac2fd84b1631590a4cf6b7181b78c60..0000000000000000000000000000000000000000
+GIT binary patch
+literal 0
+Hc$@<O00001
+
+diff --git a/mozglue/moz.build b/mozglue/moz.build
+--- a/mozglue/moz.build
++++ b/mozglue/moz.build
+@@ -2,18 +2,15 @@
+ # 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/.
+ 
+ with Files("**"):
+     BUG_COMPONENT = ("Core", "mozglue")
+ 
+-if CONFIG['MOZ_LINKER']:
+-    DIRS += ['linker']
+-
+ DIRS += [
+   'build',
+   'misc',
+ ]
+ 
+ if CONFIG['MOZ_WIDGET_TOOLKIT']:
+     TEST_DIRS += ['tests']
+diff --git a/old-configure.in b/old-configure.in
+--- a/old-configure.in
++++ b/old-configure.in
+@@ -601,20 +601,16 @@ case "$target" in
+     fi
+ 
+     MOZ_FIX_LINK_PATHS=
+     ;;
+ 
+ *-android*|*-linuxandroid*)
+     AC_DEFINE(NO_PW_GECOS)
+ 
+-    if test "$COMPILE_ENVIRONMENT"; then
+-        MOZ_LINKER=1
+-    fi
+-
+     MOZ_GFX_OPTIMIZE_MOBILE=1
+     if test -z "$CLANG_CC"; then
+         MOZ_OPTIMIZE_FLAGS="-freorder-blocks -fno-reorder-functions -Os"
+     else
+         # From https://github.com/android-ndk/ndk/issues/133#issuecomment-308549264
+         # -Oz is smaller than -Os on clang.
+         MOZ_OPTIMIZE_FLAGS="-Oz"
+     fi
+@@ -933,23 +929,16 @@ if test -z "$MOZ_OPTIMIZE_FLAGS"; then
+     MOZ_OPTIMIZE_FLAGS="-O"
+ fi
+ 
+ AC_SUBST_LIST(MMX_FLAGS)
+ AC_SUBST_LIST(SSE_FLAGS)
+ AC_SUBST_LIST(SSE2_FLAGS)
+ AC_SUBST_LIST(SSSE3_FLAGS)
+ 
+-AC_SUBST(MOZ_LINKER)
+-if test -n "$MOZ_LINKER"; then
+-  AC_DEFINE(MOZ_LINKER)
+-  MOZ_LINKER_EXTRACT=1
+-  AC_CHECK_PROGS(XZ, xz)
+-fi
+-
+ if test -z "$COMPILE_ENVIRONMENT"; then
+     SKIP_COMPILER_CHECKS=1
+     SKIP_LIBRARY_CHECKS=1
+     MOZ_DEBUGGING_OPTS
+ else
+     MOZ_COMPILER_OPTS
+ fi # COMPILE_ENVIRONMENT
+ 
+@@ -1391,17 +1380,17 @@ AC_CACHE_CHECK(for __thread keyword for 
+                ac_cv_thread_keyword,
+                [AC_TRY_LINK([__thread bool tlsIsMainThread = false;],
+                             [return tlsIsMainThread;],
+                             ac_cv_thread_keyword=yes,
+                             ac_cv_thread_keyword=no)])
+ LDFLAGS=$_SAVE_LDFLAGS
+ # The custom dynamic linker doesn't support TLS variables
+ MOZ_TLS=
+-if test "$ac_cv_thread_keyword" = yes -a "$MOZ_LINKER" != 1; then
++if test "$ac_cv_thread_keyword" = yes; then
+   # mips builds fail with TLS variables because of a binutils bug.
+   # See bug 528687
+   # OpenBSD doesn't have TLS support, and the test succeeds with clang++
+   case "${target}" in
+     mips*-*)
+       :
+       ;;
+     *-android*|*-linuxandroid*)
+@@ -1412,33 +1401,16 @@ if test "$ac_cv_thread_keyword" = yes -a
+       ;;
+     *)
+       AC_DEFINE(HAVE_THREAD_TLS_KEYWORD)
+       MOZ_TLS=1
+       ;;
+   esac
+ fi
+ 
+-dnl Using the custom linker on ARMv6 requires 16k alignment of ELF segments.
+-if test -n "$MOZ_LINKER"; then
+-  if test "$CPU_ARCH" = arm; then
+-    dnl When building for < ARMv7, we need to ensure 16k alignment of ELF segments
+-    if test -n "$ARM_ARCH" && test "$ARM_ARCH" -lt 7; then
+-      LDFLAGS="$LDFLAGS -Wl,-z,max-page-size=0x4000 -Wl,-z,common-page-size=0x4000"
+-      _SUBDIR_LDFLAGS="$_SUBDIR_LDFLAGS -Wl,-z,max-page-size=0x4000 -Wl,-z,common-page-size=0x4000"
+-    fi
+-  fi
+-
+-dnl gold emits wrong sysv-style elf hash tables when building both sysv and
+-dnl style tables. https://sourceware.org/bugzilla/show_bug.cgi?id=13597
+-dnl Since the linker only understands the sysv ones, no need to build the
+-dnl gnu style tables anyways.
+-  LDFLAGS="$LDFLAGS -Wl,--hash-style=sysv"
+-fi
+-
+ dnl End of C++ language/feature checks
+ AC_LANG_C
+ 
+ dnl ========================================================
+ dnl =  Internationalization checks
+ dnl ========================================================
+ dnl
+ dnl Internationalization and Locale support is different
+@@ -2483,17 +2455,17 @@ fi # COMPILE_ENVIRONMENT
+ dnl ========================================================
+ dnl =
+ dnl = Static Build Options
+ dnl =
+ dnl ========================================================
+ MOZ_ARG_HEADER(Static build options)
+ 
+ if test -z "$MOZ_SYSTEM_ZLIB"; then
+-if test -n "$JS_SHARED_LIBRARY" -o -n "$MOZ_LINKER"; then
++if test -n "$JS_SHARED_LIBRARY"; then
+   ZLIB_IN_MOZGLUE=1
+   AC_DEFINE(ZLIB_IN_MOZGLUE)
+ fi
+ fi
+ 
+ AC_SUBST(ZLIB_IN_MOZGLUE)
+ 
+ dnl ========================================================
+@@ -2742,17 +2714,16 @@ AC_SUBST(MOZ_ANDROID_APPLICATION_CLASS)
+ AC_SUBST(MOZ_ANDROID_BROWSER_INTENT_CLASS)
+ AC_SUBST(MOZ_EXCLUDE_HYPHENATION_DICTIONARIES)
+ AC_SUBST(STRIP_FLAGS)
+ AC_SUBST(INCREMENTAL_LINKER)
+ 
+ AC_SUBST_LIST(MOZ_FIX_LINK_PATHS)
+ 
+ AC_SUBST(MOZ_POST_PROGRAM_COMMAND)
+-AC_SUBST(MOZ_LINKER_EXTRACT)
+ 
+ if test -n "$MOZ_BINARY_EXTENSIONS"; then
+   AC_DEFINE(MOZ_BINARY_EXTENSIONS)
+ fi
+ 
+ AC_SUBST(MOZ_ADDON_SIGNING)
+ if test "$MOZ_ADDON_SIGNING" = 1; then
+   AC_DEFINE(MOZ_ADDON_SIGNING)
+diff --git a/testing/xpcshell/remotexpcshelltests.py b/testing/xpcshell/remotexpcshelltests.py
+--- a/testing/xpcshell/remotexpcshelltests.py
++++ b/testing/xpcshell/remotexpcshelltests.py
+@@ -341,17 +341,16 @@ class XPCShellRemote(xpcshell.XPCShellTe
+         self.device.pushFile(localWrapper, self.remoteClearDirScript)
+         os.remove(localWrapper)
+ 
+         self.device.chmodDir(self.remoteBinDir)
+ 
+     def buildEnvironment(self):
+         self.buildCoreEnvironment()
+         self.setLD_LIBRARY_PATH()
+-        self.env["MOZ_LINKER_CACHE"] = self.remoteBinDir
+         if self.options['localAPK'] and self.appRoot:
+             self.env["GRE_HOME"] = self.appRoot
+         self.env["XPCSHELL_TEST_PROFILE_DIR"] = self.profileDir
+         self.env["TMPDIR"] = self.remoteTmpDir
+         self.env["HOME"] = self.profileDir
+         self.env["XPCSHELL_TEST_TEMP_DIR"] = self.remoteTmpDir
+         self.env["XPCSHELL_MINIDUMP_DIR"] = self.remoteMinidumpDir
+         if self.options['setup']:
+diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp
+--- a/toolkit/xre/nsAppRunner.cpp
++++ b/toolkit/xre/nsAppRunner.cpp
+@@ -275,20 +275,16 @@ nsString gAbsoluteArgv0Path;
+ #endif
+ #ifdef MOZ_X11
+ #include <gdk/gdkx.h>
+ #endif /* MOZ_X11 */
+ #include <fontconfig/fontconfig.h>
+ #endif
+ #include "BinaryPath.h"
+ 
+-#ifdef MOZ_LINKER
+-extern "C" MFBT_API bool IsSignalHandlingBroken();
+-#endif
+-
+ #ifdef FUZZING
+ #include "FuzzerRunner.h"
+ 
+ namespace mozilla {
+ FuzzerRunner* fuzzerRunner = 0;
+ } // namespace mozilla
+ 
+ #ifdef LIBFUZZER
+@@ -3343,21 +3339,16 @@ XREMain::XRE_mainInit(bool* aExitFlag)
+                                          nsDependentCString(mAppData->buildID));
+ 
+     nsDependentCString packageVersion(NS_STRINGIFY(MOZ_PKG_VERSION));
+     CrashReporter::AnnotateCrashReport(
+       CrashReporter::Annotation::PackageVersion, packageVersion);
+     nsDependentCString releaseChannel(NS_STRINGIFY(MOZ_UPDATE_CHANNEL));
+     CrashReporter::AnnotateCrashReport(
+       CrashReporter::Annotation::ReleaseChannel, releaseChannel);
+-#ifdef MOZ_LINKER
+-    CrashReporter::AnnotateCrashReport(
+-      CrashReporter::Annotation::CrashAddressLikelyWrong,
+-      IsSignalHandlingBroken());
+-#endif
+ 
+ #ifdef XP_WIN
+     nsAutoString appInitDLLs;
+     if (widget::WinUtils::GetAppInitDLLs(appInitDLLs)) {
+       CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AppInitDLLs,
+                                          NS_ConvertUTF16toUTF8(appInitDLLs));
+     }
+ #endif
+diff --git a/tools/profiler/moz.build b/tools/profiler/moz.build
+--- a/tools/profiler/moz.build
++++ b/tools/profiler/moz.build
+@@ -80,17 +80,16 @@ if CONFIG['MOZ_GECKO_PROFILER']:
+         SOURCES += [
+             'core/shared-libraries-win32.cc',
+         ]
+ 
+     LOCAL_INCLUDES += [
+         '/caps',
+         '/docshell/base',
+         '/ipc/chromium/src',
+-        '/mozglue/linker',
+         '/toolkit/crashreporter/google-breakpad/src',
+         '/tools/profiler/core/',
+         '/tools/profiler/gecko/',
+         '/xpcom/base',
+     ]
+ 
+     if CONFIG['OS_TARGET'] == 'Android':
+         DEFINES['ANDROID_NDK_MAJOR_VERSION'] = CONFIG['ANDROID_NDK_MAJOR_VERSION']
+diff --git a/xpcom/glue/standalone/nsXPCOMGlue.cpp b/xpcom/glue/standalone/nsXPCOMGlue.cpp
+--- a/xpcom/glue/standalone/nsXPCOMGlue.cpp
++++ b/xpcom/glue/standalone/nsXPCOMGlue.cpp
+@@ -75,28 +75,16 @@ static void
+ CloseLibHandle(LibHandleType aLibHandle)
+ {
+   FreeLibrary(aLibHandle);
+ }
+ 
+ #else
+ #include <dlfcn.h>
+ 
+-#if defined(MOZ_LINKER)
+-extern "C" {
+-NS_HIDDEN __typeof(dlopen) __wrap_dlopen;
+-NS_HIDDEN __typeof(dlsym) __wrap_dlsym;
+-NS_HIDDEN __typeof(dlclose) __wrap_dlclose;
+-}
+-
+-#define dlopen __wrap_dlopen
+-#define dlsym __wrap_dlsym
+-#define dlclose __wrap_dlclose
+-#endif
+-
+ typedef void* LibHandleType;
+ 
+ static LibHandleType
+ GetLibHandle(pathstr_t aDependentLib)
+ {
+   LibHandleType libHandle = dlopen(aDependentLib,
+                                    RTLD_GLOBAL | RTLD_LAZY
+ #ifdef XP_MACOSX
+@@ -111,24 +99,22 @@ GetLibHandle(pathstr_t aDependentLib)
+ }
+ 
+ static NSFuncPtr
+ GetSymbol(LibHandleType aLibHandle, const char* aSymbol)
+ {
+   return (NSFuncPtr)dlsym(aLibHandle, aSymbol);
+ }
+ 
+-#ifndef MOZ_LINKER
+ static void
+ CloseLibHandle(LibHandleType aLibHandle)
+ {
+   dlclose(aLibHandle);
+ }
+ #endif
+-#endif
+ 
+ struct DependentLib
+ {
+   LibHandleType libHandle;
+   DependentLib* next;
+ };
+ 
+ static DependentLib* sTop;
+@@ -145,20 +131,18 @@ AppendDependentLib(LibHandleType aLibHan
+   d->libHandle = aLibHandle;
+ 
+   sTop = d;
+ }
+ 
+ static bool
+ ReadDependentCB(pathstr_t aDependentLib)
+ {
+-#ifndef MOZ_LINKER
+   // We do this unconditionally because of data in bug 771745
+   ReadAheadLib(aDependentLib);
+-#endif
+   LibHandleType libHandle = GetLibHandle(aDependentLib);
+   if (libHandle) {
+     AppendDependentLib(libHandle);
+   }
+ 
+   return libHandle;
+ }
+ 
+@@ -195,30 +179,28 @@ struct ScopedCloseFileTraits
+   {
+     if (aFile) {
+       fclose(aFile);
+     }
+   }
+ };
+ typedef Scoped<ScopedCloseFileTraits> ScopedCloseFile;
+ 
+-#ifndef MOZ_LINKER
+ static void
+ XPCOMGlueUnload()
+ {
+   while (sTop) {
+     CloseLibHandle(sTop->libHandle);
+ 
+     DependentLib* temp = sTop;
+     sTop = sTop->next;
+ 
+     delete temp;
+   }
+ }
+-#endif
+ 
+ #if defined(XP_WIN)
+ // like strpbrk but finds the *last* char, not the first
+ static const char*
+ ns_strrpbrk(const char* string, const char* strCharSet)
+ {
+   const char* found = nullptr;
+   for (; *string; ++string) {
+@@ -233,21 +215,16 @@ ns_strrpbrk(const char* string, const ch
+ 
+   return found;
+ }
+ #endif
+ 
+ static nsresult
+ XPCOMGlueLoad(const char* aXPCOMFile)
+ {
+-#ifdef MOZ_LINKER
+-  if (!ReadDependentCB(aXPCOMFile)) {
+-    return NS_ERROR_FAILURE;
+-  }
+-#else
+   char xpcomDir[MAXPATHLEN];
+ #ifdef XP_WIN
+   const char* lastSlash = ns_strrpbrk(aXPCOMFile, "/\\");
+ #elif XP_MACOSX
+   // On OSX, the dependentlibs.list file lives under Contents/Resources.
+   // However, the actual libraries listed in dependentlibs.list live under
+   // Contents/MacOS. We want to read the list from Contents/Resources, then
+   // load the libraries from Contents/MacOS.
+@@ -330,17 +307,16 @@ XPCOMGlueLoad(const char* aXPCOMFile)
+     }
+ 
+     strcpy(cursor, buffer);
+     if (!ReadDependentCB(xpcomDir)) {
+       XPCOMGlueUnload();
+       return NS_ERROR_FAILURE;
+     }
+   }
+-#endif
+   return NS_OK;
+ }
+ 
+ #if defined(MOZ_WIDGET_GTK) && (defined(MOZ_MEMORY) || defined(__FreeBSD__) || defined(__NetBSD__))
+ #define MOZ_GSLICE_INIT
+ #endif
+ 
+ #ifdef MOZ_GSLICE_INIT

+ 35 - 0
frg/work-js/mozilla-release/patches/NOBUG-removessdks-25319.patch

@@ -0,0 +1,35 @@
+# HG changeset patch
+# User Frank-Rainer Grahl <frgrahl@gmx.net>
+# Date 1705406924 -3600
+# Parent  8dc104d0262ddbe0b48cff08d3132592240f50c9
+No Bug - Remove unsued sdks from build files. r=me a=me
+
+Leanplum and friends produce variables in config.status and are useless for us.
+We keep the underlying python functions generationg the variables for now.
+
+diff --git a/toolkit/moz.configure b/toolkit/moz.configure
+--- a/toolkit/moz.configure
++++ b/toolkit/moz.configure
+@@ -625,22 +625,16 @@ check_prog('GN', ('gn',), allow_missing=
+ include('../build/moz.configure/keyfiles.configure')
+ 
+ simple_keyfile('Mozilla API')
+ 
+ simple_keyfile('Google Location Service API')
+ 
+ simple_keyfile('Google Safebrowsing API')
+ 
+-id_and_secret_keyfile('Bing API')
+-
+-simple_keyfile('Adjust SDK')
+-
+-id_and_secret_keyfile('Leanplum SDK')
+-
+ # Servo integration
+ # ==============================================================
+ option('--enable-stylo', nargs='?', choices=('build', 'only'),
+        help='Include Stylo in the build.  "build" means to disable Stylo at ' +
+             'runtime, and "only" means to exclude the old style system from ' +
+             'the build.')
+ 
+ @depends('--enable-stylo', '--help')

+ 94 - 0
frg/work-js/mozilla-release/patches/TOP-1472170-PARTIAL-NOTESTS-63a1.patch

@@ -0,0 +1,94 @@
+# HG changeset patch
+# User omersid <omersid@nomailaddy.com>
+# Date 1535426094 14400
+# Node ID 709590a1a24fc8d55f891204dd1e105eaf787177
+# Parent  f24d56bf25339e20635838a4d5d7ce3bcce2fad6
+Bug 1472170- Added description as a property for Symbol and updated tests262. r=anba
+
+diff --git a/js/src/builtin/Symbol.cpp b/js/src/builtin/Symbol.cpp
+--- a/js/src/builtin/Symbol.cpp
++++ b/js/src/builtin/Symbol.cpp
+@@ -26,16 +26,17 @@ SymbolObject::create(JSContext* cx, JS::
+     SymbolObject* obj = NewBuiltinClassInstance<SymbolObject>(cx);
+     if (!obj)
+         return nullptr;
+     obj->setPrimitiveValue(symbol);
+     return obj;
+ }
+ 
+ const JSPropertySpec SymbolObject::properties[] = {
++    JS_PSG("description", descriptionGetter, 0),
+     JS_PS_END
+ };
+ 
+ const JSFunctionSpec SymbolObject::methods[] = {
+     JS_FN(js_toString_str, toString, 0, 0),
+     JS_FN(js_valueOf_str, valueOf, 0, 0),
+     JS_SYM_FN(toPrimitive, toPrimitive, 1, JSPROP_READONLY),
+     JS_FS_END
+@@ -226,16 +227,41 @@ SymbolObject::toPrimitive(JSContext* cx,
+ {
+     CallArgs args = CallArgsFromVp(argc, vp);
+ 
+     // The specification gives exactly the same algorithm for @@toPrimitive as
+     // for valueOf, so reuse the valueOf implementation.
+     return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args);
+ }
+ 
++bool
++SymbolObject::descriptionGetter_impl(JSContext* cx, const CallArgs& args)
++{
++    // Get symbol object pointer.
++    HandleValue thisv = args.thisv();
++    MOZ_ASSERT(IsSymbol(thisv));
++    Rooted<Symbol*> sym(cx, thisv.isSymbol()
++                            ? thisv.toSymbol()
++                            : thisv.toObject().as<SymbolObject>().unbox());
++
++    // Return the symbol's description if present, otherwise return undefined.
++    if (JSString* str = sym->description())
++        args.rval().setString(str);
++    else
++        args.rval().setUndefined();
++    return true;
++}
++
++bool
++SymbolObject::descriptionGetter(JSContext* cx, unsigned argc, Value* vp)
++{
++    CallArgs args = CallArgsFromVp(argc, vp);
++    return CallNonGenericMethod<IsSymbol, descriptionGetter_impl>(cx, args);
++}
++
+ JSObject*
+ js::InitSymbolClass(JSContext* cx, Handle<GlobalObject*> global)
+ {
+     return SymbolObject::initClass(cx, global, true);
+ }
+ 
+ JSObject*
+ js::InitBareSymbolCtor(JSContext* cx, Handle<GlobalObject*> global)
+diff --git a/js/src/builtin/Symbol.h b/js/src/builtin/Symbol.h
+--- a/js/src/builtin/Symbol.h
++++ b/js/src/builtin/Symbol.h
+@@ -49,16 +49,20 @@ class SymbolObject : public NativeObject
+ 
+     // Methods defined on Symbol.prototype.
+     static MOZ_MUST_USE bool toString_impl(JSContext* cx, const CallArgs& args);
+     static MOZ_MUST_USE bool toString(JSContext* cx, unsigned argc, Value* vp);
+     static MOZ_MUST_USE bool valueOf_impl(JSContext* cx, const CallArgs& args);
+     static MOZ_MUST_USE bool valueOf(JSContext* cx, unsigned argc, Value* vp);
+     static MOZ_MUST_USE bool toPrimitive(JSContext* cx, unsigned argc, Value* vp);
+ 
++    // Properties defined on Symbol.prototype.
++    static MOZ_MUST_USE bool descriptionGetter_impl(JSContext* cx, const CallArgs& args);
++    static MOZ_MUST_USE bool descriptionGetter(JSContext* cx, unsigned argc, Value *vp);
++
+     static const JSPropertySpec properties[];
+     static const JSFunctionSpec methods[];
+     static const JSFunctionSpec staticMethods[];
+ };
+ 
+ extern JSObject*
+ InitSymbolClass(JSContext* cx, Handle<GlobalObject*> global);
+ 

+ 97 - 0
frg/work-js/mozilla-release/patches/TOP-1472170-PARTIAL-NOTESTS-63a1.patcha

@@ -0,0 +1,97 @@
+# HG changeset patch
+# User omersid <omersid@nomailaddy.com>
+# Date 1535426094 14400
+# Node ID 709590a1a24fc8d55f891204dd1e105eaf787177
+# Parent  8e4a9d415c6a37c26afda6b747253f9f3bc0cdf7
+Bug 1472170- Added description as a property for Symbol and updated tests262. r=anba
+
+When this fails to apply afer renaming source SymbolObject.xxx files to
+Symbol.xxx check if the full patch can be put in.
+
+diff --git a/js/src/builtin/SymbolObject.cpp b/js/src/builtin/SymbolObject.cpp
+--- a/js/src/builtin/SymbolObject.cpp
++++ b/js/src/builtin/SymbolObject.cpp
+@@ -27,16 +27,17 @@ SymbolObject::create(JSContext* cx, JS::
+     if (!obj)
+         return nullptr;
+     SymbolObject& symobj = obj->as<SymbolObject>();
+     symobj.setPrimitiveValue(symbol);
+     return &symobj;
+ }
+ 
+ const JSPropertySpec SymbolObject::properties[] = {
++    JS_PSG("description", descriptionGetter, 0),
+     JS_PS_END
+ };
+ 
+ const JSFunctionSpec SymbolObject::methods[] = {
+     JS_FN(js_toString_str, toString, 0, 0),
+     JS_FN(js_valueOf_str, valueOf, 0, 0),
+     JS_SYM_FN(toPrimitive, toPrimitive, 1, JSPROP_READONLY),
+     JS_FS_END
+@@ -229,16 +230,41 @@ SymbolObject::toPrimitive(JSContext* cx,
+ {
+     CallArgs args = CallArgsFromVp(argc, vp);
+ 
+     // The specification gives exactly the same algorithm for @@toPrimitive as
+     // for valueOf, so reuse the valueOf implementation.
+     return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args);
+ }
+ 
++bool
++SymbolObject::descriptionGetter_impl(JSContext* cx, const CallArgs& args)
++{
++    // Get symbol object pointer.
++    HandleValue thisv = args.thisv();
++    MOZ_ASSERT(IsSymbol(thisv));
++    Rooted<Symbol*> sym(cx, thisv.isSymbol()
++                            ? thisv.toSymbol()
++                            : thisv.toObject().as<SymbolObject>().unbox());
++
++    // Return the symbol's description if present, otherwise return undefined.
++    if (JSString* str = sym->description())
++        args.rval().setString(str);
++    else
++        args.rval().setUndefined();
++    return true;
++}
++
++bool
++SymbolObject::descriptionGetter(JSContext* cx, unsigned argc, Value* vp)
++{
++    CallArgs args = CallArgsFromVp(argc, vp);
++    return CallNonGenericMethod<IsSymbol, descriptionGetter_impl>(cx, args);
++}
++
+ JSObject*
+ js::InitSymbolClass(JSContext* cx, HandleObject obj)
+ {
+     return SymbolObject::initClass(cx, obj, true);
+ }
+ 
+ JSObject*
+ js::InitBareSymbolCtor(JSContext* cx, HandleObject obj)
+diff --git a/js/src/builtin/SymbolObject.h b/js/src/builtin/SymbolObject.h
+--- a/js/src/builtin/SymbolObject.h
++++ b/js/src/builtin/SymbolObject.h
+@@ -47,16 +47,20 @@ class SymbolObject : public NativeObject
+ 
+     // Methods defined on Symbol.prototype.
+     static MOZ_MUST_USE bool toString_impl(JSContext* cx, const CallArgs& args);
+     static MOZ_MUST_USE bool toString(JSContext* cx, unsigned argc, Value* vp);
+     static MOZ_MUST_USE bool valueOf_impl(JSContext* cx, const CallArgs& args);
+     static MOZ_MUST_USE bool valueOf(JSContext* cx, unsigned argc, Value* vp);
+     static MOZ_MUST_USE bool toPrimitive(JSContext* cx, unsigned argc, Value* vp);
+ 
++    // Properties defined on Symbol.prototype.
++    static MOZ_MUST_USE bool descriptionGetter_impl(JSContext* cx, const CallArgs& args);
++    static MOZ_MUST_USE bool descriptionGetter(JSContext* cx, unsigned argc, Value *vp);
++
+     static const JSPropertySpec properties[];
+     static const JSFunctionSpec methods[];
+     static const JSFunctionSpec staticMethods[];
+ };
+ 
+ extern JSObject*
+ InitSymbolClass(JSContext* cx, HandleObject obj);
+ 

+ 3 - 3
frg/work-js/mozilla-release/patches/TOP-NOBUG-PLASTER-fix-strip-25319.patch

@@ -1,8 +1,8 @@
 # HG changeset patch
 # HG changeset patch
 # User Bill Gianopoulos <wgianopoulos@gmail.com>
 # User Bill Gianopoulos <wgianopoulos@gmail.com>
 # Date 1702217928 0
 # Date 1702217928 0
-# Parent  6ef6764c1b3433bc374d3cb36f6264b6e73e97be
-No Bug - Avoid package faiures on Windows with some strip otion combinations.
+# Parent  15614510b82177888cf3945104f74c54be70cd22
+No Bug - Avoid package faiures on Windows with some strip option combinations. r=me a=me
 
 
 diff --git a/js/src/old-configure.in b/js/src/old-configure.in
 diff --git a/js/src/old-configure.in b/js/src/old-configure.in
 --- a/js/src/old-configure.in
 --- a/js/src/old-configure.in
@@ -30,7 +30,7 @@ diff --git a/js/src/old-configure.in b/js/src/old-configure.in
 diff --git a/old-configure.in b/old-configure.in
 diff --git a/old-configure.in b/old-configure.in
 --- a/old-configure.in
 --- a/old-configure.in
 +++ b/old-configure.in
 +++ b/old-configure.in
-@@ -673,17 +673,18 @@ case "$target" in
+@@ -669,17 +669,18 @@ case "$target" in
              # function thunks need to be generated for cross-DLL calls.
              # function thunks need to be generated for cross-DLL calls.
              MOZ_FOLD_LIBS_FLAGS="-mnop-fun-dllimport"
              MOZ_FOLD_LIBS_FLAGS="-mnop-fun-dllimport"
          else
          else

+ 0 - 2895
frg/work-js/mozilla-release/patches/mozilla-central_388196.patch

@@ -1,2895 +0,0 @@
-# HG changeset patch
-# User Junior Hsu <juhsu@mozilla.com>
-# Date 1504001787 -28800
-#      Tue Aug 29 18:16:27 2017 +0800
-# Node ID 1d83d62684e2971fc9705f8950521e1a9afd6ae4
-# Parent  8e55fa4da0cb3213d24aca8326792f6346594ed6
-Bug 870460 - Part 1: Let cookie db startup-read off-main-thread. r=nwgh, r=jdm, data-r=francois
-
-diff --git a/extensions/cookie/test/unit/test_cookies_async_failure.js b/extensions/cookie/test/unit/test_cookies_async_failure.js
---- a/extensions/cookie/test/unit/test_cookies_async_failure.js
-+++ b/extensions/cookie/test/unit/test_cookies_async_failure.js
-@@ -227,46 +227,33 @@ function* run_test_2(generator)
- 
-   // Load the profile.
-   do_load_profile();
- 
-   // At this point, the database connection should be open. Ensure that it
-   // succeeded.
-   do_check_false(do_get_backup_file(profile).exists());
- 
--  // Synchronously read in the first cookie. This will cause it to go into the
--  // cookie table, whereupon it will be written out during database rebuild.
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
--
--  // Wait for the asynchronous read to choke, at which point the backup file
--  // will be created and the database rebuilt.
--  new _observer(sub_generator, "cookie-db-rebuilding");
--  yield;
--  do_execute_soon(function() { do_run_generator(sub_generator); });
--  yield;
--
--  // At this point, the cookies should still be in memory.
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
--  do_check_eq(do_count_cookies(), 1);
-+  // Recreate a new database since it was corrupted
-+  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 0);
-+  do_check_eq(do_count_cookies(), 0);
- 
-   // Close the profile.
-   do_close_profile(sub_generator);
-   yield;
- 
-   // Check that the original database was renamed.
-   do_check_true(do_get_backup_file(profile).exists());
-   do_check_eq(do_get_backup_file(profile).fileSize, size);
-   let db = Services.storage.openDatabase(do_get_cookie_file(profile));
--  do_check_eq(do_count_cookies_in_db(db, "0.com"), 1);
-   db.close();
- 
--  // Load the profile, and check that it contains the new cookie.
-   do_load_profile();
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
--  do_check_eq(do_count_cookies(), 1);
-+  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 0);
-+  do_check_eq(do_count_cookies(), 0);
- 
-   // Close the profile.
-   do_close_profile(sub_generator);
-   yield;
- 
-   // Clean up.
-   do_get_cookie_file(profile).remove(false);
-   do_get_backup_file(profile).remove(false);
-@@ -305,35 +292,26 @@ function* run_test_3(generator)
- 
-   // Load the profile.
-   do_load_profile();
- 
-   // At this point, the database connection should be open. Ensure that it
-   // succeeded.
-   do_check_false(do_get_backup_file(profile).exists());
- 
--  // Synchronously read in the cookies for our two domains. The first should
--  // succeed, but the second should fail midway through, resulting in none of
--  // those cookies being present.
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("hither.com"), 10);
-+  // Recreate a new database since it was corrupted
-+  do_check_eq(Services.cookiemgr.countCookiesFromHost("hither.com"), 0);
-   do_check_eq(Services.cookiemgr.countCookiesFromHost("haithur.com"), 0);
- 
--  // Wait for the backup file to be created and the database rebuilt.
--  do_check_false(do_get_backup_file(profile).exists());
--  new _observer(sub_generator, "cookie-db-rebuilding");
--  yield;
--  do_execute_soon(function() { do_run_generator(sub_generator); });
--  yield;
--
-   // Close the profile.
-   do_close_profile(sub_generator);
-   yield;
-   let db = Services.storage.openDatabase(do_get_cookie_file(profile));
--  do_check_eq(do_count_cookies_in_db(db, "hither.com"), 10);
--  do_check_eq(do_count_cookies_in_db(db), 10);
-+  do_check_eq(do_count_cookies_in_db(db, "hither.com"), 0);
-+  do_check_eq(do_count_cookies_in_db(db), 0);
-   db.close();
- 
-   // Check that the original database was renamed.
-   do_check_true(do_get_backup_file(profile).exists());
-   do_check_eq(do_get_backup_file(profile).fileSize, size);
- 
-   // Rename it back, and try loading the entire database synchronously.
-   do_get_backup_file(profile).moveTo(null, "cookies.sqlite");
-@@ -341,23 +319,16 @@ function* run_test_3(generator)
- 
-   // At this point, the database connection should be open. Ensure that it
-   // succeeded.
-   do_check_false(do_get_backup_file(profile).exists());
- 
-   // Synchronously read in everything.
-   do_check_eq(do_count_cookies(), 0);
- 
--  // Wait for the backup file to be created and the database rebuilt.
--  do_check_false(do_get_backup_file(profile).exists());
--  new _observer(sub_generator, "cookie-db-rebuilding");
--  yield;
--  do_execute_soon(function() { do_run_generator(sub_generator); });
--  yield;
--
-   // Close the profile.
-   do_close_profile(sub_generator);
-   yield;
-   db = Services.storage.openDatabase(do_get_cookie_file(profile));
-   do_check_eq(do_count_cookies_in_db(db), 0);
-   db.close();
- 
-   // Check that the original database was renamed.
-@@ -392,52 +363,40 @@ function* run_test_4(generator)
- 
-   // Load the profile.
-   do_load_profile();
- 
-   // At this point, the database connection should be open. Ensure that it
-   // succeeded.
-   do_check_false(do_get_backup_file(profile).exists());
- 
--  // Synchronously read in the first cookie. This will cause it to go into the
--  // cookie table, whereupon it will be written out during database rebuild.
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
-+  // Recreate a new database since it was corrupted
-+  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 0);
- 
-   // Queue up an INSERT for the same base domain. This should also go into
-   // memory and be written out during database rebuild.
-   let uri = NetUtil.newURI("http://0.com/");
-   Services.cookies.setCookieString(uri, null, "oh2=hai; max-age=1000", null);
- 
--  // Wait for the asynchronous read to choke and the insert to fail shortly
--  // thereafter, at which point the backup file will be created and the database
--  // rebuilt.
--  new _observer(sub_generator, "cookie-db-rebuilding");
--  yield;
--  do_execute_soon(function() { do_run_generator(sub_generator); });
--  yield;
--
-   // At this point, the cookies should still be in memory.
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 2);
--  do_check_eq(do_count_cookies(), 2);
-+  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
-+  do_check_eq(do_count_cookies(), 1);
- 
-   // Close the profile.
-   do_close_profile(sub_generator);
-   yield;
- 
-   // Check that the original database was renamed.
-   do_check_true(do_get_backup_file(profile).exists());
-   do_check_eq(do_get_backup_file(profile).fileSize, size);
--  let db = Services.storage.openDatabase(do_get_cookie_file(profile));
--  do_check_eq(do_count_cookies_in_db(db, "0.com"), 2);
--  db.close();
- 
-   // Load the profile, and check that it contains the new cookie.
-   do_load_profile();
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 2);
--  do_check_eq(do_count_cookies(), 2);
-+  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
-+  do_check_eq(do_count_cookies(), 1);
- 
-   // Close the profile.
-   do_close_profile(sub_generator);
-   yield;
- 
-   // Clean up.
-   do_get_cookie_file(profile).remove(false);
-   do_get_backup_file(profile).remove(false);
-@@ -469,72 +428,44 @@ function* run_test_5(generator)
- 
-   // Load the profile.
-   do_load_profile();
- 
-   // At this point, the database connection should be open. Ensure that it
-   // succeeded.
-   do_check_false(do_get_backup_file(profile).exists());
- 
--  // Synchronously read in the first two cookies. This will cause them to go
--  // into the cookie table, whereupon it will be written out during database
--  // rebuild.
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1);
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
--
--  // Wait for the asynchronous read to choke, at which point the backup file
--  // will be created and a new connection opened.
--  new _observer(sub_generator, "cookie-db-rebuilding");
--  yield;
--
--  // At this point, the cookies should still be in memory. (Note that these
--  // calls are re-entrant into the cookie service, but it's OK!)
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1);
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
--  do_check_eq(do_count_cookies(), 2);
-+  // Recreate a new database since it was corrupted
-+  do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 0);
-+  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 0);
-+  do_check_eq(do_count_cookies(), 0);
-   do_check_true(do_get_backup_file(profile).exists());
-   do_check_eq(do_get_backup_file(profile).fileSize, size);
-   do_check_false(do_get_rebuild_backup_file(profile).exists());
- 
-   // Open a database connection, and write a row that will trigger a constraint
-   // violation.
-   let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 4);
-   db.insertCookie(cookie);
-   do_check_eq(do_count_cookies_in_db(db.db, "bar.com"), 1);
-   do_check_eq(do_count_cookies_in_db(db.db), 1);
-   db.close();
- 
--  // Wait for the rebuild to bail and the database to be closed.
--  new _observer(sub_generator, "cookie-db-closed");
--  yield;
--
-   // Check that the original backup and the database itself are gone.
--  do_check_true(do_get_rebuild_backup_file(profile).exists());
-   do_check_true(do_get_backup_file(profile).exists());
-   do_check_eq(do_get_backup_file(profile).fileSize, size);
--  do_check_false(do_get_cookie_file(profile).exists());
- 
--  // Check that the rebuild backup has the original bar.com cookie, and possibly
--  // a 0.com cookie depending on whether it got written out first or second.
--  db = new CookieDatabaseConnection(do_get_rebuild_backup_file(profile), 4);
--  do_check_eq(do_count_cookies_in_db(db.db, "bar.com"), 1);
--  let count = do_count_cookies_in_db(db.db);
--  do_check_true(count == 1 ||
--    count == 2 && do_count_cookies_in_db(db.db, "0.com") == 1);
--  db.close();
--
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1);
--  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1);
--  do_check_eq(do_count_cookies(), 2);
-+  do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 0);
-+  do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 0);
-+  do_check_eq(do_count_cookies(), 0);
- 
-   // Close the profile. We do not need to wait for completion, because the
-   // database has already been closed.
-   do_close_profile();
- 
-   // Clean up.
-+  do_get_cookie_file(profile).remove(false);
-   do_get_backup_file(profile).remove(false);
--  do_get_rebuild_backup_file(profile).remove(false);
-   do_check_false(do_get_cookie_file(profile).exists());
-   do_check_false(do_get_backup_file(profile).exists());
--  do_check_false(do_get_rebuild_backup_file(profile).exists());
-   do_run_generator(generator);
- }
- 
-diff --git a/extensions/cookie/test/unit/test_cookies_read.js b/extensions/cookie/test/unit/test_cookies_read.js
---- a/extensions/cookie/test/unit/test_cookies_read.js
-+++ b/extensions/cookie/test/unit/test_cookies_read.js
-@@ -22,17 +22,18 @@ function finish_test() {
- function* do_run_test() {
-   // Set up a profile.
-   let profile = do_get_profile();
- 
-   // Allow all cookies.
-   Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
- 
-   // Start the cookieservice, to force creation of a database.
--  Services.cookies;
-+  // Get the sessionEnumerator to join the initialization in cookie thread
-+  Services.cookiemgr.sessionEnumerator;
- 
-   // Open a database connection now, after synchronous initialization has
-   // completed. We may not be able to open one later once asynchronous writing
-   // begins.
-   do_check_true(do_get_cookie_file(profile).exists());
-   let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 4);
- 
-   for (let i = 0; i < CMAX; ++i) {
-diff --git a/extensions/cookie/test/unit/test_schema_2_migration.js b/extensions/cookie/test/unit/test_schema_2_migration.js
---- a/extensions/cookie/test/unit/test_schema_2_migration.js
-+++ b/extensions/cookie/test/unit/test_schema_2_migration.js
-@@ -17,16 +17,27 @@ function finish_test() {
-     do_test_finished();
-   });
- }
- 
- function* do_run_test() {
-   // Set up a profile.
-   let profile = do_get_profile();
- 
-+  // Start the cookieservice, to force creation of a database.
-+  // Get the sessionEnumerator to join the initialization in cookie thread
-+  Services.cookiemgr.sessionEnumerator;
-+
-+  // Close the profile.
-+  do_close_profile(test_generator);
-+  yield;
-+
-+  // Remove the cookie file in order to create another database file.
-+  do_get_cookie_file(profile).remove(false);
-+
-   // Create a schema 2 database.
-   let schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2);
- 
-   let now = Date.now() * 1000;
-   let futureExpiry = Math.round(now / 1e6 + 1000);
-   let pastExpiry = Math.round(now / 1e6 - 1000);
- 
-   // Populate it, with:
-@@ -74,16 +85,18 @@ function* do_run_test() {
-   }
- 
-   // Close it.
-   schema2db.close();
-   schema2db = null;
- 
-   // Load the database, forcing migration to the current schema version. Then
-   // test the expected set of cookies:
-+  do_load_profile();
-+
-   // 1) All unexpired, unique cookies exist.
-   do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 20);
- 
-   // 2) All expired, unique cookies exist.
-   do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 20);
- 
-   // 3) Only one cookie remains, and it's the one with the highest expiration
-   // time.
-diff --git a/extensions/cookie/test/unit/test_schema_3_migration.js b/extensions/cookie/test/unit/test_schema_3_migration.js
---- a/extensions/cookie/test/unit/test_schema_3_migration.js
-+++ b/extensions/cookie/test/unit/test_schema_3_migration.js
-@@ -17,16 +17,27 @@ function finish_test() {
-     do_test_finished();
-   });
- }
- 
- function* do_run_test() {
-   // Set up a profile.
-   let profile = do_get_profile();
- 
-+  // Start the cookieservice, to force creation of a database.
-+  // Get the sessionEnumerator to join the initialization in cookie thread
-+  Services.cookiemgr.sessionEnumerator;
-+
-+  // Close the profile.
-+  do_close_profile(test_generator);
-+  yield;
-+
-+  // Remove the cookie file in order to create another database file.
-+  do_get_cookie_file(profile).remove(false);
-+
-   // Create a schema 3 database.
-   let schema3db = new CookieDatabaseConnection(do_get_cookie_file(profile), 3);
- 
-   let now = Date.now() * 1000;
-   let futureExpiry = Math.round(now / 1e6 + 1000);
-   let pastExpiry = Math.round(now / 1e6 - 1000);
- 
-   // Populate it, with:
-@@ -74,16 +85,18 @@ function* do_run_test() {
-   }
- 
-   // Close it.
-   schema3db.close();
-   schema3db = null;
- 
-   // Load the database, forcing migration to the current schema version. Then
-   // test the expected set of cookies:
-+  do_load_profile();
-+
-   // 1) All unexpired, unique cookies exist.
-   do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 20);
- 
-   // 2) All expired, unique cookies exist.
-   do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 20);
- 
-   // 3) Only one cookie remains, and it's the one with the highest expiration
-   // time.
-diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp
---- a/netwerk/base/nsIOService.cpp
-+++ b/netwerk/base/nsIOService.cpp
-@@ -1484,16 +1484,21 @@ nsIOService::Observe(nsISupports *subjec
-             // Set up the initilization flag regardless the actuall result.
-             // If we fail here, we will fail always on.
-             mNetworkLinkServiceInitialized = true;
- 
-             // And now reflect the preference setting
-             nsCOMPtr<nsIPrefBranch> prefBranch;
-             GetPrefBranch(getter_AddRefs(prefBranch));
-             PrefsChanged(prefBranch, MANAGE_OFFLINE_STATUS_PREF);
-+
-+            // Bug 870460 - Read cookie database at an early-as-possible time
-+            // off main thread. Hence, we have more chance to finish db query
-+            // before something calls into the cookie service.
-+            nsCOMPtr<nsISupports> cookieServ = do_GetService(NS_COOKIESERVICE_CONTRACTID);
-         }
-     } else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
-         // Remember we passed XPCOM shutdown notification to prevent any
-         // changes of the offline status from now. We must not allow going
-         // online after this point.
-         mShutdown = true;
- 
-         if (!mHttpHandlerAlreadyShutingDown && !mOfflineForProfileChange) {
-diff --git a/netwerk/cookie/nsCookieService.cpp b/netwerk/cookie/nsCookieService.cpp
---- a/netwerk/cookie/nsCookieService.cpp
-+++ b/netwerk/cookie/nsCookieService.cpp
-@@ -29,16 +29,17 @@
- #include "nsIURI.h"
- #include "nsIURL.h"
- #include "nsIChannel.h"
- #include "nsIFile.h"
- #include "nsIObserverService.h"
- #include "nsILineInputStream.h"
- #include "nsIEffectiveTLDService.h"
- #include "nsIIDNService.h"
-+#include "nsIThread.h"
- #include "mozIThirdPartyUtil.h"
- 
- #include "nsTArray.h"
- #include "nsCOMArray.h"
- #include "nsIMutableArray.h"
- #include "nsArrayEnumerator.h"
- #include "nsEnumeratorUtils.h"
- #include "nsAutoPtr.h"
-@@ -77,18 +78,17 @@ static StaticRefPtr<nsCookieService> gCo
- 
- // XXX_hack. See bug 178993.
- // This is a hack to hide HttpOnly cookies from older browsers
- #define HTTP_ONLY_PREFIX "#HttpOnly_"
- 
- #define COOKIES_FILE "cookies.sqlite"
- #define COOKIES_SCHEMA_VERSION 9
- 
--// parameter indexes; see EnsureReadDomain, EnsureReadComplete and
--// ReadCookieDBListener::HandleResult
-+// parameter indexes; see |Read|
- #define IDX_NAME 0
- #define IDX_VALUE 1
- #define IDX_HOST 2
- #define IDX_PATH 3
- #define IDX_EXPIRY 4
- #define IDX_LAST_ACCESSED 5
- #define IDX_CREATION_TIME 6
- #define IDX_SECURE 7
-@@ -454,102 +454,16 @@ public:
-   {
-     return NS_OK;
-   }
- };
- 
- NS_IMPL_ISUPPORTS(RemoveCookieDBListener, mozIStorageStatementCallback)
- 
- /******************************************************************************
-- * ReadCookieDBListener impl:
-- * mozIStorageStatementCallback used to track asynchronous removal operations.
-- ******************************************************************************/
--class ReadCookieDBListener final : public DBListenerErrorHandler
--{
--private:
--  const char *GetOpType() override { return "READ"; }
--  bool mCanceled;
--
--  ~ReadCookieDBListener() = default;
--
--public:
--  NS_DECL_ISUPPORTS
--
--  explicit ReadCookieDBListener(DBState* dbState)
--    : DBListenerErrorHandler(dbState)
--    , mCanceled(false)
--  {
--  }
--
--  void Cancel() { mCanceled = true; }
--
--  NS_IMETHOD HandleResult(mozIStorageResultSet *aResult) override
--  {
--    nsCOMPtr<mozIStorageRow> row;
--
--    while (true) {
--      DebugOnly<nsresult> rv = aResult->GetNextRow(getter_AddRefs(row));
--      NS_ASSERT_SUCCESS(rv);
--
--      if (!row)
--        break;
--
--      CookieDomainTuple *tuple = mDBState->hostArray.AppendElement();
--      row->GetUTF8String(IDX_BASE_DOMAIN, tuple->key.mBaseDomain);
--
--      nsAutoCString suffix;
--      row->GetUTF8String(IDX_ORIGIN_ATTRIBUTES, suffix);
--      DebugOnly<bool> success = tuple->key.mOriginAttributes.PopulateFromSuffix(suffix);
--      MOZ_ASSERT(success);
--
--      tuple->cookie =
--        gCookieService->GetCookieFromRow(row, tuple->key.mOriginAttributes);
--    }
--
--    return NS_OK;
--  }
--  NS_IMETHOD HandleCompletion(uint16_t aReason) override
--  {
--    // Process the completion of the read operation. If we have been canceled,
--    // we cannot assume that the cookieservice still has an open connection
--    // or that it even refers to the same database, so we must return early.
--    // Conversely, the cookieservice guarantees that if we have not been
--    // canceled, the database connection is still alive and we can safely
--    // operate on it.
--
--    if (mCanceled) {
--      // We may receive a REASON_FINISHED after being canceled;
--      // tweak the reason accordingly.
--      aReason = mozIStorageStatementCallback::REASON_CANCELED;
--    }
--
--    switch (aReason) {
--    case mozIStorageStatementCallback::REASON_FINISHED:
--      gCookieService->AsyncReadComplete();
--      break;
--    case mozIStorageStatementCallback::REASON_CANCELED:
--      // Nothing more to do here. The partially read data has already been
--      // thrown away.
--      COOKIE_LOGSTRING(LogLevel::Debug, ("Read canceled"));
--      break;
--    case mozIStorageStatementCallback::REASON_ERROR:
--      // Nothing more to do here. DBListenerErrorHandler::HandleError()
--      // can handle it.
--      COOKIE_LOGSTRING(LogLevel::Debug, ("Read error"));
--      break;
--    default:
--      NS_NOTREACHED("invalid reason");
--    }
--    return NS_OK;
--  }
--};
--
--NS_IMPL_ISUPPORTS(ReadCookieDBListener, mozIStorageStatementCallback)
--
--/******************************************************************************
-  * CloseCookieDBListener imp:
-  * Static mozIStorageCompletionCallback used to notify when the database is
-  * successfully closed.
-  ******************************************************************************/
- class CloseCookieDBListener final :  public mozIStorageCompletionCallback
- {
-   ~CloseCookieDBListener() = default;
- 
-@@ -605,38 +519,22 @@ nsCookieEntry::SizeOfExcludingThis(Mallo
-   for (uint32_t i = 0; i < mCookies.Length(); ++i) {
-     amount += mCookies[i]->SizeOfIncludingThis(aMallocSizeOf);
-   }
- 
-   return amount;
- }
- 
- size_t
--CookieDomainTuple::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--{
--  size_t amount = 0;
--
--  amount += key.SizeOfExcludingThis(aMallocSizeOf);
--  amount += cookie->SizeOfIncludingThis(aMallocSizeOf);
--
--  return amount;
--}
--
--size_t
- DBState::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
- {
-   size_t amount = 0;
- 
-   amount += aMallocSizeOf(this);
-   amount += hostTable.SizeOfExcludingThis(aMallocSizeOf);
--  amount += hostArray.ShallowSizeOfExcludingThis(aMallocSizeOf);
--  for (uint32_t i = 0; i < hostArray.Length(); ++i) {
--    amount += hostArray[i].SizeOfExcludingThis(aMallocSizeOf);
--  }
--  amount += readSet.SizeOfExcludingThis(aMallocSizeOf);
- 
-   return amount;
- }
- 
- /******************************************************************************
-  * nsCookieService impl:
-  * singleton instance ctor/dtor methods
-  ******************************************************************************/
-@@ -703,16 +601,21 @@ nsCookieService::nsCookieService()
-  : mDBState(nullptr)
-  , mCookieBehavior(nsICookieService::BEHAVIOR_ACCEPT)
-  , mThirdPartySession(false)
-  , mThirdPartyNonsecureSession(false)
-  , mLeaveSecureAlone(true)
-  , mMaxNumberOfCookies(kMaxNumberOfCookies)
-  , mMaxCookiesPerHost(kMaxCookiesPerHost)
-  , mCookiePurgeAge(kCookiePurgeAge)
-+ , mThread(nullptr)
-+ , mMonitor("CookieThread")
-+ , mInitializedDBStates(false)
-+ , mInitializedDBConn(false)
-+ , mAccumulatedWaitTelemetry(false)
- {
- }
- 
- nsresult
- nsCookieService::Init()
- {
-   nsresult rv;
-   mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
-@@ -735,16 +638,19 @@ nsCookieService::Init()
-     prefBranch->AddObserver(kPrefThirdPartyNonsecureSession, this, true);
-     prefBranch->AddObserver(kCookieLeaveSecurityAlone,  this, true);
-     PrefChanged(prefBranch);
-   }
- 
-   mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
-   NS_ENSURE_SUCCESS(rv, rv);
- 
-+  rv = NS_NewNamedThread("Cookie", getter_AddRefs(mThread));
-+  NS_ENSURE_SUCCESS(rv, rv);
-+
-   // Init our default, and possibly private DBStates.
-   InitDBStates();
- 
-   RegisterWeakMemoryReporter(this);
- 
-   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-   NS_ENSURE_STATE(os);
-   os->AddObserver(this, "profile-before-change", true);
-@@ -761,59 +667,86 @@ nsCookieService::Init()
- }
- 
- void
- nsCookieService::InitDBStates()
- {
-   NS_ASSERTION(!mDBState, "already have a DBState");
-   NS_ASSERTION(!mDefaultDBState, "already have a default DBState");
-   NS_ASSERTION(!mPrivateDBState, "already have a private DBState");
-+  NS_ASSERTION(!mInitializedDBStates, "already initialized");
- 
-   // Create a new default DBState and set our current one.
-   mDefaultDBState = new DBState();
-   mDBState = mDefaultDBState;
- 
-   mPrivateDBState = new DBState();
- 
-   // Get our cookie file.
-   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
-     getter_AddRefs(mDefaultDBState->cookieFile));
-   if (NS_FAILED(rv)) {
-     // We've already set up our DBStates appropriately; nothing more to do.
-     COOKIE_LOGSTRING(LogLevel::Warning,
-       ("InitDBStates(): couldn't get cookie file"));
-+
-+    mInitializedDBConn = true;
-+    mInitializedDBStates = true;
-     return;
-   }
-   mDefaultDBState->cookieFile->AppendNative(NS_LITERAL_CSTRING(COOKIES_FILE));
- 
--  // Attempt to open and read the database. If TryInitDB() returns RESULT_RETRY,
--  // do so.
--  OpenDBResult result = TryInitDB(false);
--  if (result == RESULT_RETRY) {
--    // Database may be corrupt. Synchronously close the connection, clean up the
--    // default DBState, and try again.
--    COOKIE_LOGSTRING(LogLevel::Warning, ("InitDBStates(): retrying TryInitDB()"));
--    CleanupCachedStatements();
--    CleanupDefaultDBConnection();
--    result = TryInitDB(true);
-+  nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction("InitDBStates.TryInitDB", [] {
-+    NS_ENSURE_TRUE_VOID(gCookieService &&
-+                        gCookieService->mDBState &&
-+                        gCookieService->mDefaultDBState);
-+
-+    MonitorAutoLock lock(gCookieService->mMonitor);
-+
-+    // Attempt to open and read the database. If TryInitDB() returns RESULT_RETRY,
-+    // do so.
-+    OpenDBResult result = gCookieService->TryInitDB(false);
-     if (result == RESULT_RETRY) {
--      // We're done. Change the code to failure so we clean up below.
--      result = RESULT_FAILURE;
-+      // Database may be corrupt. Synchronously close the connection, clean up the
-+      // default DBState, and try again.
-+      COOKIE_LOGSTRING(LogLevel::Warning, ("InitDBStates(): retrying TryInitDB()"));
-+      gCookieService->CleanupCachedStatements();
-+      gCookieService->CleanupDefaultDBConnection();
-+      result = gCookieService->TryInitDB(true);
-+      if (result == RESULT_RETRY) {
-+        // We're done. Change the code to failure so we clean up below.
-+        result = RESULT_FAILURE;
-+      }
-     }
--  }
--
--  if (result == RESULT_FAILURE) {
--    COOKIE_LOGSTRING(LogLevel::Warning,
--      ("InitDBStates(): TryInitDB() failed, closing connection"));
--
--    // Connection failure is unrecoverable. Clean up our connection. We can run
--    // fine without persistent storage -- e.g. if there's no profile.
--    CleanupCachedStatements();
--    CleanupDefaultDBConnection();
--  }
-+
-+    if (result == RESULT_FAILURE) {
-+      COOKIE_LOGSTRING(LogLevel::Warning,
-+        ("InitDBStates(): TryInitDB() failed, closing connection"));
-+
-+      // Connection failure is unrecoverable. Clean up our connection. We can run
-+      // fine without persistent storage -- e.g. if there's no profile.
-+      gCookieService->CleanupCachedStatements();
-+      gCookieService->CleanupDefaultDBConnection();
-+
-+      // No need to initialize dbConn
-+      gCookieService->mInitializedDBConn = true;
-+    }
-+
-+    gCookieService->mInitializedDBStates = true;
-+
-+    NS_DispatchToMainThread(
-+      NS_NewRunnableFunction("TryInitDB.InitDBConn", [] {
-+        NS_ENSURE_TRUE_VOID(gCookieService);
-+        gCookieService->InitDBConn();
-+      })
-+    );
-+    gCookieService->mMonitor.Notify();
-+  });
-+
-+  mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
- }
- 
- namespace {
- 
- class ConvertAppIdToOriginAttrsSQLFunction final : public mozIStorageFunction
- {
-   ~ConvertAppIdToOriginAttrsSQLFunction() = default;
- 
-@@ -928,16 +861,17 @@ SetInBrowserFromOriginAttributesSQLFunct
-  */
- OpenDBResult
- nsCookieService::TryInitDB(bool aRecreateDB)
- {
-   NS_ASSERTION(!mDefaultDBState->dbConn, "nonnull dbConn");
-   NS_ASSERTION(!mDefaultDBState->stmtInsert, "nonnull stmtInsert");
-   NS_ASSERTION(!mDefaultDBState->insertListener, "nonnull insertListener");
-   NS_ASSERTION(!mDefaultDBState->syncConn, "nonnull syncConn");
-+  NS_ASSERTION(NS_GetCurrentThread() == mThread, "non cookie thread");
- 
-   // Ditch an existing db, if we've been told to (i.e. it's corrupt). We don't
-   // want to delete it outright, since it may be useful for debugging purposes,
-   // so we move it out of the way.
-   nsresult rv;
-   if (aRecreateDB) {
-     nsCOMPtr<nsIFile> backupFile;
-     mDefaultDBState->cookieFile->Clone(getter_AddRefs(backupFile));
-@@ -951,81 +885,72 @@ nsCookieService::TryInitDB(bool aRecreat
-     Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_COOKIES_OPEN_READAHEAD_MS>
-       telemetry;
-     ReadAheadFile(mDefaultDBState->cookieFile);
- 
-     // open a connection to the cookie database, and only cache our connection
-     // and statements upon success. The connection is opened unshared to eliminate
-     // cache contention between the main and background threads.
-     rv = mStorageService->OpenUnsharedDatabase(mDefaultDBState->cookieFile,
--      getter_AddRefs(mDefaultDBState->dbConn));
-+      getter_AddRefs(mDefaultDBState->syncConn));
-     NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-   }
- 
--  // Set up our listeners.
--  mDefaultDBState->insertListener = new InsertCookieDBListener(mDefaultDBState);
--  mDefaultDBState->updateListener = new UpdateCookieDBListener(mDefaultDBState);
--  mDefaultDBState->removeListener = new RemoveCookieDBListener(mDefaultDBState);
--  mDefaultDBState->closeListener = new CloseCookieDBListener(mDefaultDBState);
--
--  // Grow cookie db in 512KB increments
--  mDefaultDBState->dbConn->SetGrowthIncrement(512 * 1024, EmptyCString());
--
-   bool tableExists = false;
--  mDefaultDBState->dbConn->TableExists(NS_LITERAL_CSTRING("moz_cookies"),
-+  mDefaultDBState->syncConn->TableExists(NS_LITERAL_CSTRING("moz_cookies"),
-     &tableExists);
-   if (!tableExists) {
-     rv = CreateTable();
-     NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-   } else {
-     // table already exists; check the schema version before reading
-     int32_t dbSchemaVersion;
--    rv = mDefaultDBState->dbConn->GetSchemaVersion(&dbSchemaVersion);
-+    rv = mDefaultDBState->syncConn->GetSchemaVersion(&dbSchemaVersion);
-     NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-     // Start a transaction for the whole migration block.
--    mozStorageTransaction transaction(mDefaultDBState->dbConn, true);
-+    mozStorageTransaction transaction(mDefaultDBState->syncConn, true);
- 
-     switch (dbSchemaVersion) {
-     // Upgrading.
-     // Every time you increment the database schema, you need to implement
-     // the upgrading code from the previous version to the new one. If migration
-     // fails for any reason, it's a bug -- so we return RESULT_RETRY such that
-     // the original database will be saved, in the hopes that we might one day
-     // see it and fix it.
-     case 1:
-       {
-         // Add the lastAccessed column to the table.
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "ALTER TABLE moz_cookies ADD lastAccessed INTEGER"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-       }
-       // Fall through to the next upgrade.
-       MOZ_FALLTHROUGH;
- 
-     case 2:
-       {
-         // Add the baseDomain column and index to the table.
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "ALTER TABLE moz_cookies ADD baseDomain TEXT"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Compute the baseDomains for the table. This must be done eagerly
-         // otherwise we won't be able to synchronously read in individual
-         // domains on demand.
-         const int64_t SCHEMA2_IDX_ID  =  0;
-         const int64_t SCHEMA2_IDX_HOST = 1;
-         nsCOMPtr<mozIStorageStatement> select;
--        rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
-           "SELECT id, host FROM moz_cookies"), getter_AddRefs(select));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         nsCOMPtr<mozIStorageStatement> update;
--        rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
-           "UPDATE moz_cookies SET baseDomain = :baseDomain WHERE id = :id"),
-           getter_AddRefs(update));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         nsCString baseDomain, host;
-         bool hasResult;
-         while (true) {
-           rv = select->ExecuteStep(&hasResult);
-@@ -1049,17 +974,17 @@ nsCookieService::TryInitDB(bool aRecreat
-                                        id);
-           NS_ASSERT_SUCCESS(rv);
- 
-           rv = update->ExecuteStep(&hasResult);
-           NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-         }
- 
-         // Create an index on baseDomain.
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-       }
-       // Fall through to the next upgrade.
-       MOZ_FALLTHROUGH;
- 
-     case 3:
-       {
-@@ -1074,24 +999,24 @@ nsCookieService::TryInitDB(bool aRecreat
-         // Select the whole table, and order by the fields we're interested in.
-         // This means we can simply do a linear traversal of the results and
-         // check for duplicates as we go.
-         const int64_t SCHEMA3_IDX_ID =   0;
-         const int64_t SCHEMA3_IDX_NAME = 1;
-         const int64_t SCHEMA3_IDX_HOST = 2;
-         const int64_t SCHEMA3_IDX_PATH = 3;
-         nsCOMPtr<mozIStorageStatement> select;
--        rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
-           "SELECT id, name, host, path FROM moz_cookies "
-             "ORDER BY name ASC, host ASC, path ASC, expiry ASC"),
-           getter_AddRefs(select));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         nsCOMPtr<mozIStorageStatement> deleteExpired;
--        rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
-           "DELETE FROM moz_cookies WHERE id = :id"),
-           getter_AddRefs(deleteExpired));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Read the first row.
-         bool hasResult;
-         rv = select->ExecuteStep(&hasResult);
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-@@ -1134,28 +1059,28 @@ nsCookieService::TryInitDB(bool aRecreat
-             name1 = name2;
-             host1 = host2;
-             path1 = path2;
-             id1 = id2;
-           }
-         }
- 
-         // Add the creationTime column to the table.
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "ALTER TABLE moz_cookies ADD creationTime INTEGER"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Copy the id of each row into the new creationTime column.
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "UPDATE moz_cookies SET creationTime = "
-             "(SELECT id WHERE id = moz_cookies.id)"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Create a unique index on (name, host, path) to allow fast lookup.
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "CREATE UNIQUE INDEX moz_uniqueid "
-           "ON moz_cookies (name, host, path)"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-       }
-       // Fall through to the next upgrade.
-       MOZ_FALLTHROUGH;
- 
-     case 4:
-@@ -1167,41 +1092,41 @@ nsCookieService::TryInitDB(bool aRecreat
-         //
-         // Why we made this change: appId/inBrowserElement allow "cookie jars"
-         // for Firefox OS. We create a separate cookie namespace per {appId,
-         // inBrowserElement}.  When upgrading, we convert existing cookies
-         // (which imply we're on desktop/mobile) to use {0, false}, as that is
-         // the only namespace used by a non-Firefox-OS implementation.
- 
-         // Rename existing table
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "ALTER TABLE moz_cookies RENAME TO moz_cookies_old"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Drop existing index (CreateTable will create new one for new table)
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "DROP INDEX moz_basedomain"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Create new table (with new fields and new unique constraint)
-         rv = CreateTableForSchemaVersion5();
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Copy data from old table, using appId/inBrowser=0 for existing rows
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "INSERT INTO moz_cookies "
-           "(baseDomain, appId, inBrowserElement, name, value, host, path, expiry,"
-           " lastAccessed, creationTime, isSecure, isHttpOnly) "
-           "SELECT baseDomain, 0, 0, name, value, host, path, expiry,"
-           " lastAccessed, creationTime, isSecure, isHttpOnly "
-           "FROM moz_cookies_old"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Drop old table
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "DROP TABLE moz_cookies_old"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         COOKIE_LOGSTRING(LogLevel::Debug,
-           ("Upgraded database to schema version 5"));
-       }
-       // Fall through to the next upgrade.
-       MOZ_FALLTHROUGH;
-@@ -1217,58 +1142,58 @@ nsCookieService::TryInitDB(bool aRecreat
-         //
-         // We do the migration in several steps:
-         // 1. Rename the old table.
-         // 2. Create a new table.
-         // 3. Copy data from the old table to the new table; convert appId and
-         //    inBrowserElement to originAttributes in the meantime.
- 
-         // Rename existing table.
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-              "ALTER TABLE moz_cookies RENAME TO moz_cookies_old"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Drop existing index (CreateTable will create new one for new table).
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-              "DROP INDEX moz_basedomain"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Create new table with new fields and new unique constraint.
-         rv = CreateTableForSchemaVersion6();
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Copy data from old table without the two deprecated columns appId and
-         // inBrowserElement.
-         nsCOMPtr<mozIStorageFunction>
-           convertToOriginAttrs(new ConvertAppIdToOriginAttrsSQLFunction());
-         NS_ENSURE_TRUE(convertToOriginAttrs, RESULT_RETRY);
- 
-         NS_NAMED_LITERAL_CSTRING(convertToOriginAttrsName,
-                                  "CONVERT_TO_ORIGIN_ATTRIBUTES");
- 
--        rv = mDefaultDBState->dbConn->CreateFunction(convertToOriginAttrsName,
-+        rv = mDefaultDBState->syncConn->CreateFunction(convertToOriginAttrsName,
-                                                      2, convertToOriginAttrs);
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "INSERT INTO moz_cookies "
-           "(baseDomain, originAttributes, name, value, host, path, expiry,"
-           " lastAccessed, creationTime, isSecure, isHttpOnly) "
-           "SELECT baseDomain, "
-           " CONVERT_TO_ORIGIN_ATTRIBUTES(appId, inBrowserElement),"
-           " name, value, host, path, expiry, lastAccessed, creationTime, "
-           " isSecure, isHttpOnly "
-           "FROM moz_cookies_old"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
--        rv = mDefaultDBState->dbConn->RemoveFunction(convertToOriginAttrsName);
-+        rv = mDefaultDBState->syncConn->RemoveFunction(convertToOriginAttrsName);
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Drop old table
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-              "DROP TABLE moz_cookies_old"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         COOKIE_LOGSTRING(LogLevel::Debug,
-           ("Upgraded database to schema version 6"));
-       }
-       MOZ_FALLTHROUGH;
- 
-@@ -1277,81 +1202,81 @@ nsCookieService::TryInitDB(bool aRecreat
-         // We made a mistake in schema version 6. We cannot remove expected
-         // columns of any version (checked in the default case) from cookie
-         // database, because doing this would destroy the possibility of
-         // downgrading database.
-         //
-         // This version simply restores appId and inBrowserElement columns in
-         // order to fix downgrading issue even though these two columns are no
-         // longer used in the latest schema.
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "ALTER TABLE moz_cookies ADD appId INTEGER DEFAULT 0;"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "ALTER TABLE moz_cookies ADD inBrowserElement INTEGER DEFAULT 0;"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Compute and populate the values of appId and inBrwoserElement from
-         // originAttributes.
-         nsCOMPtr<mozIStorageFunction>
-           setAppId(new SetAppIdFromOriginAttributesSQLFunction());
-         NS_ENSURE_TRUE(setAppId, RESULT_RETRY);
- 
-         NS_NAMED_LITERAL_CSTRING(setAppIdName, "SET_APP_ID");
- 
--        rv = mDefaultDBState->dbConn->CreateFunction(setAppIdName, 1, setAppId);
-+        rv = mDefaultDBState->syncConn->CreateFunction(setAppIdName, 1, setAppId);
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         nsCOMPtr<mozIStorageFunction>
-           setInBrowser(new SetInBrowserFromOriginAttributesSQLFunction());
-         NS_ENSURE_TRUE(setInBrowser, RESULT_RETRY);
- 
-         NS_NAMED_LITERAL_CSTRING(setInBrowserName, "SET_IN_BROWSER");
- 
--        rv = mDefaultDBState->dbConn->CreateFunction(setInBrowserName, 1,
-+        rv = mDefaultDBState->syncConn->CreateFunction(setInBrowserName, 1,
-                                                      setInBrowser);
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "UPDATE moz_cookies SET appId = SET_APP_ID(originAttributes), "
-           "inBrowserElement = SET_IN_BROWSER(originAttributes);"
-         ));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
--        rv = mDefaultDBState->dbConn->RemoveFunction(setAppIdName);
-+        rv = mDefaultDBState->syncConn->RemoveFunction(setAppIdName);
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
--        rv = mDefaultDBState->dbConn->RemoveFunction(setInBrowserName);
-+        rv = mDefaultDBState->syncConn->RemoveFunction(setInBrowserName);
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         COOKIE_LOGSTRING(LogLevel::Debug,
-           ("Upgraded database to schema version 7"));
-       }
-       MOZ_FALLTHROUGH;
- 
-     case 7:
-       {
-         // Remove the appId field from moz_cookies.
-         //
-         // Unfortunately sqlite doesn't support dropping columns using ALTER
-         // TABLE, so we need to go through the procedure documented in
-         // https://www.sqlite.org/lang_altertable.html.
- 
-         // Drop existing index
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-              "DROP INDEX moz_basedomain"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Create a new_moz_cookies table without the appId field.
-         rv = CreateTableWorker("new_moz_cookies");
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Move the data over.
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "INSERT INTO new_moz_cookies ("
-               "id, "
-               "baseDomain, "
-               "originAttributes, "
-               "name, "
-               "value, "
-               "host, "
-               "path, "
-@@ -1374,77 +1299,77 @@ nsCookieService::TryInitDB(bool aRecreat
-               "creationTime, "
-               "isSecure, "
-               "isHttpOnly, "
-               "inBrowserElement "
-             "FROM moz_cookies;"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Drop the old table
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "DROP TABLE moz_cookies;"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Rename new_moz_cookies to moz_cookies.
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "ALTER TABLE new_moz_cookies RENAME TO moz_cookies;"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         // Recreate our index.
-         rv = CreateIndex();
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         COOKIE_LOGSTRING(LogLevel::Debug,
-           ("Upgraded database to schema version 8"));
-       }
-       MOZ_FALLTHROUGH;
- 
-     case 8:
-       {
-         // Add the sameSite column to the table.
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(
-           NS_LITERAL_CSTRING("ALTER TABLE moz_cookies ADD sameSite INTEGER"));
-         COOKIE_LOGSTRING(LogLevel::Debug,
-           ("Upgraded database to schema version 9"));
-       }
- 
-       // No more upgrades. Update the schema version.
--      rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
-+      rv = mDefaultDBState->syncConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
-       NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-       MOZ_FALLTHROUGH;
- 
-     case COOKIES_SCHEMA_VERSION:
-       break;
- 
-     case 0:
-       {
-         NS_WARNING("couldn't get schema version!");
- 
-         // the table may be usable; someone might've just clobbered the schema
-         // version. we can treat this case like a downgrade using the codepath
-         // below, by verifying the columns we care about are all there. for now,
-         // re-set the schema version in the db, in case the checks succeed (if
-         // they don't, we're dropping the table anyway).
--        rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
-+        rv = mDefaultDBState->syncConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-       }
-       // fall through to downgrade check
-       MOZ_FALLTHROUGH;
- 
-     // downgrading.
-     // if columns have been added to the table, we can still use the ones we
-     // understand safely. if columns have been deleted or altered, just
-     // blow away the table and start from scratch! if you change the way
-     // a column is interpreted, make sure you also change its name so this
-     // check will catch it.
-     default:
-       {
-         // check if all the expected columns exist
-         nsCOMPtr<mozIStorageStatement> stmt;
--        rv = mDefaultDBState->dbConn->CreateStatement(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
-           "SELECT "
-             "id, "
-             "baseDomain, "
-             "originAttributes, "
-             "name, "
-             "value, "
-             "host, "
-             "path, "
-@@ -1454,27 +1379,139 @@ nsCookieService::TryInitDB(bool aRecreat
-             "isSecure, "
-             "isHttpOnly, "
-             "sameSite "
-           "FROM moz_cookies"), getter_AddRefs(stmt));
-         if (NS_SUCCEEDED(rv))
-           break;
- 
-         // our columns aren't there - drop the table!
--        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+        rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-           "DROP TABLE moz_cookies"));
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-         rv = CreateTable();
-         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-       }
-       break;
-     }
-   }
- 
-+  // if we deleted a corrupt db, don't attempt to import - return now
-+  if (aRecreateDB) {
-+    return RESULT_OK;
-+  }
-+
-+  // check whether to import or just read in the db
-+  if (tableExists) {
-+    return Read();
-+  }
-+
-+  nsCOMPtr<nsIRunnable> runnable =
-+    NS_NewRunnableFunction("TryInitDB.ImportCookies", [] {
-+      NS_ENSURE_TRUE_VOID(gCookieService);
-+      NS_ENSURE_TRUE_VOID(gCookieService->mDefaultDBState);
-+      nsCOMPtr<nsIFile> oldCookieFile;
-+      nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
-+        getter_AddRefs(oldCookieFile));
-+      if (NS_FAILED(rv)) {
-+        return;
-+      }
-+
-+      // Import cookies, and clean up the old file regardless of success or failure.
-+      // Note that we have to switch out our DBState temporarily, in case we're in
-+      // private browsing mode; otherwise ImportCookies() won't be happy.
-+      DBState* initialState = gCookieService->mDBState;
-+      gCookieService->mDBState = gCookieService->mDefaultDBState;
-+      oldCookieFile->AppendNative(NS_LITERAL_CSTRING(OLD_COOKIE_FILE_NAME));
-+      gCookieService->ImportCookies(oldCookieFile);
-+      oldCookieFile->Remove(false);
-+      gCookieService->mDBState = initialState;
-+    });
-+
-+  NS_DispatchToMainThread(runnable);
-+
-+  return RESULT_OK;
-+}
-+
-+void
-+nsCookieService::InitDBConn()
-+{
-+  MOZ_ASSERT(NS_IsMainThread());
-+
-+  // We should skip InitDBConn if we close profile during initializing DBStates
-+  // and then InitDBConn is called after we close the DBStates.
-+  if (!mInitializedDBStates || mInitializedDBConn || !mDefaultDBState) {
-+    return;
-+  }
-+
-+  if (!mAccumulatedWaitTelemetry) {
-+    mAccumulatedWaitTelemetry = true;
-+    Telemetry::Accumulate(Telemetry::MOZ_SQLITE_COOKIES_BLOCK_MAIN_THREAD_MS, 0);
-+  }
-+  for (uint32_t i = 0; i < mReadArray.Length(); ++i) {
-+    CookieDomainTuple& tuple = mReadArray[i];
-+    RefPtr<nsCookie> cookie = nsCookie::Create(tuple.cookie->name,
-+                                               tuple.cookie->value,
-+                                               tuple.cookie->host,
-+                                               tuple.cookie->path,
-+                                               tuple.cookie->expiry,
-+                                               tuple.cookie->lastAccessed,
-+                                               tuple.cookie->creationTime,
-+                                               false,
-+                                               tuple.cookie->isSecure,
-+                                               tuple.cookie->isHttpOnly,
-+                                               tuple.cookie->originAttributes,
-+                                               tuple.cookie->sameSite);
-+
-+    AddCookieToList(tuple.key, cookie, mDefaultDBState, nullptr, false);
-+  }
-+
-+  if (NS_FAILED(InitDBConnInternal())) {
-+    COOKIE_LOGSTRING(LogLevel::Warning, ("InitDBConn(): retrying InitDBConnInternal()"));
-+    CleanupCachedStatements();
-+    CleanupDefaultDBConnection();
-+    if (NS_FAILED(InitDBConnInternal())) {
-+      COOKIE_LOGSTRING(LogLevel::Warning,
-+        ("InitDBConn(): InitDBConnInternal() failed, closing connection"));
-+
-+      // Game over, clean the connections.
-+      CleanupCachedStatements();
-+      CleanupDefaultDBConnection();
-+    }
-+  }
-+  mInitializedDBConn = true;
-+
-+  COOKIE_LOGSTRING(LogLevel::Debug, ("InitDBConn(): mInitializedDBConn = true"));
-+
-+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-+  if (os && !mReadArray.IsEmpty()) {
-+    os->NotifyObservers(nullptr, "cookie-db-read", nullptr);
-+    mReadArray.Clear();
-+  }
-+}
-+
-+nsresult
-+nsCookieService::InitDBConnInternal()
-+{
-+  MOZ_ASSERT(NS_IsMainThread());
-+
-+  nsresult rv = mStorageService->OpenUnsharedDatabase(mDefaultDBState->cookieFile,
-+    getter_AddRefs(mDefaultDBState->dbConn));
-+  NS_ENSURE_SUCCESS(rv, rv);
-+
-+  // Set up our listeners.
-+  mDefaultDBState->insertListener = new InsertCookieDBListener(mDefaultDBState);
-+  mDefaultDBState->updateListener = new UpdateCookieDBListener(mDefaultDBState);
-+  mDefaultDBState->removeListener = new RemoveCookieDBListener(mDefaultDBState);
-+  mDefaultDBState->closeListener = new CloseCookieDBListener(mDefaultDBState);
-+
-+  // Grow cookie db in 512KB increments
-+  mDefaultDBState->dbConn->SetGrowthIncrement(512 * 1024, EmptyCString());
-+
-   // make operations on the table asynchronous, for performance
-   mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-     "PRAGMA synchronous = OFF"));
- 
-   // Use write-ahead-logging for performance. We cap the autocheckpoint limit at
-   // 16 pages (around 500KB).
-   mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-     MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA journal_mode = WAL"));
-@@ -1506,54 +1543,29 @@ nsCookieService::TryInitDB(bool aRecreat
-       ":expiry, "
-       ":lastAccessed, "
-       ":creationTime, "
-       ":isSecure, "
-       ":isHttpOnly, "
-       ":sameSite"
-     ")"),
-     getter_AddRefs(mDefaultDBState->stmtInsert));
--  NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-+  NS_ENSURE_SUCCESS(rv, rv);
- 
-   rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
-     "DELETE FROM moz_cookies "
-     "WHERE name = :name AND host = :host AND path = :path AND originAttributes = :originAttributes"),
-     getter_AddRefs(mDefaultDBState->stmtDelete));
--  NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-+  NS_ENSURE_SUCCESS(rv, rv);
- 
-   rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
-     "UPDATE moz_cookies SET lastAccessed = :lastAccessed "
-     "WHERE name = :name AND host = :host AND path = :path AND originAttributes = :originAttributes"),
-     getter_AddRefs(mDefaultDBState->stmtUpdate));
--  NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
--
--  // if we deleted a corrupt db, don't attempt to import - return now
--  if (aRecreateDB)
--    return RESULT_OK;
--
--  // check whether to import or just read in the db
--  if (tableExists)
--    return Read();
--
--  nsCOMPtr<nsIFile> oldCookieFile;
--  rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
--    getter_AddRefs(oldCookieFile));
--  if (NS_FAILED(rv)) return RESULT_OK;
--
--  // Import cookies, and clean up the old file regardless of success or failure.
--  // Note that we have to switch out our DBState temporarily, in case we're in
--  // private browsing mode; otherwise ImportCookies() won't be happy.
--  DBState* initialState = mDBState;
--  mDBState = mDefaultDBState;
--  oldCookieFile->AppendNative(NS_LITERAL_CSTRING(OLD_COOKIE_FILE_NAME));
--  ImportCookies(oldCookieFile);
--  oldCookieFile->Remove(false);
--  mDBState = initialState;
--
--  return RESULT_OK;
-+  return rv;
- }
- 
- // Sets the schema version and creates the moz_cookies table.
- nsresult
- nsCookieService::CreateTableWorker(const char* aName)
- {
-   // Create the table.
-   // We default originAttributes to empty string: this is so if users revert to
-@@ -1573,56 +1585,56 @@ nsCookieService::CreateTableWorker(const
-       "lastAccessed INTEGER, "
-       "creationTime INTEGER, "
-       "isSecure INTEGER, "
-       "isHttpOnly INTEGER, "
-       "inBrowserElement INTEGER DEFAULT 0, "
-       "sameSite INTEGER DEFAULT 0, "
-       "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)"
-     ")");
--  return mDefaultDBState->dbConn->ExecuteSimpleSQL(command);
-+  return mDefaultDBState->syncConn->ExecuteSimpleSQL(command);
- }
- 
- // Sets the schema version and creates the moz_cookies table.
- nsresult
- nsCookieService::CreateTable()
- {
-   // Set the schema version, before creating the table.
--  nsresult rv = mDefaultDBState->dbConn->SetSchemaVersion(
-+  nsresult rv = mDefaultDBState->syncConn->SetSchemaVersion(
-     COOKIES_SCHEMA_VERSION);
-   if (NS_FAILED(rv)) return rv;
- 
-   rv = CreateTableWorker("moz_cookies");
-   if (NS_FAILED(rv)) return rv;
- 
-   return CreateIndex();
- }
- 
- nsresult
- nsCookieService::CreateIndex()
- {
-   // Create an index on baseDomain.
--  return mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+  return mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-     "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, "
-                                                 "originAttributes)"));
- }
- 
- // Sets the schema version and creates the moz_cookies table.
- nsresult
- nsCookieService::CreateTableForSchemaVersion6()
- {
-   // Set the schema version, before creating the table.
--  nsresult rv = mDefaultDBState->dbConn->SetSchemaVersion(6);
-+  nsresult rv = mDefaultDBState->syncConn->SetSchemaVersion(6);
-   if (NS_FAILED(rv)) return rv;
- 
-   // Create the table.
-   // We default originAttributes to empty string: this is so if users revert to
-   // an older Firefox version that doesn't know about this field, any cookies
-   // set will still work once they upgrade back.
--  rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+  rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-     "CREATE TABLE moz_cookies ("
-       "id INTEGER PRIMARY KEY, "
-       "baseDomain TEXT, "
-       "originAttributes TEXT NOT NULL DEFAULT '', "
-       "name TEXT, "
-       "value TEXT, "
-       "host TEXT, "
-       "path TEXT, "
-@@ -1631,33 +1643,33 @@ nsCookieService::CreateTableForSchemaVer
-       "creationTime INTEGER, "
-       "isSecure INTEGER, "
-       "isHttpOnly INTEGER, "
-       "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)"
-     ")"));
-   if (NS_FAILED(rv)) return rv;
- 
-   // Create an index on baseDomain.
--  return mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+  return mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-     "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, "
-                                                 "originAttributes)"));
- }
- 
- // Sets the schema version and creates the moz_cookies table.
- nsresult
- nsCookieService::CreateTableForSchemaVersion5()
- {
-   // Set the schema version, before creating the table.
--  nsresult rv = mDefaultDBState->dbConn->SetSchemaVersion(5);
-+  nsresult rv = mDefaultDBState->syncConn->SetSchemaVersion(5);
-   if (NS_FAILED(rv)) return rv;
- 
-   // Create the table. We default appId/inBrowserElement to 0: this is so if
-   // users revert to an older Firefox version that doesn't know about these
-   // fields, any cookies set will still work once they upgrade back.
--  rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+  rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-     "CREATE TABLE moz_cookies ("
-       "id INTEGER PRIMARY KEY, "
-       "baseDomain TEXT, "
-       "appId INTEGER DEFAULT 0, "
-       "inBrowserElement INTEGER DEFAULT 0, "
-       "name TEXT, "
-       "value TEXT, "
-       "host TEXT, "
-@@ -1667,50 +1679,52 @@ nsCookieService::CreateTableForSchemaVer
-       "creationTime INTEGER, "
-       "isSecure INTEGER, "
-       "isHttpOnly INTEGER, "
-       "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, appId, inBrowserElement)"
-     ")"));
-   if (NS_FAILED(rv)) return rv;
- 
-   // Create an index on baseDomain.
--  return mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+  return mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-     "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, "
-                                                 "appId, "
-                                                 "inBrowserElement)"));
- }
- 
- void
- nsCookieService::CloseDBStates()
- {
-+  // return if we already closed
-+  if (!mDBState) {
-+    return;
-+  }
-+
-+  EnsureReadComplete(false);
-+
-   // Null out our private and pointer DBStates regardless.
-   mPrivateDBState = nullptr;
-   mDBState = nullptr;
- 
-   // If we don't have a default DBState, we're done.
-   if (!mDefaultDBState)
-     return;
- 
-   // Cleanup cached statements before we can close anything.
-   CleanupCachedStatements();
- 
-   if (mDefaultDBState->dbConn) {
--    // Cancel any pending read. No further results will be received by our
--    // read listener.
--    if (mDefaultDBState->pendingRead) {
--      CancelAsyncRead(true);
--    }
--
-     // Asynchronously close the connection. We will null it below.
-     mDefaultDBState->dbConn->AsyncClose(mDefaultDBState->closeListener);
-   }
- 
-   CleanupDefaultDBConnection();
- 
-   mDefaultDBState = nullptr;
-+  mInitializedDBStates = false;
- }
- 
- // Null out the statements.
- // This must be done before closing the connection.
- void
- nsCookieService::CleanupCachedStatements()
- {
-   mDefaultDBState->stmtInsert = nullptr;
-@@ -1733,21 +1747,22 @@ nsCookieService::CleanupDefaultDBConnect
-   // asynchronous operations yet, this will synchronously close it; otherwise,
-   // it's expected that the caller has performed an AsyncClose prior.
-   mDefaultDBState->dbConn = nullptr;
-   mDefaultDBState->syncConn = nullptr;
- 
-   // Manually null out our listeners. This is necessary because they hold a
-   // strong ref to the DBState itself. They'll stay alive until whatever
-   // statements are still executing complete.
--  mDefaultDBState->readListener = nullptr;
-   mDefaultDBState->insertListener = nullptr;
-   mDefaultDBState->updateListener = nullptr;
-   mDefaultDBState->removeListener = nullptr;
-   mDefaultDBState->closeListener = nullptr;
-+
-+  mInitializedDBConn = false;
- }
- 
- void
- nsCookieService::HandleDBClosed(DBState* aDBState)
- {
-   COOKIE_LOGSTRING(LogLevel::Debug,
-     ("HandleDBClosed(): DBState %p closed", aDBState));
- 
-@@ -1805,26 +1820,16 @@ nsCookieService::HandleCorruptDB(DBState
- 
-   // Mark the database corrupt, so the close listener can begin reconstructing
-   // it.
-   switch (mDefaultDBState->corruptFlag) {
-   case DBState::OK: {
-     // Move to 'closing' state.
-     mDefaultDBState->corruptFlag = DBState::CLOSING_FOR_REBUILD;
- 
--    // Cancel any pending read and close the database. If we do have an
--    // in-flight read we want to throw away all the results so far -- we have no
--    // idea how consistent the database is. Note that we may have already
--    // canceled the read but not emptied our readSet; do so now.
--    mDefaultDBState->readSet.Clear();
--    if (mDefaultDBState->pendingRead) {
--      CancelAsyncRead(true);
--      mDefaultDBState->syncConn = nullptr;
--    }
--
-     CleanupCachedStatements();
-     mDefaultDBState->dbConn->AsyncClose(mDefaultDBState->closeListener);
-     CleanupDefaultDBConnection();
-     break;
-   }
-   case DBState::CLOSING_FOR_REBUILD: {
-     // We had an error while waiting for close completion. That's OK, just
-     // ignore it -- we're rebuilding anyway.
-@@ -1865,80 +1870,102 @@ nsCookieService::RebuildCorruptDB(DBStat
-       os->NotifyObservers(nullptr, "cookie-db-closed", nullptr);
-     }
-     return;
-   }
- 
-   COOKIE_LOGSTRING(LogLevel::Debug,
-     ("RebuildCorruptDB(): creating new database"));
- 
--  // The database has been closed, and we're ready to rebuild. Open a
--  // connection.
--  OpenDBResult result = TryInitDB(true);
--  if (result != RESULT_OK) {
--    // We're done. Reset our DB connection and statements, and notify of
--    // closure.
--    COOKIE_LOGSTRING(LogLevel::Warning,
--      ("RebuildCorruptDB(): TryInitDB() failed with result %u", result));
--    CleanupCachedStatements();
--    CleanupDefaultDBConnection();
--    mDefaultDBState->corruptFlag = DBState::OK;
--    if (os) {
--      os->NotifyObservers(nullptr, "cookie-db-closed", nullptr);
--    }
--    return;
--  }
--
--  // Notify observers that we're beginning the rebuild.
--  if (os) {
--    os->NotifyObservers(nullptr, "cookie-db-rebuilding", nullptr);
--  }
--
--  // Enumerate the hash, and add cookies to the params array.
--  mozIStorageAsyncStatement* stmt = aDBState->stmtInsert;
--  nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
--  stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
--  for (auto iter = aDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
--    nsCookieEntry* entry = iter.Get();
--
--    const nsCookieEntry::ArrayType& cookies = entry->GetCookies();
--    for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
--      nsCookie* cookie = cookies[i];
--
--      if (!cookie->IsSession()) {
--        bindCookieParameters(paramsArray, nsCookieKey(entry), cookie);
--      }
--    }
--  }
--
--  // Make sure we've got something to write. If we don't, we're done.
--  uint32_t length;
--  paramsArray->GetLength(&length);
--  if (length == 0) {
--    COOKIE_LOGSTRING(LogLevel::Debug,
--      ("RebuildCorruptDB(): nothing to write, rebuild complete"));
--    mDefaultDBState->corruptFlag = DBState::OK;
--    return;
--  }
--
--  // Execute the statement. If any errors crop up, we won't try again.
--  DebugOnly<nsresult> rv = stmt->BindParameters(paramsArray);
--  NS_ASSERT_SUCCESS(rv);
--  nsCOMPtr<mozIStoragePendingStatement> handle;
--  rv = stmt->ExecuteAsync(aDBState->insertListener, getter_AddRefs(handle));
--  NS_ASSERT_SUCCESS(rv);
-+  nsCOMPtr<nsIRunnable> runnable =
-+    NS_NewRunnableFunction("RebuildCorruptDB.TryInitDB", [] {
-+      NS_ENSURE_TRUE_VOID(gCookieService && gCookieService->mDefaultDBState);
-+
-+      // The database has been closed, and we're ready to rebuild. Open a
-+      // connection.
-+      OpenDBResult result = gCookieService->TryInitDB(true);
-+
-+      nsCOMPtr<nsIRunnable> innerRunnable =
-+        NS_NewRunnableFunction("RebuildCorruptDB.TryInitDBComplete", [result] {
-+          NS_ENSURE_TRUE_VOID(gCookieService && gCookieService->mDefaultDBState);
-+
-+          nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-+          if (result != RESULT_OK) {
-+            // We're done. Reset our DB connection and statements, and notify of
-+            // closure.
-+            COOKIE_LOGSTRING(LogLevel::Warning,
-+              ("RebuildCorruptDB(): TryInitDB() failed with result %u", result));
-+            gCookieService->CleanupCachedStatements();
-+            gCookieService->CleanupDefaultDBConnection();
-+            gCookieService->mDefaultDBState->corruptFlag = DBState::OK;
-+            if (os) {
-+              os->NotifyObservers(nullptr, "cookie-db-closed", nullptr);
-+            }
-+            return;
-+          }
-+
-+          // Notify observers that we're beginning the rebuild.
-+          if (os) {
-+            os->NotifyObservers(nullptr, "cookie-db-rebuilding", nullptr);
-+          }
-+
-+          gCookieService->InitDBConn();
-+
-+          // Enumerate the hash, and add cookies to the params array.
-+          mozIStorageAsyncStatement* stmt = gCookieService->mDefaultDBState->stmtInsert;
-+          nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
-+          stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
-+          for (auto iter = gCookieService->mDefaultDBState->hostTable.Iter();
-+               !iter.Done();
-+               iter.Next()) {
-+            nsCookieEntry* entry = iter.Get();
-+
-+            const nsCookieEntry::ArrayType& cookies = entry->GetCookies();
-+            for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
-+              nsCookie* cookie = cookies[i];
-+
-+              if (!cookie->IsSession()) {
-+                bindCookieParameters(paramsArray, nsCookieKey(entry), cookie);
-+              }
-+            }
-+          }
-+
-+          // Make sure we've got something to write. If we don't, we're done.
-+          uint32_t length;
-+          paramsArray->GetLength(&length);
-+          if (length == 0) {
-+            COOKIE_LOGSTRING(LogLevel::Debug,
-+              ("RebuildCorruptDB(): nothing to write, rebuild complete"));
-+            gCookieService->mDefaultDBState->corruptFlag = DBState::OK;
-+            return;
-+          }
-+
-+          // Execute the statement. If any errors crop up, we won't try again.
-+          DebugOnly<nsresult> rv = stmt->BindParameters(paramsArray);
-+          NS_ASSERT_SUCCESS(rv);
-+          nsCOMPtr<mozIStoragePendingStatement> handle;
-+          rv = stmt->ExecuteAsync(gCookieService->mDefaultDBState->insertListener,
-+                                  getter_AddRefs(handle));
-+          NS_ASSERT_SUCCESS(rv);
-+        });
-+      NS_DispatchToMainThread(innerRunnable);
-+    });
-+  mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
- }
- 
- nsCookieService::~nsCookieService()
- {
-   CloseDBStates();
- 
-   UnregisterWeakMemoryReporter(this);
- 
-   gCookieService = nullptr;
-+  if (mThread) {
-+    mThread->Shutdown();
-+  }
- }
- 
- NS_IMETHODIMP
- nsCookieService::Observe(nsISupports     *aSubject,
-                          const char      *aTopic,
-                          const char16_t *aData)
- {
-   // check the topic
-@@ -2122,16 +2149,18 @@ nsCookieService::SetCookieStringInternal
- {
-   NS_ASSERTION(aHostURI, "null host!");
- 
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return;
-   }
- 
-+  EnsureReadComplete(true);
-+
-   AutoRestore<DBState*> savePrevDBState(mDBState);
-   mDBState = (aOriginAttrs.mPrivateBrowsingId > 0) ? mPrivateDBState : mDefaultDBState;
- 
-   // get the base domain for the host URI.
-   // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
-   // file:// URI's (i.e. with an empty host) are allowed, but any other
-   // scheme must have a non-empty host. A trailing dot in the host
-   // is acceptable.
-@@ -2308,16 +2337,23 @@ nsCookieService::CreatePurgeList(nsICook
-  * nsCookieService:
-  * public transaction helper impl
-  ******************************************************************************/
- 
- NS_IMETHODIMP
- nsCookieService::RunInTransaction(nsICookieTransactionCallback* aCallback)
- {
-   NS_ENSURE_ARG(aCallback);
-+  if (!mDBState) {
-+    NS_WARNING("No DBState! Profile already closed?");
-+    return NS_ERROR_NOT_AVAILABLE;
-+  }
-+
-+  EnsureReadComplete(true);
-+
-   if (NS_WARN_IF(!mDefaultDBState->dbConn)) {
-     return NS_ERROR_NOT_AVAILABLE;
-   }
-   mozStorageTransaction transaction(mDefaultDBState->dbConn, true);
- 
-   if (NS_FAILED(aCallback->Callback())) {
-     Unused << transaction.Rollback();
-     return NS_ERROR_FAILURE;
-@@ -2367,28 +2403,24 @@ nsCookieService::PrefChanged(nsIPrefBran
- NS_IMETHODIMP
- nsCookieService::RemoveAll()
- {
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
- 
-+  EnsureReadComplete(true);
-+
-   RemoveAllFromMemory();
- 
-   // clear the cookie file
-   if (mDBState->dbConn) {
-     NS_ASSERTION(mDBState == mDefaultDBState, "not in default DB state");
- 
--    // Cancel any pending read. No further results will be received by our
--    // read listener.
--    if (mDefaultDBState->pendingRead) {
--      CancelAsyncRead(true);
--    }
--
-     nsCOMPtr<mozIStorageAsyncStatement> stmt;
-     nsresult rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
-       "DELETE FROM moz_cookies"), getter_AddRefs(stmt));
-     if (NS_SUCCEEDED(rv)) {
-       nsCOMPtr<mozIStoragePendingStatement> handle;
-       rv = stmt->ExecuteAsync(mDefaultDBState->removeListener,
-         getter_AddRefs(handle));
-       NS_ASSERT_SUCCESS(rv);
-@@ -2407,17 +2439,17 @@ nsCookieService::RemoveAll()
- NS_IMETHODIMP
- nsCookieService::GetEnumerator(nsISimpleEnumerator **aEnumerator)
- {
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
- 
--  EnsureReadComplete();
-+  EnsureReadComplete(true);
- 
-   nsCOMArray<nsICookie> cookieList(mDBState->cookieCount);
-   for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
-     const nsCookieEntry::ArrayType& cookies = iter.Get()->GetCookies();
-     for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
-       cookieList.AppendObject(cookies[i]);
-     }
-   }
-@@ -2428,17 +2460,17 @@ nsCookieService::GetEnumerator(nsISimple
- NS_IMETHODIMP
- nsCookieService::GetSessionEnumerator(nsISimpleEnumerator **aEnumerator)
- {
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
- 
--  EnsureReadComplete();
-+  EnsureReadComplete(true);
- 
-   nsCOMArray<nsICookie> cookieList(mDBState->cookieCount);
-   for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
-     const nsCookieEntry::ArrayType& cookies = iter.Get()->GetCookies();
-     for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
-       nsCookie* cookie = cookies[i];
-       // Filter out non-session cookies.
-       if (cookie->IsSession()) {
-@@ -2534,16 +2566,18 @@ nsCookieService::AddNative(const nsACStr
-     return NS_ERROR_FAILURE;
-   }
- 
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
- 
-+  EnsureReadComplete(true);
-+
-   AutoRestore<DBState*> savePrevDBState(mDBState);
-   mDBState = (aOriginAttributes->mPrivateBrowsingId > 0) ? mPrivateDBState : mDefaultDBState;
- 
-   // first, normalize the hostname, and fail if it contains illegal characters.
-   nsAutoCString host(aHost);
-   nsresult rv = NormalizeHost(host);
-   NS_ENSURE_SUCCESS(rv, rv);
- 
-@@ -2580,16 +2614,18 @@ nsCookieService::Remove(const nsACString
-                         const nsACString& aName, const nsACString& aPath,
-                         bool aBlocked)
- {
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
- 
-+  EnsureReadComplete(true);
-+
-   AutoRestore<DBState*> savePrevDBState(mDBState);
-   mDBState = (aAttrs.mPrivateBrowsingId > 0) ? mPrivateDBState : mDefaultDBState;
- 
-   // first, normalize the hostname, and fail if it contains illegal characters.
-   nsAutoCString host(aHost);
-   nsresult rv = NormalizeHost(host);
-   NS_ENSURE_SUCCESS(rv, rv);
- 
-@@ -2673,80 +2709,20 @@ nsCookieService::RemoveNative(const nsAC
-   return NS_OK;
- }
- 
- /******************************************************************************
-  * nsCookieService impl:
-  * private file I/O functions
-  ******************************************************************************/
- 
--// Begin an asynchronous read from the database.
--OpenDBResult
--nsCookieService::Read()
--{
--  // Set up a statement for the read. Note that our query specifies that
--  // 'baseDomain' not be nullptr -- see below for why.
--  nsCOMPtr<mozIStorageAsyncStatement> stmtRead;
--  nsresult rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
--    "SELECT "
--      "name, "
--      "value, "
--      "host, "
--      "path, "
--      "expiry, "
--      "lastAccessed, "
--      "creationTime, "
--      "isSecure, "
--      "isHttpOnly, "
--      "baseDomain, "
--      "originAttributes, "
--      "sameSite "
--    "FROM moz_cookies "
--    "WHERE baseDomain NOTNULL"), getter_AddRefs(stmtRead));
--  NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
--
--  // Set up a statement to delete any rows with a nullptr 'baseDomain'
--  // column. This takes care of any cookies set by browsers that don't
--  // understand the 'baseDomain' column, where the database schema version
--  // is from one that does. (This would occur when downgrading.)
--  nsCOMPtr<mozIStorageAsyncStatement> stmtDeleteNull;
--  rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
--    "DELETE FROM moz_cookies WHERE baseDomain ISNULL"),
--    getter_AddRefs(stmtDeleteNull));
--  NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
--
--  // Start a new connection for sync reads, to reduce contention with the
--  // background thread. We need to do this before we kick off write statements,
--  // since they can lock the database and prevent connections from being opened.
--  rv = mStorageService->OpenUnsharedDatabase(mDefaultDBState->cookieFile,
--    getter_AddRefs(mDefaultDBState->syncConn));
--  NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
--
--  // Init our readSet hash and execute the statements. Note that, after this
--  // point, we cannot fail without altering the cleanup code in InitDBStates()
--  // to handle closing of the now-asynchronous connection.
--  mDefaultDBState->hostArray.SetCapacity(kMaxNumberOfCookies);
--
--  mDefaultDBState->readListener = new ReadCookieDBListener(mDefaultDBState);
--  rv = stmtRead->ExecuteAsync(mDefaultDBState->readListener,
--    getter_AddRefs(mDefaultDBState->pendingRead));
--  NS_ASSERT_SUCCESS(rv);
--
--  nsCOMPtr<mozIStoragePendingStatement> handle;
--  rv = stmtDeleteNull->ExecuteAsync(mDefaultDBState->removeListener,
--    getter_AddRefs(handle));
--  NS_ASSERT_SUCCESS(rv);
--
--  return RESULT_OK;
--}
--
- // Extract data from a single result row and create an nsCookie.
--// This is templated since 'T' is different for sync vs async results.
--template<class T> nsCookie*
--nsCookieService::GetCookieFromRow(T &aRow, const OriginAttributes& aOriginAttributes)
-+mozilla::UniquePtr<ConstCookie>
-+nsCookieService::GetCookieFromRow(mozIStorageStatement *aRow,
-+                                  const OriginAttributes &aOriginAttributes)
- {
-   // Skip reading 'baseDomain' -- up to the caller.
-   nsCString name, value, host, path;
-   DebugOnly<nsresult> rv = aRow->GetUTF8String(IDX_NAME, name);
-   NS_ASSERT_SUCCESS(rv);
-   rv = aRow->GetUTF8String(IDX_VALUE, value);
-   NS_ASSERT_SUCCESS(rv);
-   rv = aRow->GetUTF8String(IDX_HOST, host);
-@@ -2756,328 +2732,151 @@ nsCookieService::GetCookieFromRow(T &aRo
- 
-   int64_t expiry = aRow->AsInt64(IDX_EXPIRY);
-   int64_t lastAccessed = aRow->AsInt64(IDX_LAST_ACCESSED);
-   int64_t creationTime = aRow->AsInt64(IDX_CREATION_TIME);
-   bool isSecure = 0 != aRow->AsInt32(IDX_SECURE);
-   bool isHttpOnly = 0 != aRow->AsInt32(IDX_HTTPONLY);
-   int32_t sameSite = aRow->AsInt32(IDX_SAME_SITE);
- 
--  // Create a new nsCookie and assign the data.
--  return nsCookie::Create(name, value, host, path,
--                          expiry,
--                          lastAccessed,
--                          creationTime,
--                          false,
--                          isSecure,
--                          isHttpOnly,
--                          aOriginAttributes,
--                          sameSite);
--}
--
--void
--nsCookieService::AsyncReadComplete()
--{
--  // We may be in the private browsing DB state, with a pending read on the
--  // default DB state. (This would occur if we started up in private browsing
--  // mode.) As long as we do all our operations on the default state, we're OK.
--  NS_ASSERTION(mDefaultDBState, "no default DBState");
--  NS_ASSERTION(mDefaultDBState->pendingRead, "no pending read");
--  NS_ASSERTION(mDefaultDBState->readListener, "no read listener");
--
--  mozStorageTransaction transaction(mDefaultDBState->dbConn, false);
--  // Merge the data read on the background thread with the data synchronously
--  // read on the main thread. Note that transactions on the cookie table may
--  // have occurred on the main thread since, making the background data stale.
--  for (uint32_t i = 0; i < mDefaultDBState->hostArray.Length(); ++i) {
--    const CookieDomainTuple &tuple = mDefaultDBState->hostArray[i];
--
--    // Tiebreak: if the given base domain has already been read in, ignore
--    // the background data. Note that readSet may contain domains that were
--    // queried but found not to be in the db -- that's harmless.
--    if (mDefaultDBState->readSet.GetEntry(tuple.key))
--      continue;
--
--    AddCookieToList(tuple.key, tuple.cookie, mDefaultDBState, nullptr, false);
--  }
--  DebugOnly<nsresult> rv = transaction.Commit();
--  MOZ_ASSERT(NS_SUCCEEDED(rv));
--
--  mDefaultDBState->stmtReadDomain = nullptr;
--  mDefaultDBState->pendingRead = nullptr;
--  mDefaultDBState->readListener = nullptr;
--
--  // Close sync connection asynchronously: if we let destructor close, it may
--  // cause an expensive fsync operation on the main-thread.
--  if (mDefaultDBState->syncConn) {
--    mDefaultDBState->syncConn->AsyncClose(nullptr);
--    mDefaultDBState->syncConn = nullptr;
--  }
--  mDefaultDBState->hostArray.Clear();
--  mDefaultDBState->readSet.Clear();
--
--  COOKIE_LOGSTRING(LogLevel::Debug, ("Read(): %" PRIu32 " cookies read",
--                                  mDefaultDBState->cookieCount));
--
--  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
--  if (os) {
--    os->NotifyObservers(nullptr, "cookie-db-read", nullptr);
--  }
--}
--
--void
--nsCookieService::CancelAsyncRead(bool aPurgeReadSet)
--{
--  // We may be in the private browsing DB state, with a pending read on the
--  // default DB state. (This would occur if we started up in private browsing
--  // mode.) As long as we do all our operations on the default state, we're OK.
--  NS_ASSERTION(mDefaultDBState, "no default DBState");
--  NS_ASSERTION(mDefaultDBState->pendingRead, "no pending read");
--  NS_ASSERTION(mDefaultDBState->readListener, "no read listener");
--
--  // Cancel the pending read, kill the read listener, and empty the array
--  // of data already read in on the background thread.
--  mDefaultDBState->readListener->Cancel();
--  DebugOnly<nsresult> rv = mDefaultDBState->pendingRead->Cancel();
--  NS_ASSERT_SUCCESS(rv);
--
--  mDefaultDBState->stmtReadDomain = nullptr;
--  mDefaultDBState->pendingRead = nullptr;
--  mDefaultDBState->readListener = nullptr;
--  mDefaultDBState->hostArray.Clear();
--
--  // Only clear the 'readSet' table if we no longer need to know what set of
--  // data is already accounted for.
--  if (aPurgeReadSet)
--    mDefaultDBState->readSet.Clear();
-+  // Create a new constCookie and assign the data.
-+  return mozilla::MakeUnique<ConstCookie>(name,
-+                                          value,
-+                                          host,
-+                                          path,
-+                                          expiry,
-+                                          lastAccessed,
-+                                          creationTime,
-+                                          isSecure,
-+                                          isHttpOnly,
-+                                          aOriginAttributes,
-+                                          sameSite);
- }
- 
- void
--nsCookieService::EnsureReadDomain(const nsCookieKey &aKey)
-+nsCookieService::EnsureReadComplete(bool aInitDBConn)
- {
--  NS_ASSERTION(!mDBState->dbConn || mDBState == mDefaultDBState,
--    "not in default db state");
--
--  // Fast path 1: nothing to read, or we've already finished reading.
--  if (MOZ_LIKELY(!mDBState->dbConn || !mDefaultDBState->pendingRead))
--    return;
--
--  // Fast path 2: already read in this particular domain.
--  if (MOZ_LIKELY(mDefaultDBState->readSet.GetEntry(aKey)))
--    return;
--
--  // Read in the data synchronously.
--  // see IDX_NAME, etc. for parameter indexes
--  nsresult rv;
--  if (!mDefaultDBState->stmtReadDomain) {
--    // Cache the statement, since it's likely to be used again.
--    rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
--      "SELECT "
--        "name, "
--        "value, "
--        "host, "
--        "path, "
--        "expiry, "
--        "lastAccessed, "
--        "creationTime, "
--        "isSecure, "
--        "isHttpOnly, "
--        "baseDomain, "
--        "originAttributes, "
--        "sameSite "
--      "FROM moz_cookies "
--      "WHERE baseDomain = :baseDomain "
--      "  AND originAttributes = :originAttributes"),
--      getter_AddRefs(mDefaultDBState->stmtReadDomain));
--
--    if (NS_FAILED(rv)) {
--      // Recreate the database.
--      COOKIE_LOGSTRING(LogLevel::Debug,
--        ("EnsureReadDomain(): corruption detected when creating statement "
--         "with rv 0x%" PRIx32, static_cast<uint32_t>(rv)));
--      HandleCorruptDB(mDefaultDBState);
--      return;
-+  MOZ_ASSERT(NS_IsMainThread());
-+
-+  if (!mInitializedDBStates) {
-+    TimeStamp startBlockTime = TimeStamp::Now();
-+    MonitorAutoLock lock(mMonitor);
-+
-+    while (!mInitializedDBStates) {
-+      mMonitor.Wait();
-     }
-+    Telemetry::AccumulateTimeDelta(Telemetry::MOZ_SQLITE_COOKIES_BLOCK_MAIN_THREAD_MS,
-+                                   startBlockTime);
-+    mAccumulatedWaitTelemetry = true;
-   }
--
--  NS_ASSERTION(mDefaultDBState->syncConn, "should have a sync db connection");
--
--  mozStorageStatementScoper scoper(mDefaultDBState->stmtReadDomain);
--
--  rv = mDefaultDBState->stmtReadDomain->BindUTF8StringByName(
--    NS_LITERAL_CSTRING("baseDomain"), aKey.mBaseDomain);
--  NS_ASSERT_SUCCESS(rv);
--
--  nsAutoCString suffix;
--  aKey.mOriginAttributes.CreateSuffix(suffix);
--  rv = mDefaultDBState->stmtReadDomain->BindUTF8StringByName(
--    NS_LITERAL_CSTRING("originAttributes"), suffix);
--  NS_ASSERT_SUCCESS(rv);
--
--  bool hasResult;
--  nsCString name, value, host, path;
--  AutoTArray<RefPtr<nsCookie>, kMaxCookiesPerHost> array;
--  while (true) {
--    rv = mDefaultDBState->stmtReadDomain->ExecuteStep(&hasResult);
--    if (NS_FAILED(rv)) {
--      // Recreate the database.
--      COOKIE_LOGSTRING(LogLevel::Debug,
--        ("EnsureReadDomain(): corruption detected when reading result "
--         "with rv 0x%" PRIx32, static_cast<uint32_t>(rv)));
--      HandleCorruptDB(mDefaultDBState);
--      return;
--    }
--
--    if (!hasResult)
--      break;
--
--    array.AppendElement(GetCookieFromRow(mDefaultDBState->stmtReadDomain,
--                                         aKey.mOriginAttributes));
-+  if (!mInitializedDBConn && aInitDBConn && mDefaultDBState) {
-+    InitDBConn();
-   }
--
--  mozStorageTransaction transaction(mDefaultDBState->dbConn, false);
--  // Add the cookies to the table in a single operation. This makes sure that
--  // either all the cookies get added, or in the case of corruption, none.
--  for (uint32_t i = 0; i < array.Length(); ++i) {
--    AddCookieToList(aKey, array[i], mDefaultDBState, nullptr, false);
--  }
--  rv = transaction.Commit();
--  MOZ_ASSERT(NS_SUCCEEDED(rv));
--
--  // Add it to the hashset of read entries, so we don't read it again.
--  mDefaultDBState->readSet.PutEntry(aKey);
--
--  COOKIE_LOGSTRING(LogLevel::Debug,
--    ("EnsureReadDomain(): %zu cookies read for base domain %s, "
--     " originAttributes = %s", array.Length(), aKey.mBaseDomain.get(),
--     suffix.get()));
- }
- 
--void
--nsCookieService::EnsureReadComplete()
-+OpenDBResult
-+nsCookieService::Read()
- {
--  NS_ASSERTION(!mDBState->dbConn || mDBState == mDefaultDBState,
--    "not in default db state");
--
--  // Fast path 1: nothing to read, or we've already finished reading.
--  if (MOZ_LIKELY(!mDBState->dbConn || !mDefaultDBState->pendingRead))
--    return;
--
--  // Cancel the pending read, so we don't get any more results.
--  CancelAsyncRead(false);
-+  MOZ_ASSERT(NS_GetCurrentThread() == mThread);
-+
-+  // Set up a statement to delete any rows with a nullptr 'baseDomain'
-+  // column. This takes care of any cookies set by browsers that don't
-+  // understand the 'baseDomain' column, where the database schema version
-+  // is from one that does. (This would occur when downgrading.)
-+  nsresult rv = mDefaultDBState->syncConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-+                  "DELETE FROM moz_cookies WHERE baseDomain ISNULL"));
-+  NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-   // Read in the data synchronously.
-   // see IDX_NAME, etc. for parameter indexes
-   nsCOMPtr<mozIStorageStatement> stmt;
--  nsresult rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
-+  rv = mDefaultDBState->syncConn->CreateStatement(NS_LITERAL_CSTRING(
-     "SELECT "
-       "name, "
-       "value, "
-       "host, "
-       "path, "
-       "expiry, "
-       "lastAccessed, "
-       "creationTime, "
-       "isSecure, "
-       "isHttpOnly, "
-       "baseDomain, "
-       "originAttributes, "
-       "sameSite "
-     "FROM moz_cookies "
-     "WHERE baseDomain NOTNULL"), getter_AddRefs(stmt));
- 
--  if (NS_FAILED(rv)) {
--    // Recreate the database.
--    COOKIE_LOGSTRING(LogLevel::Debug,
--      ("EnsureReadComplete(): corruption detected when creating statement "
--       "with rv 0x%" PRIx32, static_cast<uint32_t>(rv)));
--    HandleCorruptDB(mDefaultDBState);
--    return;
-+  NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-+
-+  if (NS_WARN_IF(!mReadArray.IsEmpty())) {
-+    mReadArray.Clear();
-   }
-+  mReadArray.SetCapacity(kMaxNumberOfCookies);
- 
-   nsCString baseDomain, name, value, host, path;
-   bool hasResult;
--  nsTArray<CookieDomainTuple> array(kMaxNumberOfCookies);
-   while (true) {
-     rv = stmt->ExecuteStep(&hasResult);
--    if (NS_FAILED(rv)) {
--      // Recreate the database.
--      COOKIE_LOGSTRING(LogLevel::Debug,
--        ("EnsureReadComplete(): corruption detected when reading result "
--         "with rv 0x%" PRIx32, static_cast<uint32_t>(rv)));
--      HandleCorruptDB(mDefaultDBState);
--      return;
-+    if (NS_WARN_IF(NS_FAILED(rv))) {
-+      mReadArray.Clear();
-+      return RESULT_RETRY;
-     }
- 
-     if (!hasResult)
-       break;
- 
-     // Make sure we haven't already read the data.
-     stmt->GetUTF8String(IDX_BASE_DOMAIN, baseDomain);
- 
-     nsAutoCString suffix;
-     OriginAttributes attrs;
-     stmt->GetUTF8String(IDX_ORIGIN_ATTRIBUTES, suffix);
-     // If PopulateFromSuffix failed we just ignore the OA attributes
-     // that we don't support
-     Unused << attrs.PopulateFromSuffix(suffix);
- 
-     nsCookieKey key(baseDomain, attrs);
--    if (mDefaultDBState->readSet.GetEntry(key))
--      continue;
--
--    CookieDomainTuple* tuple = array.AppendElement();
-+    CookieDomainTuple* tuple = mReadArray.AppendElement();
-     tuple->key = key;
-     tuple->cookie = GetCookieFromRow(stmt, attrs);
-   }
- 
--  mozStorageTransaction transaction(mDefaultDBState->dbConn, false);
--  // Add the cookies to the table in a single operation. This makes sure that
--  // either all the cookies get added, or in the case of corruption, none.
--  for (uint32_t i = 0; i < array.Length(); ++i) {
--    CookieDomainTuple& tuple = array[i];
--    AddCookieToList(tuple.key, tuple.cookie, mDefaultDBState, nullptr,
--      false);
--  }
--  rv = transaction.Commit();
--  MOZ_ASSERT(NS_SUCCEEDED(rv));
--
-   mDefaultDBState->syncConn = nullptr;
--  mDefaultDBState->readSet.Clear();
--
--  COOKIE_LOGSTRING(LogLevel::Debug,
--    ("EnsureReadComplete(): %zu cookies read", array.Length()));
-+
-+  COOKIE_LOGSTRING(LogLevel::Debug, ("Read(): %zu cookies read", mReadArray.Length()));
-+
-+  return RESULT_OK;
- }
- 
- NS_IMETHODIMP
- nsCookieService::ImportCookies(nsIFile *aCookieFile)
- {
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
- 
-+  EnsureReadComplete(true);
-+
-   // Make sure we're in the default DB state. We don't want people importing
-   // cookies into a private browsing session!
-   if (mDBState != mDefaultDBState) {
-     NS_WARNING("Trying to import cookies in a private browsing session!");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
- 
-   nsresult rv;
-   nsCOMPtr<nsIInputStream> fileInputStream;
-   rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), aCookieFile);
-   if (NS_FAILED(rv)) return rv;
- 
-   nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
-   if (NS_FAILED(rv)) return rv;
- 
--  // First, ensure we've read in everything from the database, if we have one.
--  EnsureReadComplete();
--
-   static const char kTrue[] = "TRUE";
- 
-   nsAutoCString buffer, baseDomain;
-   bool isMore = true;
-   int32_t hostIndex, isDomainIndex, pathIndex, secureIndex, expiresIndex, nameIndex, cookieIndex;
-   nsACString::char_iterator iter;
-   int32_t numInts;
-   int64_t expires;
-@@ -3286,16 +3085,18 @@ nsCookieService::GetCookiesForURI(nsIURI
- {
-   NS_ASSERTION(aHostURI, "null host!");
- 
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return;
-   }
- 
-+  EnsureReadComplete(true);
-+
-   AutoRestore<DBState*> savePrevDBState(mDBState);
-   mDBState = (aOriginAttrs.mPrivateBrowsingId > 0) ? mPrivateDBState : mDefaultDBState;
- 
-   // get the base domain, host, and path from the URI.
-   // e.g. for "www.bbc.co.uk", the base domain would be "bbc.co.uk".
-   // file:// URI's (i.e. with an empty host) are allowed, but any other
-   // scheme must have a non-empty host. A trailing dot in the host
-   // is acceptable.
-@@ -3343,17 +3144,16 @@ nsCookieService::GetCookiesForURI(nsIURI
-   }
- 
-   nsCookie *cookie;
-   int64_t currentTimeInUsec = PR_Now();
-   int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
-   bool stale = false;
- 
-   nsCookieKey key(baseDomain, aOriginAttrs);
--  EnsureReadDomain(key);
- 
-   // perform the hash lookup
-   nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
-   if (!entry)
-     return;
- 
-   // iterate the cookies!
-   const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
-@@ -3700,16 +3500,19 @@ nsCookieService::SetCookieInternal(nsIUR
- void
- nsCookieService::AddInternal(const nsCookieKey &aKey,
-                              nsCookie          *aCookie,
-                              int64_t            aCurrentTimeInUsec,
-                              nsIURI            *aHostURI,
-                              const char        *aCookieHeader,
-                              bool               aFromHttp)
- {
-+  MOZ_ASSERT(mInitializedDBStates);
-+  MOZ_ASSERT(mInitializedDBConn);
-+
-   int64_t currentTime = aCurrentTimeInUsec / PR_USEC_PER_SEC;
- 
-   nsListIter exactIter;
-   bool foundCookie = false;
-   foundCookie = FindCookie(aKey, aCookie->Host(),
-                            aCookie->Name(), aCookie->Path(), exactIter);
-   bool foundSecureExact = foundCookie && exactIter.Cookie()->IsSecure();
-   bool isSecure = true;
-@@ -4612,17 +4415,16 @@ public:
-   }
- };
- 
- // purges expired and old cookies in a batch operation.
- already_AddRefed<nsIArray>
- nsCookieService::PurgeCookies(int64_t aCurrentTimeInUsec)
- {
-   NS_ASSERTION(mDBState->hostTable.Count() > 0, "table is empty");
--  EnsureReadComplete();
- 
-   uint32_t initialCookieCount = mDBState->cookieCount;
-   COOKIE_LOGSTRING(LogLevel::Debug,
-     ("PurgeCookies(): beginning purge with %" PRIu32 " cookies and %" PRId64 " oldest age",
-      mDBState->cookieCount, aCurrentTimeInUsec - mDBState->cookieOldestTime));
- 
-   typedef nsTArray<nsListIter> PurgeList;
-   PurgeList purgeList(kMaxNumberOfCookies);
-@@ -4762,16 +4564,18 @@ nsCookieService::CookieExistsNative(nsIC
-   NS_ENSURE_ARG_POINTER(aOriginAttributes);
-   NS_ENSURE_ARG_POINTER(aFoundCookie);
- 
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
- 
-+  EnsureReadComplete(true);
-+
-   AutoRestore<DBState*> savePrevDBState(mDBState);
-   mDBState = (aOriginAttributes->mPrivateBrowsingId > 0) ? mPrivateDBState : mDefaultDBState;
- 
-   nsAutoCString host, name, path;
-   nsresult rv = aCookie->GetHost(host);
-   NS_ENSURE_SUCCESS(rv, rv);
-   rv = aCookie->GetName(name);
-   NS_ENSURE_SUCCESS(rv, rv);
-@@ -4903,27 +4707,28 @@ NS_IMETHODIMP
- nsCookieService::CountCookiesFromHost(const nsACString &aHost,
-                                       uint32_t         *aCountFromHost)
- {
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
- 
-+  EnsureReadComplete(true);
-+
-   // first, normalize the hostname, and fail if it contains illegal characters.
-   nsAutoCString host(aHost);
-   nsresult rv = NormalizeHost(host);
-   NS_ENSURE_SUCCESS(rv, rv);
- 
-   nsAutoCString baseDomain;
-   rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
-   NS_ENSURE_SUCCESS(rv, rv);
- 
-   nsCookieKey key = DEFAULT_APP_KEY(baseDomain);
--  EnsureReadDomain(key);
- 
-   // Return a count of all cookies, including expired.
-   nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
-   *aCountFromHost = entry ? entry->GetCookies().Length() : 0;
-   return NS_OK;
- }
- 
- // get an enumerator of cookies stored by a particular host. this is provided by the
-@@ -4937,16 +4742,18 @@ nsCookieService::GetCookiesFromHost(cons
- {
-   MOZ_ASSERT(aArgc == 0 || aArgc == 1);
- 
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
- 
-+  EnsureReadComplete(true);
-+
-   // first, normalize the hostname, and fail if it contains illegal characters.
-   nsAutoCString host(aHost);
-   nsresult rv = NormalizeHost(host);
-   NS_ENSURE_SUCCESS(rv, rv);
- 
-   nsAutoCString baseDomain;
-   rv = GetBaseDomainFromHost(mTLDService, host, baseDomain);
-   NS_ENSURE_SUCCESS(rv, rv);
-@@ -4959,17 +4766,16 @@ nsCookieService::GetCookiesFromHost(cons
-                                   u"nsICookieManager2.getCookiesFromHost()",
-                                   u"2");
-   NS_ENSURE_SUCCESS(rv, rv);
- 
-   AutoRestore<DBState*> savePrevDBState(mDBState);
-   mDBState = (attrs.mPrivateBrowsingId > 0) ? mPrivateDBState : mDefaultDBState;
- 
-   nsCookieKey key = nsCookieKey(baseDomain, attrs);
--  EnsureReadDomain(key);
- 
-   nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
-   if (!entry)
-     return NS_NewEmptyEnumerator(aEnumerator);
- 
-   nsCOMArray<nsICookie> cookieList(mMaxCookiesPerHost);
-   const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
-   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
-@@ -5005,16 +4811,17 @@ nsCookieService::GetCookiesWithOriginAtt
-     const mozilla::OriginAttributesPattern& aPattern,
-     const nsCString& aBaseDomain,
-     nsISimpleEnumerator **aEnumerator)
- {
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already closed?");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
-+  EnsureReadComplete(true);
- 
-   AutoRestore<DBState*> savePrevDBState(mDBState);
-   mDBState = (aPattern.mPrivateBrowsingId.WasPassed() &&
-       aPattern.mPrivateBrowsingId.Value() > 0) ? mPrivateDBState : mDefaultDBState;
- 
-   nsCOMArray<nsICookie> cookies;
-   for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
-     nsCookieEntry* entry = iter.Get();
-@@ -5064,16 +4871,18 @@ nsCookieService::RemoveCookiesWithOrigin
-     const mozilla::OriginAttributesPattern& aPattern,
-     const nsCString& aBaseDomain)
- {
-   if (!mDBState) {
-     NS_WARNING("No DBState! Profile already close?");
-     return NS_ERROR_NOT_AVAILABLE;
-   }
- 
-+  EnsureReadComplete(true);
-+
-   AutoRestore<DBState*> savePrevDBState(mDBState);
-   mDBState = (aPattern.mPrivateBrowsingId.WasPassed() &&
-       aPattern.mPrivateBrowsingId.Value() > 0) ? mPrivateDBState : mDefaultDBState;
- 
-   mozStorageTransaction transaction(mDBState->dbConn, false);
-   // Iterate the hash table of nsCookieEntry.
-   for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
-     nsCookieEntry* entry = iter.Get();
-@@ -5112,18 +4921,16 @@ nsCookieService::RemoveCookiesWithOrigin
-   return NS_OK;
- }
- 
- // find an secure cookie specified by host and name
- bool
- nsCookieService::FindSecureCookie(const nsCookieKey &aKey,
-                                   nsCookie          *aCookie)
- {
--  EnsureReadDomain(aKey);
--
-   nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey);
-   if (!entry)
-     return false;
- 
-   const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
-   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
-     nsCookie *cookie = cookies[i];
-     // isn't a match if insecure or a different name
-@@ -5149,17 +4956,19 @@ nsCookieService::FindSecureCookie(const 
- // find an exact cookie specified by host, name, and path that hasn't expired.
- bool
- nsCookieService::FindCookie(const nsCookieKey    &aKey,
-                             const nsCString& aHost,
-                             const nsCString& aName,
-                             const nsCString& aPath,
-                             nsListIter           &aIter)
- {
--  EnsureReadDomain(aKey);
-+  // Should |EnsureReadComplete| before.
-+  MOZ_ASSERT(mInitializedDBStates);
-+  MOZ_ASSERT(mInitializedDBConn);
- 
-   nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey);
-   if (!entry)
-     return false;
- 
-   const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
-   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
-     nsCookie *cookie = cookies[i];
-diff --git a/netwerk/cookie/nsCookieService.h b/netwerk/cookie/nsCookieService.h
---- a/netwerk/cookie/nsCookieService.h
-+++ b/netwerk/cookie/nsCookieService.h
-@@ -24,30 +24,35 @@
- #include "mozIStoragePendingStatement.h"
- #include "mozIStorageConnection.h"
- #include "mozIStorageRow.h"
- #include "mozIStorageCompletionCallback.h"
- #include "mozIStorageStatementCallback.h"
- #include "mozIStorageFunction.h"
- #include "nsIVariant.h"
- #include "nsIFile.h"
-+#include "mozilla/Atomics.h"
- #include "mozilla/BasePrincipal.h"
- #include "mozilla/MemoryReporting.h"
- #include "mozilla/Maybe.h"
-+#include "mozilla/Monitor.h"
-+#include "mozilla/UniquePtr.h"
-+
- 
- using mozilla::OriginAttributes;
- 
- class nsICookiePermission;
- class nsIEffectiveTLDService;
- class nsIIDNService;
- class nsIPrefBranch;
- class nsIObserverService;
- class nsIURI;
- class nsIChannel;
- class nsIArray;
-+class nsIThread;
- class mozIStorageService;
- class mozIThirdPartyUtil;
- class ReadCookieDBListener;
- 
- struct nsListIter;
- 
- namespace mozilla {
- namespace net {
-@@ -83,23 +88,62 @@ class nsCookieEntry : public nsCookieKey
-     inline ArrayType& GetCookies() { return mCookies; }
- 
-     size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
- 
-   private:
-     ArrayType mCookies;
- };
- 
-+// struct for a constant cookie for threadsafe
-+struct ConstCookie
-+{
-+  ConstCookie(const nsCString& aName,
-+              const nsCString& aValue,
-+              const nsCString& aHost,
-+              const nsCString& aPath,
-+              int64_t aExpiry,
-+              int64_t aLastAccessed,
-+              int64_t aCreationTime,
-+              bool aIsSecure,
-+              bool aIsHttpOnly,
-+              const OriginAttributes &aOriginAttributes,
-+              int32_t aSameSite)
-+    : name(aName)
-+    , value(aValue)
-+    , host(aHost)
-+    , path(aPath)
-+    , expiry(aExpiry)
-+    , lastAccessed(aLastAccessed)
-+    , creationTime(aCreationTime)
-+    , isSecure(aIsSecure)
-+    , isHttpOnly(aIsHttpOnly)
-+    , originAttributes(aOriginAttributes)
-+    , sameSite(aSameSite)
-+  {
-+  }
-+
-+  const nsCString name;
-+  const nsCString value;
-+  const nsCString host;
-+  const nsCString path;
-+  const int64_t expiry;
-+  const int64_t lastAccessed;
-+  const int64_t creationTime;
-+  const bool isSecure;
-+  const bool isHttpOnly;
-+  const OriginAttributes originAttributes;
-+  const int32_t sameSite;
-+};
-+
- // encapsulates a (key, nsCookie) tuple for temporary storage purposes.
- struct CookieDomainTuple
- {
-   nsCookieKey key;
--  RefPtr<nsCookie> cookie;
--
--  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
-+  mozilla::UniquePtr<ConstCookie> cookie;
- };
- 
- // encapsulates in-memory and on-disk DB states, so we can
- // conveniently switch state when entering or exiting private browsing.
- struct DBState final
- {
-   DBState() : cookieCount(0), cookieOldestTime(INT64_MAX), corruptFlag(OK)
-   {
-@@ -132,27 +176,19 @@ public:
-   nsCOMPtr<mozIStorageAsyncStatement> stmtDelete;
-   nsCOMPtr<mozIStorageAsyncStatement> stmtUpdate;
-   CorruptFlag                     corruptFlag;
- 
-   // Various parts representing asynchronous read state. These are useful
-   // while the background read is taking place.
-   nsCOMPtr<mozIStorageConnection>       syncConn;
-   nsCOMPtr<mozIStorageStatement>        stmtReadDomain;
--  nsCOMPtr<mozIStoragePendingStatement> pendingRead;
-   // The asynchronous read listener. This is a weak ref (storage has ownership)
-   // since it may need to outlive the DBState's database connection.
-   ReadCookieDBListener*                 readListener;
--  // An array of (baseDomain, cookie) tuples representing data read in
--  // asynchronously. This is merged into hostTable once read is complete.
--  nsTArray<CookieDomainTuple>           hostArray;
--  // A hashset of baseDomains read in synchronously, while the async read is
--  // in flight. This is used to keep track of which data in hostArray is stale
--  // when the time comes to merge.
--  nsTHashtable<nsCookieKey>        readSet;
- 
-   // DB completion handlers.
-   nsCOMPtr<mozIStorageStatementCallback>  insertListener;
-   nsCOMPtr<mozIStorageStatementCallback>  updateListener;
-   nsCOMPtr<mozIStorageStatementCallback>  removeListener;
-   nsCOMPtr<mozIStorageCompletionCallback> closeListener;
- };
- 
-@@ -237,33 +273,32 @@ class nsCookieService final : public nsI
-   void GetCookiesForURI(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, const OriginAttributes& aOriginAttrs, nsTArray<nsCookie*>& aCookieList);
- 
-   protected:
-     virtual ~nsCookieService();
- 
-     void                          PrefChanged(nsIPrefBranch *aPrefBranch);
-     void                          InitDBStates();
-     OpenDBResult                  TryInitDB(bool aDeleteExistingDB);
-+    void                          InitDBConn();
-+    nsresult                      InitDBConnInternal();
-     nsresult                      CreateTableWorker(const char* aName);
-     nsresult                      CreateIndex();
-     nsresult                      CreateTable();
-     nsresult                      CreateTableForSchemaVersion6();
-     nsresult                      CreateTableForSchemaVersion5();
-     void                          CloseDBStates();
-     void                          CleanupCachedStatements();
-     void                          CleanupDefaultDBConnection();
-     void                          HandleDBClosed(DBState* aDBState);
-     void                          HandleCorruptDB(DBState* aDBState);
-     void                          RebuildCorruptDB(DBState* aDBState);
-     OpenDBResult                  Read();
--    template<class T> nsCookie*   GetCookieFromRow(T &aRow, const OriginAttributes& aOriginAttributes);
--    void                          AsyncReadComplete();
--    void                          CancelAsyncRead(bool aPurgeReadSet);
--    void                          EnsureReadDomain(const nsCookieKey &aKey);
--    void                          EnsureReadComplete();
-+    mozilla::UniquePtr<ConstCookie> GetCookieFromRow(mozIStorageStatement *aRow, const OriginAttributes &aOriginAttributes);
-+    void                          EnsureReadComplete(bool aInitDBConn);
-     nsresult                      NormalizeHost(nsCString &aHost);
-     nsresult                      GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel, bool aHttpBound, char** aCookie);
-     void                          GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, const OriginAttributes& aOriginAttrs, nsCString &aCookie);
-     nsresult                      SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, bool aFromHttp);
-     void                          SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp, const OriginAttributes &aOriginAttrs, nsIChannel* aChannel);
-     bool                          SetCookieInternal(nsIURI *aHostURI, const nsCookieKey& aKey, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp, nsIChannel* aChannel);
-     void                          AddInternal(const nsCookieKey& aKey, nsCookie *aCookie, int64_t aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, bool aFromHttp);
-     void                          RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = nullptr);
-@@ -322,16 +357,24 @@ class nsCookieService final : public nsI
-     uint8_t                       mCookieBehavior; // BEHAVIOR_{ACCEPT, REJECTFOREIGN, REJECT, LIMITFOREIGN}
-     bool                          mThirdPartySession;
-     bool                          mThirdPartyNonsecureSession;
-     bool                          mLeaveSecureAlone;
-     uint16_t                      mMaxNumberOfCookies;
-     uint16_t                      mMaxCookiesPerHost;
-     int64_t                       mCookiePurgeAge;
- 
-+    // thread
-+    nsCOMPtr<nsIThread>           mThread;
-+    mozilla::Monitor              mMonitor;
-+    mozilla::Atomic<bool>         mInitializedDBStates;
-+    mozilla::Atomic<bool>         mInitializedDBConn;
-+    bool                          mAccumulatedWaitTelemetry;
-+    nsTArray<CookieDomainTuple>   mReadArray;
-+
-     // friends!
-     friend class DBListenerErrorHandler;
-     friend class ReadCookieDBListener;
-     friend class CloseCookieDBListener;
- 
-     static already_AddRefed<nsCookieService> GetSingleton();
-     friend class mozilla::net::CookieServiceParent;
- };
-diff --git a/netwerk/cookie/test/unit/test_bug1321912.js b/netwerk/cookie/test/unit/test_bug1321912.js
---- a/netwerk/cookie/test/unit/test_bug1321912.js
-+++ b/netwerk/cookie/test/unit/test_bug1321912.js
-@@ -45,19 +45,19 @@ conn.executeSimpleSQL("PRAGMA wal_autoch
- let now = Date.now();
- conn.executeSimpleSQL("INSERT INTO moz_cookies(" +
-   "baseDomain, host, name, value, path, expiry, " +
-   "lastAccessed, creationTime, isSecure, isHttpOnly) VALUES (" +
-   "'foo.com', '.foo.com', 'foo', 'bar=baz', '/', " +
-   now + ", " + now + ", " + now + ", 1, 1)");
- 
- // Now start the cookie service, and then check the fields in the table.
--
--const cs = Cc["@mozilla.org/cookieService;1"].
--           getService(Ci.nsICookieService);
-+// Get sessionEnumerator to wait for the initialization in cookie thread
-+const enumerator = Cc["@mozilla.org/cookieService;1"].
-+                   getService(Ci.nsICookieManager).sessionEnumerator;
- 
- do_check_true(conn.schemaVersion, 8);
- let stmt = conn.createStatement("SELECT sql FROM sqlite_master " +
-                                   "WHERE type = 'table' AND " +
-                                   "      name = 'moz_cookies'");
- try {
-   do_check_true(stmt.executeStep());
-   let sql = stmt.getString(0);
-diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
---- a/toolkit/components/telemetry/Histograms.json
-+++ b/toolkit/components/telemetry/Histograms.json
-@@ -4408,16 +4408,26 @@
-   "MOZ_SQLITE_COOKIES_READ_MS": {
-     "record_in_processes": ["main", "content"],
-     "expires_in_version": "40",
-     "kind": "exponential",
-     "high": 3000,
-     "n_buckets": 10,
-     "description": "Time spent on SQLite read() (ms) *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***"
-   },
-+  "MOZ_SQLITE_COOKIES_BLOCK_MAIN_THREAD_MS": {
-+    "record_in_processes": ["main"],
-+    "expires_in_version": "never",
-+    "kind": "exponential",
-+    "alert_emails": ["necko@mozilla.com", "junior@mozilla.com"],
-+    "bug_numbers": [870460],
-+    "high": 3000,
-+    "n_buckets": 10,
-+    "description": "Time spent on blocking main thread by startup cookie database read (ms)"
-+  },
-   "MOZ_SQLITE_COOKIES_READ_MAIN_THREAD_MS": {
-     "record_in_processes": ["main", "content"],
-     "expires_in_version": "40",
-     "kind": "exponential",
-     "high": 3000,
-     "n_buckets": 10,
-     "description": "Time spent on SQLite read() (ms) *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***"
-   },

+ 0 - 112
frg/work-js/mozilla-release/patches/mozilla-central_388197.patch

@@ -1,112 +0,0 @@
-# HG changeset patch
-# User Junior Hsu <juhsu@mozilla.com>
-# Date 1507522740 14400
-#      Mon Oct 09 00:19:00 2017 -0400
-# Node ID 17f26189df9a5c07f5550391f71127c17d7fda48
-# Parent  1d83d62684e2971fc9705f8950521e1a9afd6ae4
-Bug 870460 - Part 2: Close syncconn for edge cases. r=nwgh
-
-diff --git a/extensions/cookie/test/unit/test_cookies_async_failure.js b/extensions/cookie/test/unit/test_cookies_async_failure.js
---- a/extensions/cookie/test/unit/test_cookies_async_failure.js
-+++ b/extensions/cookie/test/unit/test_cookies_async_failure.js
-@@ -453,18 +453,19 @@ function* run_test_5(generator)
-   do_check_true(do_get_backup_file(profile).exists());
-   do_check_eq(do_get_backup_file(profile).fileSize, size);
- 
-   do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 0);
-   do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 0);
-   do_check_eq(do_count_cookies(), 0);
- 
-   // Close the profile. We do not need to wait for completion, because the
--  // database has already been closed.
--  do_close_profile();
-+  // database has already been closed. Ensure the cookie file is unlocked.
-+  do_close_profile(sub_generator);
-+  yield;
- 
-   // Clean up.
-   do_get_cookie_file(profile).remove(false);
-   do_get_backup_file(profile).remove(false);
-   do_check_false(do_get_cookie_file(profile).exists());
-   do_check_false(do_get_backup_file(profile).exists());
-   do_run_generator(generator);
- }
-diff --git a/netwerk/cookie/nsCookieService.cpp b/netwerk/cookie/nsCookieService.cpp
---- a/netwerk/cookie/nsCookieService.cpp
-+++ b/netwerk/cookie/nsCookieService.cpp
-@@ -50,16 +50,17 @@
- #include "nsNetCID.h"
- #include "nsISimpleEnumerator.h"
- #include "nsIInputStream.h"
- #include "nsAppDirectoryServiceDefs.h"
- #include "nsNetCID.h"
- #include "mozilla/storage.h"
- #include "mozilla/AutoRestore.h"
- #include "mozilla/FileUtils.h"
-+#include "mozilla/ScopeExit.h"
- #include "mozilla/Telemetry.h"
- #include "nsIConsoleService.h"
- #include "nsVariant.h"
- 
- using namespace mozilla;
- using namespace mozilla::net;
- 
- // Create key from baseDomain that will access the default cookie namespace.
-@@ -889,16 +890,20 @@ nsCookieService::TryInitDB(bool aRecreat
-     // open a connection to the cookie database, and only cache our connection
-     // and statements upon success. The connection is opened unshared to eliminate
-     // cache contention between the main and background threads.
-     rv = mStorageService->OpenUnsharedDatabase(mDefaultDBState->cookieFile,
-       getter_AddRefs(mDefaultDBState->syncConn));
-     NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
-   }
- 
-+  auto guard = MakeScopeExit([&] {
-+    mDefaultDBState->syncConn = nullptr;
-+  });
-+
-   bool tableExists = false;
-   mDefaultDBState->syncConn->TableExists(NS_LITERAL_CSTRING("moz_cookies"),
-     &tableExists);
-   if (!tableExists) {
-     rv = CreateTable();
-     NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
- 
-   } else {
-@@ -1742,17 +1747,16 @@ nsCookieService::CleanupDefaultDBConnect
-   MOZ_ASSERT(!mDefaultDBState->stmtInsert, "stmtInsert has been cleaned up");
-   MOZ_ASSERT(!mDefaultDBState->stmtDelete, "stmtDelete has been cleaned up");
-   MOZ_ASSERT(!mDefaultDBState->stmtUpdate, "stmtUpdate has been cleaned up");
- 
-   // Null out the database connections. If 'dbConn' has not been used for any
-   // asynchronous operations yet, this will synchronously close it; otherwise,
-   // it's expected that the caller has performed an AsyncClose prior.
-   mDefaultDBState->dbConn = nullptr;
--  mDefaultDBState->syncConn = nullptr;
- 
-   // Manually null out our listeners. This is necessary because they hold a
-   // strong ref to the DBState itself. They'll stay alive until whatever
-   // statements are still executing complete.
-   mDefaultDBState->insertListener = nullptr;
-   mDefaultDBState->updateListener = nullptr;
-   mDefaultDBState->removeListener = nullptr;
-   mDefaultDBState->closeListener = nullptr;
-@@ -2835,18 +2839,16 @@ nsCookieService::Read()
-     Unused << attrs.PopulateFromSuffix(suffix);
- 
-     nsCookieKey key(baseDomain, attrs);
-     CookieDomainTuple* tuple = mReadArray.AppendElement();
-     tuple->key = key;
-     tuple->cookie = GetCookieFromRow(stmt, attrs);
-   }
- 
--  mDefaultDBState->syncConn = nullptr;
--
-   COOKIE_LOGSTRING(LogLevel::Debug, ("Read(): %zu cookies read", mReadArray.Length()));
- 
-   return RESULT_OK;
- }
- 
- NS_IMETHODIMP
- nsCookieService::ImportCookies(nsIFile *aCookieFile)
- {

+ 0 - 30
frg/work-js/mozilla-release/patches/mozilla-central_388198.patch

@@ -1,30 +0,0 @@
-# HG changeset patch
-# User Junior Hsu <juhsu@mozilla.com>
-# Date 1507769940 14400
-#      Wed Oct 11 20:59:00 2017 -0400
-# Node ID 3e5213e725a2604383db272aca4e15833f8bd02c
-# Parent  17f26189df9a5c07f5550391f71127c17d7fda48
-Bug 870460 - Part 3: Add cookies.sqlite{|-shm} to xperf whitelist. r=jmaher
-
-diff --git a/testing/talos/talos/xtalos/xperf_whitelist.json b/testing/talos/talos/xtalos/xperf_whitelist.json
---- a/testing/talos/talos/xtalos/xperf_whitelist.json
-+++ b/testing/talos/talos/xtalos/xperf_whitelist.json
-@@ -23,16 +23,18 @@
-  "{talos}\\talos\\tests\\tp5n\\tp5n.manifest.develop": {"mincount": 0, "maxcount": 8, "minbytes": 0, "maxbytes": 32786},
-  "{profile}\\localstore.rdf": {"mincount": 2, "maxcount": 2, "minbytes": 8192, "maxbytes": 8192},
-  "{firefox}\\dependentlibs.list": {"mincount": 4, "maxcount": 4, "minbytes": 16384, "maxbytes": 16384},
-  "{profile}\\content-prefs.sqlite": {"mincount": 6, "maxcount": 6, "minbytes": 65768, "maxbytes": 65768},
-  "{profile}\\extensions.ini": {"mincount": 2, "maxcount": 2, "minbytes": 8192, "maxbytes": 8192},
-  "{profile}\\containers.json": {"mincount": 0, "maxcount": 4, "minbytes": 0, "maxbytes": 512},
-  "{profile}\\extensions.json": {"mincount": 0, "maxcount": 4, "minbytes": 0, "maxbytes": 512},
-  "{profile}\\times.json": {"mincount": 0, "maxcount": 4, "minbytes": 0, "maxbytes": 512},
-+ "{profile}\\cookies.sqlite-shm": {"mincount": 1, "maxcount": 6, "minbytes": 0, "maxbytes": 200000},
-+ "{profile}\\cookies.sqlite": {"mincount": 1, "maxcount": 6, "minbytes": 0, "maxbytes": 200000},
-  "{profile}\\sessionstore-backups\\recovery.js": {"mincount": 0, "maxcount": 2, "minbytes": 0, "maxbytes": 5602},
-  "{profile}\\sessioncheckpoints.json.tmp": {"mincount": 0, "maxcount": 2, "minbytes": 0, "maxbytes": 512},
-  "{profile}\\sessioncheckpoints.json": {"mincount": 0, "maxcount": 2, "minbytes": 0, "maxbytes": 512},
-  "{profile}\\extensions\\pageloader@mozilla.org\\chrome.manifest": {"mincount": 2, "maxcount": 2, "minbytes": 600, "maxbytes": 600},
-  "{profile}\\extensions\\pageloader@mozilla.org\\chrome\\mozillafilelogger.js": {"mincount": 0, "maxcount": 4, "minbytes": 0, "maxbytes": 125200},
-  "{profile}\\extensions\\pageloader@mozilla.org\\chrome\\memory.js": {"mincount": 0, "maxcount": 4, "minbytes": 0, "maxbytes": 127000},
-  "{profile}\\extensions\\pageloader@mozilla.org\\chrome\\profiler.js": {"mincount": 0, "maxcount": 4, "minbytes": 0, "maxbytes": 127000},
-  "{profile}\\extensions\\pageloader@mozilla.org\\chrome\\pageloader.js": {"mincount": 0, "maxcount": 2, "minbytes": 0, "maxbytes": 127000},

+ 0 - 110
frg/work-js/mozilla-release/patches/mozilla-central_388199.patch

@@ -1,110 +0,0 @@
-# HG changeset patch
-# User Junior Hsu <juhsu@mozilla.com>
-# Date 1508909400 14400
-#      Wed Oct 25 01:30:00 2017 -0400
-# Node ID 3531caed0bd9b945451c616f7def09bc44ee24bf
-# Parent  3e5213e725a2604383db272aca4e15833f8bd02c
-Bug 870460 - Part 4: Make the lifecycle of cookie thread alongwith the profile. r=ngwh
-
-diff --git a/netwerk/cookie/nsCookieService.cpp b/netwerk/cookie/nsCookieService.cpp
---- a/netwerk/cookie/nsCookieService.cpp
-+++ b/netwerk/cookie/nsCookieService.cpp
-@@ -639,19 +639,16 @@ nsCookieService::Init()
-     prefBranch->AddObserver(kPrefThirdPartyNonsecureSession, this, true);
-     prefBranch->AddObserver(kCookieLeaveSecurityAlone,  this, true);
-     PrefChanged(prefBranch);
-   }
- 
-   mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
-   NS_ENSURE_SUCCESS(rv, rv);
- 
--  rv = NS_NewNamedThread("Cookie", getter_AddRefs(mThread));
--  NS_ENSURE_SUCCESS(rv, rv);
--
-   // Init our default, and possibly private DBStates.
-   InitDBStates();
- 
-   RegisterWeakMemoryReporter(this);
- 
-   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-   NS_ENSURE_STATE(os);
-   os->AddObserver(this, "profile-before-change", true);
-@@ -669,16 +666,17 @@ nsCookieService::Init()
- 
- void
- nsCookieService::InitDBStates()
- {
-   NS_ASSERTION(!mDBState, "already have a DBState");
-   NS_ASSERTION(!mDefaultDBState, "already have a default DBState");
-   NS_ASSERTION(!mPrivateDBState, "already have a private DBState");
-   NS_ASSERTION(!mInitializedDBStates, "already initialized");
-+  NS_ASSERTION(!mThread, "already have a cookie thread");
- 
-   // Create a new default DBState and set our current one.
-   mDefaultDBState = new DBState();
-   mDBState = mDefaultDBState;
- 
-   mPrivateDBState = new DBState();
- 
-   // Get our cookie file.
-@@ -690,16 +688,18 @@ nsCookieService::InitDBStates()
-       ("InitDBStates(): couldn't get cookie file"));
- 
-     mInitializedDBConn = true;
-     mInitializedDBStates = true;
-     return;
-   }
-   mDefaultDBState->cookieFile->AppendNative(NS_LITERAL_CSTRING(COOKIES_FILE));
- 
-+  NS_ENSURE_SUCCESS_VOID(NS_NewNamedThread("Cookie", getter_AddRefs(mThread)));
-+
-   nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction("InitDBStates.TryInitDB", [] {
-     NS_ENSURE_TRUE_VOID(gCookieService &&
-                         gCookieService->mDBState &&
-                         gCookieService->mDefaultDBState);
- 
-     MonitorAutoLock lock(gCookieService->mMonitor);
- 
-     // Attempt to open and read the database. If TryInitDB() returns RESULT_RETRY,
-@@ -1698,17 +1698,20 @@ nsCookieService::CreateTableForSchemaVer
- void
- nsCookieService::CloseDBStates()
- {
-   // return if we already closed
-   if (!mDBState) {
-     return;
-   }
- 
--  EnsureReadComplete(false);
-+  if (mThread) {
-+    mThread->Shutdown();
-+    mThread = nullptr;
-+  }
- 
-   // Null out our private and pointer DBStates regardless.
-   mPrivateDBState = nullptr;
-   mDBState = nullptr;
- 
-   // If we don't have a default DBState, we're done.
-   if (!mDefaultDBState)
-     return;
-@@ -1957,19 +1960,16 @@ nsCookieService::RebuildCorruptDB(DBStat
- 
- nsCookieService::~nsCookieService()
- {
-   CloseDBStates();
- 
-   UnregisterWeakMemoryReporter(this);
- 
-   gCookieService = nullptr;
--  if (mThread) {
--    mThread->Shutdown();
--  }
- }
- 
- NS_IMETHODIMP
- nsCookieService::Observe(nsISupports     *aSubject,
-                          const char      *aTopic,
-                          const char16_t *aData)
- {
-   // check the topic

+ 212 - 169
frg/work-js/mozilla-release/patches/series

@@ -680,6 +680,7 @@ servo-19868-60a1.patch
 1431024-60a1.patch
 1431024-60a1.patch
 1433100-60a1.patch
 1433100-60a1.patch
 1428698-60a1.patch
 1428698-60a1.patch
+1431217-60a1.patch
 1432923-60a1.patch
 1432923-60a1.patch
 1432789-1-60a1.patch
 1432789-1-60a1.patch
 1432789-2-60a1.patch
 1432789-2-60a1.patch
@@ -811,11 +812,6 @@ servo-19902-60a1.patch
 1426146-2no3-60a1.patch
 1426146-2no3-60a1.patch
 1432794-60a1.patch
 1432794-60a1.patch
 1431533-5aa-60a1.patch
 1431533-5aa-60a1.patch
-1320353-60a1.patch
-1425583-1-60a1.patch
-1425583-2-60a1.patch
-1425583-3-60a1.patch
-1434267-60a1.patch
 1356331-1-60a1.patch
 1356331-1-60a1.patch
 1356331-2-60a1.patch
 1356331-2-60a1.patch
 1422393-60a1.patch
 1422393-60a1.patch
@@ -886,6 +882,10 @@ servo-19902-60a1.patch
 1426718-60a1.patch
 1426718-60a1.patch
 1431698-1-60a1.patch
 1431698-1-60a1.patch
 1431698-2-60a1.patch
 1431698-2-60a1.patch
+1320353-60a1.patch
+1425583-1-60a1.patch
+1425583-2-60a1.patch
+1425583-3-60a1.patch
 1434628-60a1.patch
 1434628-60a1.patch
 1433579-60a1.patch
 1433579-60a1.patch
 servo-19465-60a1.patch
 servo-19465-60a1.patch
@@ -907,17 +907,17 @@ servo-19915-60a1.patch
 1434217-3-60a1.patch
 1434217-3-60a1.patch
 1269527-60a1.patch
 1269527-60a1.patch
 1434279-60a1.patch
 1434279-60a1.patch
-1434318-01-60a1.patch
-1434318-02-60a1.patch
-1434169-1-60a1.patch
-1434169-2-60a1.patch
+1434267-60a1.patch
 1433496-60a1.patch
 1433496-60a1.patch
 1434546-60a1.patch
 1434546-60a1.patch
 1433432-60a1.patch
 1433432-60a1.patch
 1432168-1-60a1.patch
 1432168-1-60a1.patch
 1432168-2-60a1.patch
 1432168-2-60a1.patch
 1432168-3-60a1.patch
 1432168-3-60a1.patch
-1434224-60a1.patch
+1434318-01-60a1.patch
+1434318-02-60a1.patch
+1434169-1-60a1.patch
+1434169-2-60a1.patch
 1420026-5no4-60a1.patch
 1420026-5no4-60a1.patch
 1430014-1-60a1.patch
 1430014-1-60a1.patch
 1430014-2-60a1.patch
 1430014-2-60a1.patch
@@ -962,8 +962,9 @@ servo-19918-60a1.patch
 1386404-6-60a1.patch
 1386404-6-60a1.patch
 1386404-7-60a1.patch
 1386404-7-60a1.patch
 1417388-60a1.patch
 1417388-60a1.patch
-1434835-60a1.patch
 1434622-60a1.patch
 1434622-60a1.patch
+1434224-60a1.patch
+1434835-60a1.patch
 1434790-60a1.patch
 1434790-60a1.patch
 1433837-5-60a1.patch
 1433837-5-60a1.patch
 1433837-6-60a1.patch
 1433837-6-60a1.patch
@@ -1015,13 +1016,13 @@ servo-19927-60a1.patch
 1434789-1-60a1.patch
 1434789-1-60a1.patch
 1434789-2-60a1.patch
 1434789-2-60a1.patch
 1435187-60a1.patch
 1435187-60a1.patch
-1435262-60a1.patch
-1435015-60a1.patch
 1435228-60a1.patch
 1435228-60a1.patch
 1434689-2-60a1.patch
 1434689-2-60a1.patch
 1434689-3-60a1.patch
 1434689-3-60a1.patch
 1434689-4-60a1.patch
 1434689-4-60a1.patch
 1435219-60a1.patch
 1435219-60a1.patch
+1435262-60a1.patch
+1435015-60a1.patch
 1435200-60a1.patch
 1435200-60a1.patch
 servo-19935-60a1.patch
 servo-19935-60a1.patch
 servo-19938-60a1.patch
 servo-19938-60a1.patch
@@ -1057,6 +1058,10 @@ servo-19955-60a1.patch
 1435149-1-60a1.patch
 1435149-1-60a1.patch
 1435149-2-60a1.patch
 1435149-2-60a1.patch
 1432599-1-60a1.patch
 1432599-1-60a1.patch
+1431561-60a1.patch
+NOBUG-20180206-jsgdb-60a1.patch
+1435220-60a1.patch
+1435293-60a1.patch
 1435673-1-60a1.patch
 1435673-1-60a1.patch
 1435673-2-60a1.patch
 1435673-2-60a1.patch
 1435673-3-60a1.patch
 1435673-3-60a1.patch
@@ -1064,22 +1069,6 @@ servo-19955-60a1.patch
 1434600-1-60a1.patch
 1434600-1-60a1.patch
 1434600-2-60a1.patch
 1434600-2-60a1.patch
 1434600-3-60a1.patch
 1434600-3-60a1.patch
-1431561-60a1.patch
-NOBUG-20180205-jsgdb-60a1.patch
-1435220-60a1.patch
-1435293-60a1.patch
-1425580-01-60a1.patch
-1435756-60a1.patch
-1435295-60a1.patch
-1435249-60a1.patch
-1425580-02-60a1.patch
-1425580-03-60a1.patch
-1434953-1-60a1.patch
-1434953-2-60a1.patch
-1435570-60a1.patch
-1425580-03a-60a1.patch
-1434979-60a1.patch
-1435001-60a1.patch
 1434723-1-60a1.patch
 1434723-1-60a1.patch
 1434723-2-60a1.patch
 1434723-2-60a1.patch
 1434723-3-60a1.patch
 1434723-3-60a1.patch
@@ -1093,20 +1082,31 @@ servo-19956-60a1.patch
 1435701-60a1.patch
 1435701-60a1.patch
 1422163-1-60a1.patch
 1422163-1-60a1.patch
 1422163-2-60a1.patch
 1422163-2-60a1.patch
-1348959-1-60a1.patch
-1348959-2-60a1.patch
-1348959-3-60a1.patch
-1431217-60a1.patch
-1431179-1-60a1.patch
-1431179-2-60a1.patch
-1431179-3-60a1.patch
 1432381-60a1.patch
 1432381-60a1.patch
 1435325-60a1.patch
 1435325-60a1.patch
 1414825-2-60a1.patch
 1414825-2-60a1.patch
 1432956-2-60a1.patch
 1432956-2-60a1.patch
+1435756-60a1.patch
+1435295-60a1.patch
+1435249-60a1.patch
+1435570-60a1.patch
+1425580-01-60a1.patch
+1425580-02-60a1.patch
+1425580-03-60a1.patch
+1425580-03a-60a1.patch
+1434953-1-60a1.patch
+1434953-2-60a1.patch
+1434979-60a1.patch
+1435001-60a1.patch
 1432992-1a-60a1.patch
 1432992-1a-60a1.patch
 1432992-1b-60a1.patch
 1432992-1b-60a1.patch
 1436134-60a1.patch
 1436134-60a1.patch
+1348959-1-60a1.patch
+1348959-2-60a1.patch
+1348959-3-60a1.patch
+1431179-1-60a1.patch
+1431179-2-60a1.patch
+1431179-3-60a1.patch
 servo-19957-60a1.patch
 servo-19957-60a1.patch
 servo-19964-60a1.patch
 servo-19964-60a1.patch
 servo-19966-60a1.patch
 servo-19966-60a1.patch
@@ -1124,18 +1124,6 @@ servo-19970-60a1.patch
 1435730-1-60a1.patch
 1435730-1-60a1.patch
 1435730-2-60a1.patch
 1435730-2-60a1.patch
 1399158-60a1.patch
 1399158-60a1.patch
-1428072-1-60a1.patch
-1435209-60a1.patch
-1436065-60a1.patch
-1435796-60a1.patch
-1436353-1-60a1.patch
-1436353-2-60a1.patch
-1435525-60a1.patch
-1436353-3-60a1.patch
-1435266-60a1.patch
-1385998-1-60a1.patch
-1385998-2-60a1.patch
-1433959-60a1.patch
 1434861-60a1.patch
 1434861-60a1.patch
 1434946-1-60a1.patch
 1434946-1-60a1.patch
 1434946-2-60a1.patch
 1434946-2-60a1.patch
@@ -1155,9 +1143,18 @@ servo-19973-60a1.patch
 1435939-2-60a1.patch
 1435939-2-60a1.patch
 1435939-3-60a1.patch
 1435939-3-60a1.patch
 1435939-4-60a1.patch
 1435939-4-60a1.patch
+1428072-1-60a1.patch
 1383682-1-60a1.patch
 1383682-1-60a1.patch
 1383682-2-60a1.patch
 1383682-2-60a1.patch
 1383682-3-60a1.patch
 1383682-3-60a1.patch
+1435209-60a1.patch
+1436065-60a1.patch
+1435796-60a1.patch
+1435525-60a1.patch
+1436353-1-60a1.patch
+1436353-2-60a1.patch
+1436353-3-60a1.patch
+1435266-60a1.patch
 1436343-60a1.patch
 1436343-60a1.patch
 1392391-1-60a1.patch
 1392391-1-60a1.patch
 1392391-2-60a1.patch
 1392391-2-60a1.patch
@@ -1169,6 +1166,8 @@ servo-19974-60a1.patch
 servo-19981-60a1.patch
 servo-19981-60a1.patch
 1436279-1-60a1.patch
 1436279-1-60a1.patch
 1436279-2-60a1.patch
 1436279-2-60a1.patch
+1385998-1-60a1.patch
+1385998-2-60a1.patch
 1435143-60a1.patch
 1435143-60a1.patch
 1436265-60a1.patch
 1436265-60a1.patch
 1435624-60a1.patch
 1435624-60a1.patch
@@ -1177,6 +1176,7 @@ servo-19981-60a1.patch
 1432678-60a1.patch
 1432678-60a1.patch
 1432679-60a1.patch
 1432679-60a1.patch
 1431900-60a1.patch
 1431900-60a1.patch
+1433959-60a1.patch
 1435146-60a1.patch
 1435146-60a1.patch
 1436541-60a1.patch
 1436541-60a1.patch
 servo-19984-60a1.patch
 servo-19984-60a1.patch
@@ -1486,7 +1486,6 @@ servo-20049-60a1.patch
 1428453-11-60a1.patch
 1428453-11-60a1.patch
 1428453-12-60a1.patch
 1428453-12-60a1.patch
 1428453-13-60a1.patch
 1428453-13-60a1.patch
-1385998-3-60a1.patch
 1422043-1-60a1
 1422043-1-60a1
 1422043-2-60a1
 1422043-2-60a1
 1422043-3-60a1
 1422043-3-60a1
@@ -1544,6 +1543,7 @@ servo-20056-60a1.patch
 1432853-1-60a1.patch
 1432853-1-60a1.patch
 1432853-2-60a1.patch
 1432853-2-60a1.patch
 1436863-1-60a1.patch
 1436863-1-60a1.patch
+1385998-3-60a1.patch
 servo-20061-60a1.patch
 servo-20061-60a1.patch
 1437572-1-60a1.patch
 1437572-1-60a1.patch
 1437572-2-60a1.patch
 1437572-2-60a1.patch
@@ -3542,7 +3542,6 @@ NOBUG-20180505-lint-61a1.patch
 1456118-62a1.patch
 1456118-62a1.patch
 1454149-62a1.patch
 1454149-62a1.patch
 1460258-62a1.patch
 1460258-62a1.patch
-1460385-62a1.patch
 1459382-1-62a1.patch
 1459382-1-62a1.patch
 1459382-2-62a1.patch
 1459382-2-62a1.patch
 1459382-3-62a1.patch
 1459382-3-62a1.patch
@@ -3577,6 +3576,7 @@ NOBUG-20180508-hashtable-62a1.patch
 1335148-1-62a1.patch
 1335148-1-62a1.patch
 1335148-2no3-62a1.patch
 1335148-2no3-62a1.patch
 1335148-4-62a1.patch
 1335148-4-62a1.patch
+1460385-62a1.patch
 1460373-62a1.patch
 1460373-62a1.patch
 1460367-62a1.patch
 1460367-62a1.patch
 1460407-62a1.patch
 1460407-62a1.patch
@@ -3590,7 +3590,6 @@ NOBUG-20180508-hashtable-62a1.patch
 1459607-62a1.patch
 1459607-62a1.patch
 1434783-62a1.patch
 1434783-62a1.patch
 1459225-2-62a1.patch
 1459225-2-62a1.patch
-1460636-62a1.patch
 1457359-62a1.patch
 1457359-62a1.patch
 1454640-4-62a1.patch
 1454640-4-62a1.patch
 1441914-1-62a1.patch
 1441914-1-62a1.patch
@@ -3599,6 +3598,7 @@ NOBUG-20180508-hashtable-62a1.patch
 1460402-1-62a1.patch
 1460402-1-62a1.patch
 1460402-2-62a1.patch
 1460402-2-62a1.patch
 1460402-3-62a1.patch
 1460402-3-62a1.patch
+1460636-62a1.patch
 1415202-62a1.patch
 1415202-62a1.patch
 1459220-62a1.patch
 1459220-62a1.patch
 1459127-62a1.patch
 1459127-62a1.patch
@@ -3608,6 +3608,7 @@ NOBUG-20180508-hashtable-62a1.patch
 1460748-1-62a1.patch
 1460748-1-62a1.patch
 1460629-62a1.patch
 1460629-62a1.patch
 1452715-62a1.patch
 1452715-62a1.patch
+1449033-62a1.patch
 1460966-62a1.patch
 1460966-62a1.patch
 1454667-62a1.patch
 1454667-62a1.patch
 1432410-62a1.patch
 1432410-62a1.patch
@@ -3622,7 +3623,6 @@ NOBUG-20180508-hashtable-62a1.patch
 1460381-62a1.patch
 1460381-62a1.patch
 1459225-62a1.patch
 1459225-62a1.patch
 1455954-62a1.patch
 1455954-62a1.patch
-1449033-62a1.patch
 1460436-62a1.patch
 1460436-62a1.patch
 1440610-62a1.patch
 1440610-62a1.patch
 1459761-1-62a1.patch
 1459761-1-62a1.patch
@@ -6641,107 +6641,27 @@ NOBUG-removemobilethemes-25318.patch
 1837261-2-version-beta-mr-25318.patch
 1837261-2-version-beta-mr-25318.patch
 1861842-version-release-mr-25318.patch
 1861842-version-release-mr-25318.patch
 1861843-1-version-prebeta-mr-25319.patch
 1861843-1-version-prebeta-mr-25319.patch
-PPPPPPP-check_stdcxx-warn.patch
-PPPPPPP-NOBUG-PLASTER-getrandom.patch
-PPPPPPP-NSSgetentropy.patch
-TOP-1294490-7-PLASTER-webp-2535.patch
-TOP-1493400-6-PLASTER-dav1d-avoid-mColorDepth-2535.patch
-TOP-1445683-14-PLASTER-aom-fix-win32-bustage-2535.patch
-TOP-1683545-PLASTER-webrender-2536.patch
-TOP-1667581-3-PLASTER-2537.patch
-TOP-1469021-PLASTER-2538.patch
-TOP-1699835-PARTIAL-7810.patch
-TOP-1758291-fixgithubpolyfill-253111.patch
-TOP-1398895-2a-57a1.patch
-TOP-NOBUG-skiptests-25312.patch
-TOP-NOBUG-dav1d-V1-support-25312.patch
-TOP-NOBUG-unfiedloadicon-25312.patch
-TOP-NOBUG-nometadata-25312.patch
-TOP-1641640-BACKOUT-25313.patch
-TOP-NOBUG-fixup-VS2022-25313.patch
-TOP-1779027-freebsd-25314.patch
-TOP-1722226-aarch64-webrtc-25315.patch
-TOP-NOBUG-nsslink-25315.patch
-TOP-NOBUG-fixtests-25315.patch
-TOP-NOBUG-nonodedefault-25315.patch
-TOP-1797696-macos11sdk-25315.patch
-TOP-1804537-macostransparent-25315.patch
-TOP-1804537-macosfullscreen-25315.patch
-TOP-1804539-fixlangpack-25316.patch
-TOP-1750671-1only-PARTIAL-98a1.patch
-TOP-1788837-PARTIAL-108a1.patch
-TOP-1807802-shared-tree-styling-25316.patch
-TOP-1584803-rust133.patch
-TOP-1584803-rust135.patch
-TOP-NOBUG-cubeb-25317.patch
-TOP-NOBUG-PLASTER-IOSurface-fix-25317.patch
-TOP-1512450-60.patch
-TOP-NOBUG-PLASTER-wayland-25317.patch
-TOP-NOBUG-PLASTER-Stylo-25314.patch
-TOP-1834230-HTMLTableEditor-tb-td-25317.patch
-TOP-1794292-1-10210.patch
-TOP-1794292-2-10210.patch
-TOP-NOBUG-PLASTER-PY3-Codegen-25317.patch
-TOP-NOBUG-PLASTER-PY3-GenerateCSSPropsGenerated-25317.patch
-TOP-NOBUG-PLASTER-PY3-idl-parser-25317.patch
-TOP-NOBUG-PLASTER-PY3-typelib-25317.patch
-TOP-NOBUG-PLASTER-PY3-check_binary-25317.patch
-TOP-1620143-PARTIAL-PY3-dependentlibs-75a1.patch
-TOP-NOBUG-PLASTER-PY3-25317.patch
-TOP-NOBUG-PLASTER-PY310_support-25314.patch
-TOP-NOBUG-PLASTER-PY311_support-25317.patch
-TOP-NOBUG-enableCE-25318.patch
-TOP-1539694-allsettled-68a1-25313.patch
-TOP-1378808-optchain-63a1-25313.patch
-TOP-1466000-1-optchain-64a1-25313.patch
-TOP-1566143-1to2-optchain-74a1-25313.patch
-TOP-1566143-3-optchain-74a1-25313.patch
-TOP-1610447-optchain-74a1-25313.patch
-TOP-1611777-12-74a1-25313.patch
-TOP-1378808-2-optchain-63a1-25313.patch
-TOP-NOBUG-optchain-baselinejit-25313.patch
-TOP-1629106-1-logicassign-25317.patch
-TOP-1629106-2-logicassign-25317.patch
-TOP-NOBUG-revendor-25318.patch
-TOP-NOBUG-backout1440761-25318.patch
-TOP-NOBUG-test-fixes-25318.patch
-TOP-1846703-binutilsfix-11504.patch
-TOP-1859635-NSS3901-11506.patch
-1333140-2no1-59a1.patch
-1477010-63a1.patch
-1426827-63a1.patch
-1554306-69a1.patch
-1560439-69a1.patch
-1639815-1-78a1.patch
-1639815-2-78a1.patch
-1639815-3-78a1.patch
-1639815-4-78a1.patch
-1639815-5-78a1.patch
-1639815-6-78a1.patch
-1639815-7-78a1.patch
-1639815-8-78a1.patch
-1654470-81a1.patch
-1667896-83a1.patch
-1670130-83a1.patch
-1480005-3-86a1.patch
-1714632-92a1.patch
-1716613-92a1.patch
-1747754-PARTIAL-97a1.patch
-1761303-108a1.patch
-TOP-NOBUG-PLASTER-fix-strip-25319.patch
-TOP-1707096-91a1.patch
 1388954-57a1.patch
 1388954-57a1.patch
+1372927-57a1.patch
 1380512-57a1.patch
 1380512-57a1.patch
+1387088-57a1.patch
 1393900-57a1.patch
 1393900-57a1.patch
 1391704-57a1.patch
 1391704-57a1.patch
+1388331-57a1.patch
 1395990-1no2-57a1.patch
 1395990-1no2-57a1.patch
 1395990-3-57a1.patch
 1395990-3-57a1.patch
+1396888-57a1.patch
+1401242-57a1.patch
 1394804-1-57a1.patch
 1394804-1-57a1.patch
 1394804-2-57a1.patch
 1394804-2-57a1.patch
 1394804-3-57a1.patch
 1394804-3-57a1.patch
+1400846-58a1.patch
 1401187-1-58a1.patch
 1401187-1-58a1.patch
 1401187-2-58a1.patch
 1401187-2-58a1.patch
 1398576-58a1.patch
 1398576-58a1.patch
+1403494-58a1.patch
+1403486-58a1.patch
+1392533-58a1.patch
 1305777-1-58a1.patch
 1305777-1-58a1.patch
 1305777-2-58a1.patch
 1305777-2-58a1.patch
 1305777-3-58a1.patch
 1305777-3-58a1.patch
@@ -6749,6 +6669,7 @@ TOP-1707096-91a1.patch
 1305777-5-58a1.patch
 1305777-5-58a1.patch
 1305777-6-58a1.patch
 1305777-6-58a1.patch
 1305777-7-58a1.patch
 1305777-7-58a1.patch
+1404578-58a1.patch
 1398722-58a1.patch
 1398722-58a1.patch
 1403389-58a1.patch
 1403389-58a1.patch
 1348960-58a1.patch
 1348960-58a1.patch
@@ -6864,6 +6785,7 @@ TOP-1707096-91a1.patch
 1408124-59a1.patch
 1408124-59a1.patch
 1420233-59a1.patch
 1420233-59a1.patch
 1420303-59a1.patch
 1420303-59a1.patch
+1333140-2no1-59a1.patch
 1417039-59a1.patch
 1417039-59a1.patch
 1422258-59a1.patch
 1422258-59a1.patch
 1422265-59a1.patch
 1422265-59a1.patch
@@ -6887,6 +6809,7 @@ TOP-1707096-91a1.patch
 1422218-3-59a1.patch
 1422218-3-59a1.patch
 1422218-4-59a1.patch
 1422218-4-59a1.patch
 1422470-59a1.patch
 1422470-59a1.patch
+1420499-59a1.patch
 1408933-59a1.patch
 1408933-59a1.patch
 1241131-1-59a1.patch
 1241131-1-59a1.patch
 1241131-2-59a1.patch
 1241131-2-59a1.patch
@@ -6944,7 +6867,9 @@ TOP-1707096-91a1.patch
 1419326-59a1.patch
 1419326-59a1.patch
 1420934-59a1.patch
 1420934-59a1.patch
 1406841-59a1.patch
 1406841-59a1.patch
+1429271-59a1.patch
 1430001-59a1.patch
 1430001-59a1.patch
+1420811-59a1.patch
 1429121-1-59a1.patch
 1429121-1-59a1.patch
 1429121-2-59a1.patch
 1429121-2-59a1.patch
 1425273-2-59a1.patch
 1425273-2-59a1.patch
@@ -6969,6 +6894,7 @@ TOP-1707096-91a1.patch
 1430408-2-59a1.patch
 1430408-2-59a1.patch
 1430383-59a1.patch
 1430383-59a1.patch
 1431457-60a1.patch
 1431457-60a1.patch
+1432112-60a1.patch
 1335316-60a1.patch
 1335316-60a1.patch
 1431127-60a1.patch
 1431127-60a1.patch
 1404883-60a1.patch
 1404883-60a1.patch
@@ -6977,6 +6903,7 @@ TOP-1707096-91a1.patch
 1429763-60a1.patch
 1429763-60a1.patch
 1421225-60a1.patch
 1421225-60a1.patch
 1404888-60a1.patch
 1404888-60a1.patch
+1432520-60a1.patch
 1433123-60a1.patch
 1433123-60a1.patch
 1432842-1-60a1.patch
 1432842-1-60a1.patch
 1432842-2-60a1.patch
 1432842-2-60a1.patch
@@ -6986,6 +6913,7 @@ TOP-1707096-91a1.patch
 1432865-60a1.patch
 1432865-60a1.patch
 1420112-60a1.patch
 1420112-60a1.patch
 1412066-60a1.patch
 1412066-60a1.patch
+1428170-60a1.patch
 1424880-60a1.patch
 1424880-60a1.patch
 1421395-60a1.patch
 1421395-60a1.patch
 1432771-60a1.patch
 1432771-60a1.patch
@@ -6999,8 +6927,10 @@ TOP-1707096-91a1.patch
 1434514-60a1.patch
 1434514-60a1.patch
 1426906-60a1.patch
 1426906-60a1.patch
 1403334-1-60a1.patch
 1403334-1-60a1.patch
+1434295-60a1.patch
 1339461-1a-60a1.patch
 1339461-1a-60a1.patch
 1430916-60a1.patch
 1430916-60a1.patch
+1434225-60a1.patch
 1434769-60a1.patch
 1434769-60a1.patch
 1435180-60a1.patch
 1435180-60a1.patch
 1435084-60a1.patch
 1435084-60a1.patch
@@ -7084,6 +7014,7 @@ TOP-1707096-91a1.patch
 1438476-2-60a1.patch
 1438476-2-60a1.patch
 1438476-3-60a1.patch
 1438476-3-60a1.patch
 1404877-60a1.patch
 1404877-60a1.patch
+1437147-60a1.patch
 1393609-60a1.patch
 1393609-60a1.patch
 1438489-60a1.patch
 1438489-60a1.patch
 1439000-60a1.patch
 1439000-60a1.patch
@@ -7138,11 +7069,17 @@ TOP-1707096-91a1.patch
 1429185-1-60a1.patch
 1429185-1-60a1.patch
 1429185-2no3or4-60a1.patch
 1429185-2no3or4-60a1.patch
 1440550-60a1.patch
 1440550-60a1.patch
+1441225-60a1.patch
 1439897-60a1.patch
 1439897-60a1.patch
 1440740-60a1.patch
 1440740-60a1.patch
+1433929-1-60a1.patch
+1433929-2-60a1.patch
+1433929-3no4-60a1.patch
+1433929-5-60a1.patch
 1434333-60a1.patch
 1434333-60a1.patch
 1440320-1-60a1.patch
 1440320-1-60a1.patch
 1440320-2-60a1.patch
 1440320-2-60a1.patch
+1441528-60a1.patch
 1433175-1b-60a1.patch
 1433175-1b-60a1.patch
 1382606-60a1.patch
 1382606-60a1.patch
 1437848-60a1.patch
 1437848-60a1.patch
@@ -7150,6 +7087,7 @@ TOP-1707096-91a1.patch
 1441218-60a1.patch
 1441218-60a1.patch
 1441527-60a1.patch
 1441527-60a1.patch
 1441578-60a1.patch
 1441578-60a1.patch
+1441896-60a1.patch
 1419350-1-60a1.patch
 1419350-1-60a1.patch
 1419350-2-60a1.patch
 1419350-2-60a1.patch
 1437847-60a1.patch
 1437847-60a1.patch
@@ -7162,6 +7100,7 @@ TOP-1707096-91a1.patch
 1441622-60a1.patch
 1441622-60a1.patch
 1382609-60a1.patch
 1382609-60a1.patch
 1233890-60a1.patch
 1233890-60a1.patch
+1382577-60a1.patch
 1437844-60a1.patch
 1437844-60a1.patch
 1442305-60a1.patch
 1442305-60a1.patch
 1248498-60a1.patch
 1248498-60a1.patch
@@ -7176,11 +7115,15 @@ TOP-1707096-91a1.patch
 1307925-60a1.patch
 1307925-60a1.patch
 1442680-60a1.patch
 1442680-60a1.patch
 1443163-60a1.patch
 1443163-60a1.patch
+1382604-60a1.patch
 1437807-60a1.patch
 1437807-60a1.patch
 1401847-60a1.patch
 1401847-60a1.patch
 1442160-60a1.patch
 1442160-60a1.patch
 1421387-60a1.patch
 1421387-60a1.patch
 1443193-60a1.patch
 1443193-60a1.patch
+1442126-60a1.patch
+1443344-60a1.patch
+1443393-60a1.patch
 1154874-60a1.patch
 1154874-60a1.patch
 1382605-60a1.patch
 1382605-60a1.patch
 1443470-1-60a1.patch
 1443470-1-60a1.patch
@@ -7195,6 +7138,7 @@ TOP-1707096-91a1.patch
 1440322-60a1.patch
 1440322-60a1.patch
 1436197-60a1.patch
 1436197-60a1.patch
 1440678-60a1.patch
 1440678-60a1.patch
+1444073-60a1.patch
 1434885-60a1.patch
 1434885-60a1.patch
 1443461-60a1.patch
 1443461-60a1.patch
 1307928-1-60a1.patch
 1307928-1-60a1.patch
@@ -7214,6 +7158,7 @@ TOP-1707096-91a1.patch
 1444106-61a1.patch
 1444106-61a1.patch
 1444414-61a1.patch
 1444414-61a1.patch
 1443831-61a1.patch
 1443831-61a1.patch
+1431050-61a1.patch
 1443081-01-client-aboutdebugging-61a1.patch
 1443081-01-client-aboutdebugging-61a1.patch
 1443081-02-client-animationinspector-61a1.patch
 1443081-02-client-animationinspector-61a1.patch
 1443081-03-client-root-61a1.patch
 1443081-03-client-root-61a1.patch
@@ -7236,6 +7181,27 @@ TOP-1707096-91a1.patch
 1443081-20-server-tests-61a1.patch
 1443081-20-server-tests-61a1.patch
 1443081-21-shared-61a1.patch
 1443081-21-shared-61a1.patch
 1443081-22-startup-61a1.patch
 1443081-22-startup-61a1.patch
+1440321-1a-aboutdebugging-61a1.patch
+1440321-1b-canvasdebugger-61a1.patch
+1440321-1c-commandline-61a1.patch
+1440321-1d-debugger-61a1.patch
+1440321-1e-dom-61a1.patch
+1440321-1f-framework-61a1.patch
+1440321-1g-inspector-61a1.patch
+1440321-1h-jsonview-61a1.patch
+1440321-1i-memory-61a1.patch
+1440321-1j-performance-61a1.patch
+1440321-1k-scratchpad-61a1.patch
+1440321-1l-shadereditor-61a1.patch
+1440321-1m-shared-61a1.patch
+1440321-1n-sourceeditor-61a1.patch
+1440321-1o-storage-61a1.patch
+1440321-1p-styleeditor-61a1.patch
+1440321-1q-webaudioeditor-61a1.patch
+1440321-1r-webconsole-61a1.patch
+1440321-2-61a1.patch
+1440321-3-61a1.patch
+1440321-4-61a1.patch
 NOBUG-20180313-inspector-61a1.patch
 NOBUG-20180313-inspector-61a1.patch
 1441635-1-61a1.patch
 1441635-1-61a1.patch
 1441635-2-61a1.patch
 1441635-2-61a1.patch
@@ -7244,34 +7210,114 @@ NOBUG-20180313-inspector-61a1.patch
 1445081-2-61a1.patch
 1445081-2-61a1.patch
 1445496-61a1.patch
 1445496-61a1.patch
 1444594-61a1.patch
 1444594-61a1.patch
-1451683-62a1.patch
-1434848-68a1.patch
-TOP-NOBUG-killtelemetry-debugger-25319.patch
-1372927-57a1.patch
-1387088-57a1.patch
-1388331-57a1.patch
-1396888-57a1.patch
-1401242-57a1.patch
-1400846-58a1.patch
-1403494-58a1.patch
-1403486-58a1.patch
-1392533-58a1.patch
-1404578-58a1.patch
-1420811-59a1.patch
-1432112-60a1.patch
-1432520-60a1.patch
-1431050-61a1.patch
+1442153-61a1.patch
+1448194-61a1.patch
+1403188-61a1.patch
+1425866-61a1.patch
 1450182-61a1.patch
 1450182-61a1.patch
+1451683-62a1.patch
+1477010-63a1.patch
+1426827-63a1.patch
 1470250-1-63a1.patch
 1470250-1-63a1.patch
 1506798-65a1.patch
 1506798-65a1.patch
+1434848-68a1.patch
+1554306-69a1.patch
+1560439-69a1.patch
+1586165-71a1.patch
+1639815-1-78a1.patch
+1639815-2-78a1.patch
+1639815-3-78a1.patch
+1639815-4-78a1.patch
+1639815-5-78a1.patch
+1639815-6-78a1.patch
+1639815-7-78a1.patch
+1639815-8-78a1.patch
+1654470-81a1.patch
+1667896-83a1.patch
+1670130-83a1.patch
+1480005-3-86a1.patch
+1711872-91a1.patch
+1714632-92a1.patch
+1721149-92a1.patch
+1716613-92a1.patch
+1747754-PARTIAL-97a1.patch
+1761303-108a1.patch
 1854076-11505.patch
 1854076-11505.patch
 1826791-121a1.patch
 1826791-121a1.patch
-WIP-1729459-comment25.patch
 1660223-11507.patch
 1660223-11507.patch
-1586165-71a1.patch
-TOP-NOBUG-PLASTER-25319.patch
-TOP-NOBUG-fixcompile-25319.patch
-
+NOBUG-removessdks-25319.patch
+NOBUG-nukemozlinker-25319.patch
+PPPPPPP-check_stdcxx-warn.patch
+PPPPPPP-NOBUG-PLASTER-getrandom.patch
+PPPPPPP-NSSgetentropy.patch
+WIP-1729459-comment25.patch
+TOP-1294490-7-PLASTER-webp-2535.patch
+TOP-1493400-6-PLASTER-dav1d-avoid-mColorDepth-2535.patch
+TOP-1445683-14-PLASTER-aom-fix-win32-bustage-2535.patch
+TOP-1683545-PLASTER-webrender-2536.patch
+TOP-1667581-3-PLASTER-2537.patch
+TOP-1469021-PLASTER-2538.patch
+TOP-1699835-PARTIAL-7810.patch
+TOP-1758291-fixgithubpolyfill-253111.patch
+TOP-1398895-2a-57a1.patch
+TOP-NOBUG-skiptests-25312.patch
+TOP-NOBUG-dav1d-V1-support-25312.patch
+TOP-NOBUG-unfiedloadicon-25312.patch
+TOP-NOBUG-nometadata-25312.patch
+TOP-1641640-BACKOUT-25313.patch
+TOP-NOBUG-fixup-VS2022-25313.patch
+TOP-1779027-freebsd-25314.patch
+TOP-1722226-aarch64-webrtc-25315.patch
+TOP-NOBUG-nsslink-25315.patch
+TOP-NOBUG-fixtests-25315.patch
+TOP-NOBUG-nonodedefault-25315.patch
+TOP-1797696-macos11sdk-25315.patch
+TOP-1804537-macostransparent-25315.patch
+TOP-1804537-macosfullscreen-25315.patch
+TOP-1804539-fixlangpack-25316.patch
+TOP-1750671-1only-PARTIAL-98a1.patch
+TOP-1788837-PARTIAL-108a1.patch
+TOP-1807802-shared-tree-styling-25316.patch
+TOP-1584803-rust133.patch
+TOP-1584803-rust135.patch
+TOP-NOBUG-cubeb-25317.patch
+TOP-NOBUG-PLASTER-IOSurface-fix-25317.patch
+TOP-1512450-60.patch
+TOP-NOBUG-PLASTER-wayland-25317.patch
+TOP-NOBUG-PLASTER-Stylo-25314.patch
+TOP-1834230-HTMLTableEditor-tb-td-25317.patch
+TOP-1794292-1-10210.patch
+TOP-1794292-2-10210.patch
+TOP-NOBUG-PLASTER-PY3-Codegen-25317.patch
+TOP-NOBUG-PLASTER-PY3-GenerateCSSPropsGenerated-25317.patch
+TOP-NOBUG-PLASTER-PY3-idl-parser-25317.patch
+TOP-NOBUG-PLASTER-PY3-typelib-25317.patch
+TOP-NOBUG-PLASTER-PY3-check_binary-25317.patch
+TOP-1620143-PARTIAL-PY3-dependentlibs-75a1.patch
+TOP-NOBUG-PLASTER-PY3-25317.patch
+TOP-NOBUG-PLASTER-PY310_support-25314.patch
+TOP-NOBUG-PLASTER-PY311_support-25317.patch
+TOP-NOBUG-enableCE-25318.patch
+TOP-1539694-allsettled-68a1-25313.patch
+TOP-1378808-optchain-63a1-25313.patch
+TOP-1466000-1-optchain-64a1-25313.patch
+TOP-1566143-1to2-optchain-74a1-25313.patch
+TOP-1566143-3-optchain-74a1-25313.patch
+TOP-1610447-optchain-74a1-25313.patch
+TOP-1611777-12-74a1-25313.patch
+TOP-1378808-2-optchain-63a1-25313.patch
+TOP-NOBUG-optchain-baselinejit-25313.patch
+TOP-1629106-1-logicassign-25317.patch
+TOP-1629106-2-logicassign-25317.patch
+TOP-NOBUG-revendor-25318.patch
+TOP-NOBUG-backout1440761-25318.patch
+TOP-NOBUG-test-fixes-25318.patch
+TOP-NOBUG-PLASTER-fix-strip-25319.patch
+TOP-1707096-91a1.patch
+TOP-1846703-binutilsfix-11504.patch
+TOP-1859635-NSS3901-11506.patch
+TOP-NOBUG-killtelemetry-debugger-25319.patch
+TOP-1472170-PARTIAL-NOTESTS-63a1.patch
 TOP-NOBUG-REGEXP-01-Import-25318.patch
 TOP-NOBUG-REGEXP-01-Import-25318.patch
 TOP-NOBUG-REGEXP-02-1361856-1-dotall-76a1-25318.patch
 TOP-NOBUG-REGEXP-02-1361856-1-dotall-76a1-25318.patch
 TOP-NOBUG-REGEXP-03-1537978-68a1-25318.patch
 TOP-NOBUG-REGEXP-03-1537978-68a1-25318.patch
@@ -7320,6 +7366,9 @@ TOP-NOBUG-REGEXP-43-1691184-88a1-25318.patch
 TOP-NOBUG-REGEXP-44-irregexp-25318.patch
 TOP-NOBUG-REGEXP-44-irregexp-25318.patch
 TOP-NOBUG-REGEXP-45-final-25318.patch
 TOP-NOBUG-REGEXP-45-final-25318.patch
 TOP-NOBUG-REGEXP-46-fixes-25318.patch
 TOP-NOBUG-REGEXP-46-fixes-25318.patch
+TOP-NOBUG-PLASTER-25319.patch
+TOP-NOBUG-fixcompile-25319.patch
+
 
 
 bumm-compile-error
 bumm-compile-error
 L-1462261-1-62a1.patch
 L-1462261-1-62a1.patch
@@ -7532,12 +7581,6 @@ mozilla-esr78-push_549852.patch
 a.patch
 a.patch
 
 
 
 
-mozilla-central_388196.patch
-mozilla-central_388197.patch
-mozilla-central_388198.patch
-mozilla-central_388199.patch
-L-1415120-1-59a1.patch
-L-1415120-2-59a1.patch
 
 
 L-1498070-64a1.patch
 L-1498070-64a1.patch