Bug 1073247 - UITour: Proxy gContentAPI method calls to the content process via ContentTask and fix tests for e10s. r=felipe draft
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Wed, 27 Jan 2016 02:48:35 -0800
changeset 326046 a662d2e95710ec3d5de3a4638a5c1cec2802d56e
parent 326036 fedba60ee85a23ce41ef23bed83231a4d1fca481
child 513549 6771bbe6549026fea6bf2039cd21bd1b02fdc8ed
push id10092
push usermozilla@noorenberghe.ca
push dateWed, 27 Jan 2016 10:49:23 +0000
reviewersfelipe
bugs1073247
milestone46.0a1
Bug 1073247 - UITour: Proxy gContentAPI method calls to the content process via ContentTask and fix tests for e10s. r=felipe This gets us to 405 / 932 checks passing with e10s compared to non-e10s on my OS X machine.
browser/components/uitour/test/browser.ini
browser/components/uitour/test/browser_UITour.js
browser/components/uitour/test/browser_UITour3.js
browser/components/uitour/test/browser_UITour_annotation_size_attributes.js
browser/components/uitour/test/browser_UITour_availableTargets.js
browser/components/uitour/test/browser_UITour_detach_tab.js
browser/components/uitour/test/browser_UITour_forceReaderMode.js
browser/components/uitour/test/browser_UITour_heartbeat.js
browser/components/uitour/test/browser_UITour_modalDialog.js
browser/components/uitour/test/browser_UITour_registerPageID.js
browser/components/uitour/test/browser_UITour_resetProfile.js
browser/components/uitour/test/browser_UITour_sync.js
browser/components/uitour/test/browser_UITour_toggleReaderMode.js
browser/components/uitour/test/browser_backgroundTab.js
browser/components/uitour/test/browser_closeTab.js
browser/components/uitour/test/browser_openPreferences.js
browser/components/uitour/test/browser_showMenu_controlCenter.js
browser/components/uitour/test/browser_trackingProtection_tour.js
browser/components/uitour/test/head.js
--- a/browser/components/uitour/test/browser.ini
+++ b/browser/components/uitour/test/browser.ini
@@ -1,63 +1,51 @@
 [DEFAULT]
 support-files =
   head.js
   image.png
   uitour.html
   ../UITour-lib.js
 
 [browser_backgroundTab.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
 [browser_closeTab.js]
-skip-if = e10s # Bug 1073247 - UITour tests not e10s friendly
 [browser_fxa.js]
 skip-if = e10s || debug || asan # Bug 1073247 - UITour tests not e10s friendly # updateAppMenuItem leaks
 [browser_no_tabs.js]
 [browser_openPreferences.js]
-skip-if = e10s # Bug 1073247 - UITour tests not e10s friendly
 [browser_openSearchPanel.js]
 skip-if = true # Bug 1113038 - Intermittent "Popup was opened"
 [browser_trackingProtection.js]
 skip-if = os == "linux" # Intermittent NS_ERROR_NOT_AVAILABLE [nsIUrlClassifierDBService.beginUpdate]
 tag = trackingprotection
 [browser_trackingProtection_tour.js]
-skip-if = e10s # Bug 1073247 - UITour tests not e10s friendly
 tag = trackingprotection
 [browser_showMenu_controlCenter.js]
-skip-if = e10s # Bug 1073247 - UITour tests not e10s friendly
 tag = trackingprotection
 [browser_UITour.js]
 skip-if = os == "linux" || e10s # Intermittent failures, bug 951965
 [browser_UITour2.js]
 skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
 [browser_UITour3.js]
-skip-if = os == "linux" || e10s # Linux: Bug 986760, Bug 989101; e10s: Bug 1073247 - UITour.jsm not e10s friendly
+skip-if = os == "linux" # Linux: Bug 986760, Bug 989101
 [browser_UITour_availableTargets.js]
-skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
 [browser_UITour_annotation_size_attributes.js]
-skip-if = e10s # Bug 1073247 - UITour tests not e10s friendly
 [browser_UITour_defaultBrowser.js]
 skip-if = e10s # Bug 1073247 - UITour tests not e10s friendly
 [browser_UITour_detach_tab.js]
 skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
 [browser_UITour_forceReaderMode.js]
-skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly.
 [browser_UITour_heartbeat.js]
 skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly.
 [browser_UITour_loop.js]
 skip-if = true # Bug 1225832 - New Loop architecture is not compatible with test.
 [browser_UITour_modalDialog.js]
 skip-if = os != "mac" || e10s # modal dialog disabling only working on OS X.Bug 1073247 - UITour.jsm not e10s friendly
 [browser_UITour_observe.js]
 skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly.
 [browser_UITour_panel_close_annotation.js]
 skip-if = true # Disabled due to frequent failures, bugs 1026310 and 1032137
 [browser_UITour_pocket.js]
 skip-if = os == "linux" || e10s || debug # Bug 1073247 - UITour.jsm not e10s friendly.
 [browser_UITour_registerPageID.js]
-skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
 [browser_UITour_resetProfile.js]
-skip-if = e10s # Bug 1073247 - UITour tests not e10s friendly
 [browser_UITour_sync.js]
-skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
 [browser_UITour_toggleReaderMode.js]
-skip-if = e10s # Bug 1073247 - UITour.jsm not e10s friendly
--- a/browser/components/uitour/test/browser_UITour.js
+++ b/browser/components/uitour/test/browser_UITour.js
@@ -354,31 +354,33 @@ var tests = [
           Services.obs.removeObserver(observe, "browser-search-engine-modified");
           Services.search.defaultEngine = defaultEngine;
         });
 
         gContentAPI.setDefaultSearchEngine(someOtherEngineID);
       });
     });
   },
-  taskify(function* test_treatment_tag(done) {
+  taskify(function* test_treatment_tag() {
     let ac = new TelemetryArchiveTesting.Checker();
     yield ac.promiseInit();
-    gContentAPI.setTreatmentTag("foobar", "baz");
-    gContentAPI.getTreatmentTag("foobar", (data) => {
-      is(data.value, "baz", "set and retrieved treatmentTag");
-      ac.promiseFindPing("uitour-tag", [
-        [["payload", "tagName"], "foobar"],
-        [["payload", "tagValue"], "baz"],
-      ]).then((found) => {
-        ok(found, "Telemetry ping submitted for setTreatmentTag");
-        done();
-      }, (err) => {
-        ok(false, "Exeption finding uitour telemetry ping: " + err);
-        done();
+    yield gContentAPI.setTreatmentTag("foobar", "baz");
+    yield new Promise((resolve) => {
+      gContentAPI.getTreatmentTag("foobar", (data) => {
+        is(data.value, "baz", "set and retrieved treatmentTag");
+        ac.promiseFindPing("uitour-tag", [
+          [["payload", "tagName"], "foobar"],
+          [["payload", "tagValue"], "baz"],
+        ]).then((found) => {
+          ok(found, "Telemetry ping submitted for setTreatmentTag");
+          resolve();
+        }, (err) => {
+          ok(false, "Exception finding uitour telemetry ping: " + err);
+          resolve();
+        });
       });
     });
   }),
 
   // Make sure this test is last in the file so the appMenu gets left open and done will confirm it got tore down.
   taskify(function* cleanupMenus() {
     let shownPromise = promisePanelShown(window);
     gContentAPI.showMenu("appMenu");
--- a/browser/components/uitour/test/browser_UITour3.js
+++ b/browser/components/uitour/test/browser_UITour3.js
@@ -1,194 +1,182 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
 requestLongerTimeout(2);
 
-function test() {
-  UITourTest();
-}
+add_task(setup_UITourTest);
+
+add_UITour_task(function* test_info_icon() {
+  let popup = document.getElementById("UITourTooltip");
+  let title = document.getElementById("UITourTooltipTitle");
+  let desc = document.getElementById("UITourTooltipDescription");
+  let icon = document.getElementById("UITourTooltipIcon");
+  let buttons = document.getElementById("UITourTooltipButtons");
 
-var tests = [
-  taskify(function* test_info_icon() {
-    let popup = document.getElementById("UITourTooltip");
-    let title = document.getElementById("UITourTooltipTitle");
-    let desc = document.getElementById("UITourTooltipDescription");
-    let icon = document.getElementById("UITourTooltipIcon");
-    let buttons = document.getElementById("UITourTooltipButtons");
+  // Disable the animation to prevent the mouse clicks from hitting the main
+  // window during the transition instead of the buttons in the popup.
+  popup.setAttribute("animate", "false");
 
-    // Disable the animation to prevent the mouse clicks from hitting the main
-    // window during the transition instead of the buttons in the popup.
-    popup.setAttribute("animate", "false");
+  yield showInfoPromise("urlbar", "a title", "some text", "image.png");
 
-    yield showInfoPromise("urlbar", "a title", "some text", "image.png");
+  is(title.textContent, "a title", "Popup should have correct title");
+  is(desc.textContent, "some text", "Popup should have correct description text");
 
-    is(title.textContent, "a title", "Popup should have correct title");
-    is(desc.textContent, "some text", "Popup should have correct description text");
+  let imageURL = getRootDirectory(gTestPath) + "image.png";
+  imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.org/");
+  is(icon.src, imageURL,  "Popup should have correct icon shown");
 
-    let imageURL = getRootDirectory(gTestPath) + "image.png";
-    imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.org/");
-    is(icon.src, imageURL,  "Popup should have correct icon shown");
-
-    is(buttons.hasChildNodes(), false, "Popup should have no buttons");
-  }),
+  is(buttons.hasChildNodes(), false, "Popup should have no buttons");
+}),
 
-  taskify(function* test_info_buttons_1() {
-    let popup = document.getElementById("UITourTooltip");
-    let title = document.getElementById("UITourTooltipTitle");
-    let desc = document.getElementById("UITourTooltipDescription");
-    let icon = document.getElementById("UITourTooltipIcon");
+add_UITour_task(function* test_info_buttons_1() {
+  let popup = document.getElementById("UITourTooltip");
+  let title = document.getElementById("UITourTooltipTitle");
+  let desc = document.getElementById("UITourTooltipDescription");
+  let icon = document.getElementById("UITourTooltipIcon");
 
-    let buttons = gContentWindow.makeButtons();
+  yield showInfoPromise("urlbar", "another title", "moar text", "./image.png", "makeButtons");
 
-    yield showInfoPromise("urlbar", "another title", "moar text", "./image.png", buttons);
+  is(title.textContent, "another title", "Popup should have correct title");
+  is(desc.textContent, "moar text", "Popup should have correct description text");
 
-    is(title.textContent, "another title", "Popup should have correct title");
-    is(desc.textContent, "moar text", "Popup should have correct description text");
+  let imageURL = getRootDirectory(gTestPath) + "image.png";
+  imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.org/");
+  is(icon.src, imageURL,  "Popup should have correct icon shown");
 
-    let imageURL = getRootDirectory(gTestPath) + "image.png";
-    imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.org/");
-    is(icon.src, imageURL,  "Popup should have correct icon shown");
+  let buttons = document.getElementById("UITourTooltipButtons");
+  is(buttons.childElementCount, 4, "Popup should have four buttons");
 
-    buttons = document.getElementById("UITourTooltipButtons");
-    is(buttons.childElementCount, 4, "Popup should have four buttons");
+  is(buttons.childNodes[0].nodeName, "label", "Text label should be a <label>");
+  is(buttons.childNodes[0].getAttribute("value"), "Regular text", "Text label should have correct value");
+  is(buttons.childNodes[0].getAttribute("image"), "", "Text should have no image");
+  is(buttons.childNodes[0].className, "", "Text should have no class");
 
-    is(buttons.childNodes[0].nodeName, "label", "Text label should be a <label>");
-    is(buttons.childNodes[0].getAttribute("value"), "Regular text", "Text label should have correct value");
-    is(buttons.childNodes[0].getAttribute("image"), "", "Text should have no image");
-    is(buttons.childNodes[0].className, "", "Text should have no class");
+  is(buttons.childNodes[1].nodeName, "button", "Link should be a <button>");
+  is(buttons.childNodes[1].getAttribute("label"), "Link", "Link should have correct label");
+  is(buttons.childNodes[1].getAttribute("image"), "", "Link should have no image");
+  is(buttons.childNodes[1].className, "button-link", "Check link class");
 
-    is(buttons.childNodes[1].nodeName, "button", "Link should be a <button>");
-    is(buttons.childNodes[1].getAttribute("label"), "Link", "Link should have correct label");
-    is(buttons.childNodes[1].getAttribute("image"), "", "Link should have no image");
-    is(buttons.childNodes[1].className, "button-link", "Check link class");
+  is(buttons.childNodes[2].nodeName, "button", "Button 1 should be a <button>");
+  is(buttons.childNodes[2].getAttribute("label"), "Button 1", "First button should have correct label");
+  is(buttons.childNodes[2].getAttribute("image"), "", "First button should have no image");
+  is(buttons.childNodes[2].className, "", "Button 1 should have no class");
 
-    is(buttons.childNodes[2].nodeName, "button", "Button 1 should be a <button>");
-    is(buttons.childNodes[2].getAttribute("label"), "Button 1", "First button should have correct label");
-    is(buttons.childNodes[2].getAttribute("image"), "", "First button should have no image");
-    is(buttons.childNodes[2].className, "", "Button 1 should have no class");
+  is(buttons.childNodes[3].nodeName, "button", "Button 2 should be a <button>");
+  is(buttons.childNodes[3].getAttribute("label"), "Button 2", "Second button should have correct label");
+  is(buttons.childNodes[3].getAttribute("image"), imageURL, "Second button should have correct image");
+  is(buttons.childNodes[3].className, "button-primary", "Check button 2 class");
 
-    is(buttons.childNodes[3].nodeName, "button", "Button 2 should be a <button>");
-    is(buttons.childNodes[3].getAttribute("label"), "Button 2", "Second button should have correct label");
-    is(buttons.childNodes[3].getAttribute("image"), imageURL, "Second button should have correct image");
-    is(buttons.childNodes[3].className, "button-primary", "Check button 2 class");
+  let promiseHidden = promisePanelElementHidden(window, popup);
+  EventUtils.synthesizeMouseAtCenter(buttons.childNodes[2], {}, window);
+  yield promiseHidden;
 
-    let promiseHidden = promisePanelElementHidden(window, popup);
-    EventUtils.synthesizeMouseAtCenter(buttons.childNodes[2], {}, window);
-    yield promiseHidden;
+  ok(true, "Popup should close automatically");
 
-    ok(true, "Popup should close automatically");
-
-    yield waitForCallbackResultPromise();
+  let returnValue = yield waitForCallbackResultPromise();
+  is(returnValue.result, "button1", "Correct callback should have been called");
+});
 
-    is(gContentWindow.callbackResult, "button1", "Correct callback should have been called");
-  }),
-  taskify(function* test_info_buttons_2() {
-    let popup = document.getElementById("UITourTooltip");
-    let title = document.getElementById("UITourTooltipTitle");
-    let desc = document.getElementById("UITourTooltipDescription");
-    let icon = document.getElementById("UITourTooltipIcon");
+add_UITour_task(function* test_info_buttons_2() {
+  let popup = document.getElementById("UITourTooltip");
+  let title = document.getElementById("UITourTooltipTitle");
+  let desc = document.getElementById("UITourTooltipDescription");
+  let icon = document.getElementById("UITourTooltipIcon");
+
+  yield showInfoPromise("urlbar", "another title", "moar text", "./image.png", "makeButtons");
 
-    let buttons = gContentWindow.makeButtons();
-
-    yield showInfoPromise("urlbar", "another title", "moar text", "./image.png", buttons);
+  is(title.textContent, "another title", "Popup should have correct title");
+  is(desc.textContent, "moar text", "Popup should have correct description text");
 
-    is(title.textContent, "another title", "Popup should have correct title");
-    is(desc.textContent, "moar text", "Popup should have correct description text");
+  let imageURL = getRootDirectory(gTestPath) + "image.png";
+  imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.org/");
+  is(icon.src, imageURL,  "Popup should have correct icon shown");
 
-    let imageURL = getRootDirectory(gTestPath) + "image.png";
-    imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.org/");
-    is(icon.src, imageURL,  "Popup should have correct icon shown");
+  let buttons = document.getElementById("UITourTooltipButtons");
+  is(buttons.childElementCount, 4, "Popup should have four buttons");
 
-    buttons = document.getElementById("UITourTooltipButtons");
-    is(buttons.childElementCount, 4, "Popup should have four buttons");
+  is(buttons.childNodes[1].getAttribute("label"), "Link", "Link should have correct label");
+  is(buttons.childNodes[1].getAttribute("image"), "", "Link should have no image");
+  ok(buttons.childNodes[1].classList.contains("button-link"), "Link should have button-link class");
 
-    is(buttons.childNodes[1].getAttribute("label"), "Link", "Link should have correct label");
-    is(buttons.childNodes[1].getAttribute("image"), "", "Link should have no image");
-    ok(buttons.childNodes[1].classList.contains("button-link"), "Link should have button-link class");
-
-    is(buttons.childNodes[2].getAttribute("label"), "Button 1", "First button should have correct label");
-    is(buttons.childNodes[2].getAttribute("image"), "", "First button should have no image");
+  is(buttons.childNodes[2].getAttribute("label"), "Button 1", "First button should have correct label");
+  is(buttons.childNodes[2].getAttribute("image"), "", "First button should have no image");
 
-    is(buttons.childNodes[3].getAttribute("label"), "Button 2", "Second button should have correct label");
-    is(buttons.childNodes[3].getAttribute("image"), imageURL, "Second button should have correct image");
+  is(buttons.childNodes[3].getAttribute("label"), "Button 2", "Second button should have correct label");
+  is(buttons.childNodes[3].getAttribute("image"), imageURL, "Second button should have correct image");
 
-    let promiseHidden = promisePanelElementHidden(window, popup);
-    EventUtils.synthesizeMouseAtCenter(buttons.childNodes[3], {}, window);
-    yield promiseHidden;
+  let promiseHidden = promisePanelElementHidden(window, popup);
+  EventUtils.synthesizeMouseAtCenter(buttons.childNodes[3], {}, window);
+  yield promiseHidden;
 
-    ok(true, "Popup should close automatically");
+  ok(true, "Popup should close automatically");
 
-    yield waitForCallbackResultPromise();
+  let returnValue = yield waitForCallbackResultPromise();
 
-    is(gContentWindow.callbackResult, "button2", "Correct callback should have been called");
-  }),
+  is(returnValue.result, "button2", "Correct callback should have been called");
+}),
 
-  taskify(function* test_info_close_button() {
-    let popup = document.getElementById("UITourTooltip");
-    let closeButton = document.getElementById("UITourTooltipClose");
-    let infoOptions = gContentWindow.makeInfoOptions();
+add_UITour_task(function* test_info_close_button() {
+  let popup = document.getElementById("UITourTooltip");
+  let closeButton = document.getElementById("UITourTooltipClose");
+
+  yield showInfoPromise("urlbar", "Close me", "X marks the spot", null, null, "makeInfoOptions");
 
-    yield showInfoPromise("urlbar", "Close me", "X marks the spot", null, null, infoOptions);
+  EventUtils.synthesizeMouseAtCenter(closeButton, {}, window);
 
-    EventUtils.synthesizeMouseAtCenter(closeButton, {}, window);
+  let returnValue = yield waitForCallbackResultPromise();
 
-    yield waitForCallbackResultPromise();
+  is(returnValue.result, "closeButton", "Close button callback called");
+}),
 
-    is(gContentWindow.callbackResult, "closeButton", "Close button callback called");
-  }),
+add_UITour_task(function* test_info_target_callback() {
+  let popup = document.getElementById("UITourTooltip");
 
-  taskify(function* test_info_target_callback() {
-    let popup = document.getElementById("UITourTooltip");
-    let infoOptions = gContentWindow.makeInfoOptions();
+  yield showInfoPromise("appMenu", "I want to know when the target is clicked", "*click*", null, null, "makeInfoOptions");
+
+  yield PanelUI.show();
 
-    yield showInfoPromise("appMenu", "I want to know when the target is clicked", "*click*", null, null, infoOptions);
+  let returnValue = yield waitForCallbackResultPromise();
 
-    yield PanelUI.show();
-
-    yield waitForCallbackResultPromise();
+  is(returnValue.result, "target", "target callback called");
+  is(returnValue.data.target, "appMenu", "target callback was from the appMenu");
+  is(returnValue.data.type, "popupshown", "target callback was from the mousedown");
 
-    is(gContentWindow.callbackResult, "target", "target callback called");
-    is(gContentWindow.callbackData.target, "appMenu", "target callback was from the appMenu");
-    is(gContentWindow.callbackData.type, "popupshown", "target callback was from the mousedown");
+  // Cleanup.
+  yield hideInfoPromise();
 
-    // Cleanup.
-    yield hideInfoPromise();
+  popup.removeAttribute("animate");
+}),
 
-    popup.removeAttribute("animate");
-  }),
-
-  function test_getConfiguration_selectedSearchEngine(done) {
-    Services.search.init(rv => {
+add_UITour_task(function* test_getConfiguration_selectedSearchEngine() {
+  yield new Promise((resolve) => {
+    Services.search.init(Task.async(function*(rv) {
       ok(Components.isSuccessCode(rv), "Search service initialized");
       let engine = Services.search.defaultEngine;
-      gContentAPI.getConfiguration("selectedSearchEngine", (data) => {
-        is(data.searchEngineIdentifier, engine.identifier, "Correct engine identifier");
-        done();
-      });
-    });
-  },
+      let data = yield getConfigurationPromise("selectedSearchEngine");
+      is(data.searchEngineIdentifier, engine.identifier, "Correct engine identifier");
+      resolve();
+    }));
+  });
+});
 
-  function test_setSearchTerm(done) {
-    const TERM = "UITour Search Term";
-    gContentAPI.setSearchTerm(TERM);
+add_UITour_task(function* test_setSearchTerm() {
+  const TERM = "UITour Search Term";
+  yield gContentAPI.setSearchTerm(TERM);
 
-    let searchbar = document.getElementById("searchbar");
-    // The UITour gets to the searchbar element through a promise, so the value setting
-    // only happens after a tick.
-    waitForCondition(() => searchbar.value == TERM, done, "Correct term set");
-  },
+  let searchbar = document.getElementById("searchbar");
+  // The UITour gets to the searchbar element through a promise, so the value setting
+  // only happens after a tick.
+  yield waitForConditionPromise(() => searchbar.value == TERM, "Correct term set");
+});
 
-  function test_clearSearchTerm(done) {
-    gContentAPI.setSearchTerm("");
+add_UITour_task(function* test_clearSearchTerm() {
+  yield gContentAPI.setSearchTerm("");
 
-    let searchbar = document.getElementById("searchbar");
-    // The UITour gets to the searchbar element through a promise, so the value setting
-    // only happens after a tick.
-    waitForCondition(() => searchbar.value == "", done, "Search term cleared");
-  },
-];
+  let searchbar = document.getElementById("searchbar");
+  // The UITour gets to the searchbar element through a promise, so the value setting
+  // only happens after a tick.
+  yield waitForConditionPromise(() => searchbar.value == "", "Search term cleared");
+});
--- a/browser/components/uitour/test/browser_UITour_annotation_size_attributes.js
+++ b/browser/components/uitour/test/browser_UITour_annotation_size_attributes.js
@@ -1,49 +1,42 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
+/*
  * Test that width and height attributes don't get set by widget code on the highlight panel.
  */
 
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 var highlight = document.getElementById("UITourHighlightContainer");
 var tooltip = document.getElementById("UITourTooltip");
 
-function test() {
-  UITourTest();
-}
+add_task(setup_UITourTest);
 
-var tests = [
-  function test_highlight_size_attributes(done) {
-    gContentAPI.showHighlight("appMenu");
-    waitForElementToBeVisible(highlight, function moveTheHighlight() {
-      gContentAPI.showHighlight("urlbar");
-      waitForElementToBeVisible(highlight, function checkPanelAttributes() {
-        SimpleTest.executeSoon(() => {
-          is(highlight.height, "", "Highlight panel should have no explicit height set");
-          is(highlight.width, "", "Highlight panel should have no explicit width set");
-          done();
-        });
-      }, "Highlight should be moved to the urlbar");
-    }, "Highlight should be shown after showHighlight() for the appMenu");
-  },
+add_UITour_task(function* test_highlight_size_attributes() {
+  yield gContentAPI.showHighlight("appMenu");
+  yield elementVisiblePromise(highlight,
+                              "Highlight should be shown after showHighlight() for the appMenu");
+  yield gContentAPI.showHighlight("urlbar");
+  yield elementVisiblePromise(highlight, "Highlight should be moved to the urlbar");
+  yield new Promise((resolve) => {
+    SimpleTest.executeSoon(() => {
+      is(highlight.height, "", "Highlight panel should have no explicit height set");
+      is(highlight.width, "", "Highlight panel should have no explicit width set");
+      resolve();
+    });
+  });
+});
 
-  function test_info_size_attributes(done) {
-    gContentAPI.showInfo("appMenu", "test title", "test text");
-    waitForElementToBeVisible(tooltip, function moveTheTooltip() {
-      gContentAPI.showInfo("urlbar", "new title", "new text");
-      waitForElementToBeVisible(tooltip, function checkPanelAttributes() {
-        SimpleTest.executeSoon(() => {
-          is(tooltip.height, "", "Info panel should have no explicit height set");
-          is(tooltip.width, "", "Info panel should have no explicit width set");
-          done();
-        });
-      }, "Tooltip should be moved to the urlbar");
-    }, "Tooltip should be shown after showInfo() for the appMenu");
-  },
-
-];
+add_UITour_task(function* test_info_size_attributes() {
+  yield gContentAPI.showInfo("appMenu", "test title", "test text");
+  yield elementVisiblePromise(tooltip, "Tooltip should be shown after showInfo() for the appMenu");
+  yield gContentAPI.showInfo("urlbar", "new title", "new text");
+  yield elementVisiblePromise(tooltip, "Tooltip should be moved to the urlbar");
+  yield new Promise((resolve) => {
+    SimpleTest.executeSoon(() => {
+      is(tooltip.height, "", "Info panel should have no explicit height set");
+      is(tooltip.width, "", "Info panel should have no explicit width set");
+      resolve();
+    });
+  });
+});
--- a/browser/components/uitour/test/browser_UITour_availableTargets.js
+++ b/browser/components/uitour/test/browser_UITour_availableTargets.js
@@ -1,123 +1,109 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
 var hasWebIDE = Services.prefs.getBoolPref("devtools.webide.widget.enabled");
-
 var hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled");
 
-function test() {
-  requestLongerTimeout(2);
-  UITourTest();
-}
+requestLongerTimeout(2);
+add_task(setup_UITourTest);
 
-var tests = [
-  function test_availableTargets(done) {
-    gContentAPI.getConfiguration("availableTargets", (data) => {
-      ok_targets(data, [
-        "accountStatus",
-        "addons",
-        "appMenu",
-        "backForward",
-        "bookmarks",
-        "customize",
-        "help",
-        "home",
-        "loop",
-        "devtools",
-        ...(hasPocket ? ["pocket"] : []),
-        "privateWindow",
-        "quit",
-        "readerMode-urlBar",
-        "search",
-        "searchIcon",
-        "trackingProtection",
-        "urlbar",
-        ...(hasWebIDE ? ["webide"] : [])
-      ]);
+add_UITour_task(function* test_availableTargets() {
+  let data = yield getConfigurationPromise("availableTargets");
+  ok_targets(data, [
+    "accountStatus",
+    "addons",
+    "appMenu",
+    "backForward",
+    "bookmarks",
+    "customize",
+    "help",
+    "home",
+    "loop",
+    "devtools",
+      ...(hasPocket ? ["pocket"] : []),
+    "privateWindow",
+    "quit",
+    "readerMode-urlBar",
+    "search",
+    "searchIcon",
+    "trackingProtection",
+    "urlbar",
+      ...(hasWebIDE ? ["webide"] : [])
+  ]);
 
-      ok(UITour.availableTargetsCache.has(window),
-         "Targets should now be cached");
-      done();
-    });
-  },
+  ok(UITour.availableTargetsCache.has(window),
+     "Targets should now be cached");
+});
 
-  function test_availableTargets_changeWidgets(done) {
-    CustomizableUI.removeWidgetFromArea("bookmarks-menu-button");
-    ok(!UITour.availableTargetsCache.has(window),
-       "Targets should be evicted from cache after widget change");
-    gContentAPI.getConfiguration("availableTargets", (data) => {
-      ok_targets(data, [
-        "accountStatus",
-        "addons",
-        "appMenu",
-        "backForward",
-        "customize",
-        "help",
-        "loop",
-        "devtools",
-        "home",
-        ...(hasPocket ? ["pocket"] : []),
-        "privateWindow",
-        "quit",
-        "readerMode-urlBar",
-        "search",
-        "searchIcon",
-        "trackingProtection",
-        "urlbar",
-        ...(hasWebIDE ? ["webide"] : [])
-      ]);
+add_UITour_task(function* test_availableTargets_changeWidgets() {
+  CustomizableUI.removeWidgetFromArea("bookmarks-menu-button");
+  ok(!UITour.availableTargetsCache.has(window),
+     "Targets should be evicted from cache after widget change");
+  let data = yield getConfigurationPromise("availableTargets");
+  ok_targets(data, [
+    "accountStatus",
+    "addons",
+    "appMenu",
+    "backForward",
+    "customize",
+    "help",
+    "loop",
+    "devtools",
+    "home",
+      ...(hasPocket ? ["pocket"] : []),
+    "privateWindow",
+    "quit",
+    "readerMode-urlBar",
+    "search",
+    "searchIcon",
+    "trackingProtection",
+    "urlbar",
+      ...(hasWebIDE ? ["webide"] : [])
+  ]);
 
-      ok(UITour.availableTargetsCache.has(window),
-         "Targets should now be cached again");
-      CustomizableUI.reset();
-      ok(!UITour.availableTargetsCache.has(window),
-         "Targets should not be cached after reset");
-      done();
-    });
-  },
+  ok(UITour.availableTargetsCache.has(window),
+     "Targets should now be cached again");
+  CustomizableUI.reset();
+  ok(!UITour.availableTargetsCache.has(window),
+     "Targets should not be cached after reset");
+});
 
-  function test_availableTargets_exceptionFromGetTarget(done) {
-    // The query function for the "search" target will throw if it's not found.
-    // Make sure the callback still fires with the other available targets.
-    CustomizableUI.removeWidgetFromArea("search-container");
-    gContentAPI.getConfiguration("availableTargets", (data) => {
-      // Default minus "search" and "searchIcon"
-      ok_targets(data, [
-        "accountStatus",
-        "addons",
-        "appMenu",
-        "backForward",
-        "bookmarks",
-        "customize",
-        "help",
-        "home",
-        "loop",
-        "devtools",
-        ...(hasPocket ? ["pocket"] : []),
-        "privateWindow",
-        "quit",
-        "readerMode-urlBar",
-        "trackingProtection",
-        "urlbar",
-        ...(hasWebIDE ? ["webide"] : [])
-      ]);
+add_UITour_task(function test_availableTargets_exceptionFromGetTarget() {
+  // The query function for the "search" target will throw if it's not found.
+  // Make sure the callback still fires with the other available targets.
+  CustomizableUI.removeWidgetFromArea("search-container");
+  let data = yield getConfigurationPromise("availableTargets");
+  // Default minus "search" and "searchIcon"
+  ok_targets(data, [
+    "accountStatus",
+    "addons",
+    "appMenu",
+    "backForward",
+    "bookmarks",
+    "customize",
+    "help",
+    "home",
+    "loop",
+    "devtools",
+      ...(hasPocket ? ["pocket"] : []),
+    "privateWindow",
+    "quit",
+    "readerMode-urlBar",
+    "trackingProtection",
+    "urlbar",
+      ...(hasWebIDE ? ["webide"] : [])
+  ]);
 
-      CustomizableUI.reset();
-      done();
-    });
-  },
-];
+  CustomizableUI.reset();
+});
 
 function ok_targets(actualData, expectedTargets) {
   // Depending on how soon after page load this is called, the selected tab icon
   // may or may not be showing the loading throbber.  Check for its presence and
   // insert it into expectedTargets if it's visible.
   let selectedTabIcon =
     document.getAnonymousElementByAttribute(gBrowser.selectedTab,
                                             "anonid",
--- a/browser/components/uitour/test/browser_UITour_detach_tab.js
+++ b/browser/components/uitour/test/browser_UITour_detach_tab.js
@@ -20,17 +20,17 @@ function test() {
 }
 
 /**
  * When tab is changed we're tearing the tour down. So the UITour client has to always be aware of this
  * fact and therefore listens to visibilitychange events.
  * In particular this scenario happens for detaching the tab (ie. moving it to a new window).
  */
 var tests = [
-  taskify(function* test_move_tab_to_new_window(done) {
+  taskify(function* test_move_tab_to_new_window() {
     let onVisibilityChange = (aEvent) => {
       if (!document.hidden && window != UITour.getChromeWindow(aEvent.target)) {
         gContentAPI.showHighlight("appMenu");
       }
     };
 
     let highlight = document.getElementById("UITourHighlight");
     let windowDestroyedDeferred = Promise.defer();
--- a/browser/components/uitour/test/browser_UITour_forceReaderMode.js
+++ b/browser/components/uitour/test/browser_UITour_forceReaderMode.js
@@ -1,21 +1,17 @@
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
-function test() {
-  UITourTest();
-}
+add_task(setup_UITourTest);
 
-var tests = [
-  taskify(function*() {
-    ok(!gBrowser.selectedBrowser.isArticle, "Should not be an article when we start");
-    ok(document.getElementById("reader-mode-button").hidden, "Button should be hidden.");
-    gContentAPI.forceShowReaderIcon();
-    yield waitForConditionPromise(() => gBrowser.selectedBrowser.isArticle);
-    ok(gBrowser.selectedBrowser.isArticle, "Should suddenly be an article.");
-    ok(!document.getElementById("reader-mode-button").hidden, "Button should now be visible.");
-  })
-];
+add_UITour_task(function*() {
+  ok(!gBrowser.selectedBrowser.isArticle, "Should not be an article when we start");
+  ok(document.getElementById("reader-mode-button").hidden, "Button should be hidden.");
+  yield gContentAPI.forceShowReaderIcon();
+  yield waitForConditionPromise(() => gBrowser.selectedBrowser.isArticle);
+  ok(gBrowser.selectedBrowser.isArticle, "Should suddenly be an article.");
+  ok(!document.getElementById("reader-mode-button").hidden, "Button should now be visible.");
+});
 
--- a/browser/components/uitour/test/browser_UITour_heartbeat.js
+++ b/browser/components/uitour/test/browser_UITour_heartbeat.js
@@ -340,51 +340,51 @@ var tests = [
           ok(false, "Unexpected notification received: " + aEventName);
       }
     });
 
     gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, dummyURL,
                               "What is this?", dummyURL);
   },
 
-  taskify(function* test_invalidEngagementButtonLabel(done) {
+  taskify(function* test_invalidEngagementButtonLabel() {
     let engagementURL = "http://example.com";
     let flowId = "invalidEngagementButtonLabel-" + Math.random();
 
     let eventPromise = promisePageEvent();
 
     gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL,
                               null, null, {
                                 engagementButtonLabel: 42,
                               });
 
     yield eventPromise;
     ok(!isTourBrowser(gBrowser.selectedBrowser),
        "Invalid engagementButtonLabel should prevent init");
 
   }),
 
-  taskify(function* test_privateWindowsOnly_noneOpen(done) {
+  taskify(function* test_privateWindowsOnly_noneOpen() {
     let engagementURL = "http://example.com";
     let flowId = "privateWindowsOnly_noneOpen-" + Math.random();
 
     let eventPromise = promisePageEvent();
 
     gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL,
                               null, null, {
                                 engagementButtonLabel: "Yes!",
                                 privateWindowsOnly: true,
                               });
 
     yield eventPromise;
     ok(!isTourBrowser(gBrowser.selectedBrowser),
        "If there are no private windows opened, tour init should be prevented");
   }),
 
-  taskify(function* test_privateWindowsOnly_notMostRecent(done) {
+  taskify(function* test_privateWindowsOnly_notMostRecent() {
     let engagementURL = "http://example.com";
     let flowId = "notMostRecent-" + Math.random();
 
     let privateWin = yield BrowserTestUtils.openNewBrowserWindow({ private: true });
     let mostRecentWin = yield BrowserTestUtils.openNewBrowserWindow();
 
     let eventPromise = promisePageEvent();
 
--- a/browser/components/uitour/test/browser_UITour_modalDialog.js
+++ b/browser/components/uitour/test/browser_UITour_modalDialog.js
@@ -75,17 +75,17 @@ function getDialogDoc() {
 }
 
 function test() {
   UITourTest();
 }
 
 
 var tests = [
-  taskify(function* test_modal_dialog_while_opening_tooltip(done) {
+  taskify(function* test_modal_dialog_while_opening_tooltip() {
     let panelShown;
     let popup;
 
     handleDialog = (doc) => {
       popup = document.getElementById("UITourTooltip");
       gContentAPI.showInfo("appMenu", "test title", "test text");
       doc.defaultView.setTimeout(function() {
         is(popup.state, "closed", "Popup shouldn't be shown while dialog is up");
--- a/browser/components/uitour/test/browser_UITour_registerPageID.js
+++ b/browser/components/uitour/test/browser_UITour_registerPageID.js
@@ -1,33 +1,31 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
 Components.utils.import("resource://gre/modules/UITelemetry.jsm");
 Components.utils.import("resource:///modules/BrowserUITelemetry.jsm");
 
-function test() {
+add_task(function* setup_telemetry() {
   UITelemetry._enabled = true;
 
   registerCleanupFunction(function() {
     Services.prefs.clearUserPref("browser.uitour.seenPageIDs");
     resetSeenPageIDsLazyGetter();
     UITelemetry._enabled = undefined;
     BrowserUITelemetry.setBucket(null);
     delete window.UITelemetry;
     delete window.BrowserUITelemetry;
   });
-  UITourTest();
-}
+});
+
+add_task(setup_UITourTest);
 
 function resetSeenPageIDsLazyGetter() {
   delete UITour.seenPageIDs;
   // This should be kept in sync with how UITour.init() sets this.
   Object.defineProperty(UITour, "seenPageIDs", {
     get: UITour.restoreSeenPageIDs.bind(UITour),
     configurable: true,
   });
@@ -43,69 +41,68 @@ function checkExpectedSeenPageIDs(expect
   prefData = new Map(JSON.parse(prefData));
 
   is(prefData.size, expected.length, "Should be " + expected.length + " total seen page IDs persisted");
 
   for (let id of expected)
     ok(prefData.has(id), "Should have seen '" + id + "' page ID persisted");
 }
 
-var tests = [
-  function test_seenPageIDs_restore(done) {
-    info("Setting up seenPageIDs to be restored from pref");
-    let data = JSON.stringify([
-      ["savedID1", { lastSeen: Date.now() }],
-      ["savedID2", { lastSeen: Date.now() }],
-      // 9 weeks ago, should auto expire.
-      ["savedID3", { lastSeen: Date.now() - 9 * 7 * 24 * 60 * 60 * 1000 }],
-    ]);
-    Services.prefs.setCharPref("browser.uitour.seenPageIDs",
-                               data);
 
-    resetSeenPageIDsLazyGetter();
-    checkExpectedSeenPageIDs(["savedID1", "savedID2"]);
+add_UITour_task(function test_seenPageIDs_restore() {
+  info("Setting up seenPageIDs to be restored from pref");
+  let data = JSON.stringify([
+    ["savedID1", { lastSeen: Date.now() }],
+    ["savedID2", { lastSeen: Date.now() }],
+    // 9 weeks ago, should auto expire.
+    ["savedID3", { lastSeen: Date.now() - 9 * 7 * 24 * 60 * 60 * 1000 }],
+  ]);
+  Services.prefs.setCharPref("browser.uitour.seenPageIDs",
+                             data);
 
-    done();
-  },
-  taskify(function* test_seenPageIDs_set_1() {
-    gContentAPI.registerPageID("testpage1");
+  resetSeenPageIDsLazyGetter();
+  checkExpectedSeenPageIDs(["savedID1", "savedID2"]);
+});
 
-    yield waitForConditionPromise(() => UITour.seenPageIDs.size == 3, "Waiting for page to be registered.");
+add_UITour_task(function* test_seenPageIDs_set_1() {
+  yield gContentAPI.registerPageID("testpage1");
 
-    checkExpectedSeenPageIDs(["savedID1", "savedID2", "testpage1"]);
+  yield waitForConditionPromise(() => UITour.seenPageIDs.size == 3, "Waiting for page to be registered.");
+
+  checkExpectedSeenPageIDs(["savedID1", "savedID2", "testpage1"]);
 
-    const PREFIX = BrowserUITelemetry.BUCKET_PREFIX;
-    const SEP = BrowserUITelemetry.BUCKET_SEPARATOR;
+  const PREFIX = BrowserUITelemetry.BUCKET_PREFIX;
+  const SEP = BrowserUITelemetry.BUCKET_SEPARATOR;
 
-    let bucket = PREFIX + "UITour" + SEP + "testpage1";
-    is(BrowserUITelemetry.currentBucket, bucket, "Bucket should have correct name");
+  let bucket = PREFIX + "UITour" + SEP + "testpage1";
+  is(BrowserUITelemetry.currentBucket, bucket, "Bucket should have correct name");
 
-    gBrowser.selectedTab = gBrowser.addTab("about:blank");
-    bucket = PREFIX + "UITour" + SEP + "testpage1" + SEP + "inactive" + SEP + "1m";
-    is(BrowserUITelemetry.currentBucket, bucket,
-       "After switching tabs, bucket should be expiring");
+  gBrowser.selectedTab = gBrowser.addTab("about:blank");
+  bucket = PREFIX + "UITour" + SEP + "testpage1" + SEP + "inactive" + SEP + "1m";
+  is(BrowserUITelemetry.currentBucket, bucket,
+     "After switching tabs, bucket should be expiring");
 
-    gBrowser.removeTab(gBrowser.selectedTab);
-    gBrowser.selectedTab = gTestTab;
-    BrowserUITelemetry.setBucket(null);
-  }),
-  taskify(function* test_seenPageIDs_set_2() {
-    gContentAPI.registerPageID("testpage2");
+  gBrowser.removeTab(gBrowser.selectedTab);
+  gBrowser.selectedTab = gTestTab;
+  BrowserUITelemetry.setBucket(null);
+});
 
-    yield waitForConditionPromise(() => UITour.seenPageIDs.size == 4, "Waiting for page to be registered.");
+add_UITour_task(function* test_seenPageIDs_set_2() {
+  yield gContentAPI.registerPageID("testpage2");
 
-    checkExpectedSeenPageIDs(["savedID1", "savedID2", "testpage1", "testpage2"]);
+  yield waitForConditionPromise(() => UITour.seenPageIDs.size == 4, "Waiting for page to be registered.");
 
-    const PREFIX = BrowserUITelemetry.BUCKET_PREFIX;
-    const SEP = BrowserUITelemetry.BUCKET_SEPARATOR;
+  checkExpectedSeenPageIDs(["savedID1", "savedID2", "testpage1", "testpage2"]);
 
-    let bucket = PREFIX + "UITour" + SEP + "testpage2";
-    is(BrowserUITelemetry.currentBucket, bucket, "Bucket should have correct name");
+  const PREFIX = BrowserUITelemetry.BUCKET_PREFIX;
+  const SEP = BrowserUITelemetry.BUCKET_SEPARATOR;
+
+  let bucket = PREFIX + "UITour" + SEP + "testpage2";
+  is(BrowserUITelemetry.currentBucket, bucket, "Bucket should have correct name");
 
-    gBrowser.removeTab(gTestTab);
-    gTestTab = null;
-    bucket = PREFIX + "UITour" + SEP + "testpage2" + SEP + "closed" + SEP + "1m";
-    is(BrowserUITelemetry.currentBucket, bucket,
-       "After closing tab, bucket should be expiring");
+  gBrowser.removeTab(gTestTab);
+  gTestTab = null;
+  bucket = PREFIX + "UITour" + SEP + "testpage2" + SEP + "closed" + SEP + "1m";
+  is(BrowserUITelemetry.currentBucket, bucket,
+     "After closing tab, bucket should be expiring");
 
-    BrowserUITelemetry.setBucket(null);
-  }),
-];
+  BrowserUITelemetry.setBucket(null);
+});
--- a/browser/components/uitour/test/browser_UITour_resetProfile.js
+++ b/browser/components/uitour/test/browser_UITour_resetProfile.js
@@ -1,37 +1,34 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
-function test() {
-  UITourTest();
-}
+add_task(setup_UITourTest);
 
-var tests = [
-  // Test that a reset profile dialog appears when "resetFirefox" event is triggered
-  function test_resetFirefox(done) {
+// Test that a reset profile dialog appears when "resetFirefox" event is triggered
+add_UITour_task(function* test_resetFirefox() {
+  let dialogPromise = new Promise((resolve) => {
     let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
                      getService(Ci.nsIWindowWatcher);
     winWatcher.registerNotification(function onOpen(subj, topic, data) {
       if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
         subj.addEventListener("load", function onLoad() {
           subj.removeEventListener("load", onLoad);
           if (subj.document.documentURI ==
               "chrome://global/content/resetProfile.xul") {
             winWatcher.unregisterNotification(onOpen);
             ok(true, "Observed search manager window open");
             is(subj.opener, window,
                "Reset Firefox event opened a reset profile window.");
             subj.close();
-            done();
+            resolve();
           }
         });
       }
     });
-    gContentAPI.resetFirefox();
-  },
-];
+  });
+  yield gContentAPI.resetFirefox();
+  yield dialogPromise;
+});
+
--- a/browser/components/uitour/test/browser_UITour_sync.js
+++ b/browser/components/uitour/test/browser_UITour_sync.js
@@ -1,65 +1,29 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
-function test() {
-  registerCleanupFunction(function() {
-    Services.prefs.clearUserPref("services.sync.username");
-  });
-  UITourTest();
-}
+registerCleanupFunction(function() {
+  Services.prefs.clearUserPref("services.sync.username");
+});
 
-var tests = [
-  function test_checkSyncSetup_disabled(done) {
-    function callback(result) {
-      is(result.setup, false, "Sync shouldn't be setup by default");
-      done();
-    }
-
-    gContentAPI.getConfiguration("sync", callback);
-  },
+add_task(setup_UITourTest);
 
-  function test_checkSyncSetup_enabled(done) {
-    function callback(result) {
-      is(result.setup, true, "Sync should be setup");
-      done();
-    }
-
-    Services.prefs.setCharPref("services.sync.username", "uitour@tests.mozilla.org");
-    gContentAPI.getConfiguration("sync", callback);
-  },
+add_UITour_task(function* test_checkSyncSetup_disabled() {
+  let result = yield getConfigurationPromise("sync");
+  is(result.setup, false, "Sync shouldn't be setup by default");
+});
 
-  // The showFirefoxAccounts API is sync related, so we test that here too...
-  function test_firefoxAccounts(done) {
-    // This test will load about:accounts, and we don't want that to hit the
-    // network.
-    Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri",
-                               "https://example.com/");
-
-    loadUITourTestPage(function(contentWindow) {
-      let tabBrowser = gBrowser.selectedBrowser;
-      // This command will replace the current tab - so add a load event
-      // handler which will fire when that happens.
-      tabBrowser.addEventListener("load", function onload(evt) {
-        tabBrowser.removeEventListener("load", onload, true);
+add_UITour_task(function* test_checkSyncSetup_enabled() {
+  Services.prefs.setCharPref("services.sync.username", "uitour@tests.mozilla.org");
+  let result = yield getConfigurationPromise("sync");
+  is(result.setup, true, "Sync should be setup");
+});
 
-        is(tabBrowser.contentDocument.location.href,
-           "about:accounts?action=signup&entrypoint=uitour",
-           "about:accounts should have replaced the tab");
-
-        // the iframe in about:accounts will still be loading, so we stop
-        // that before resetting the pref.
-        tabBrowser.contentDocument.location.href = "about:blank";
-        Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
-        done();
-      }, true);
-
-      gContentAPI.showFirefoxAccounts();
-    });
-  },
-];
+// The showFirefoxAccounts API is sync related, so we test that here too...
+add_UITour_task(function* test_firefoxAccounts() {
+  yield gContentAPI.showFirefoxAccounts();
+  yield BrowserTestUtils.browserLoaded(gTestTab.linkedBrowser, false,
+                                       "about:accounts?action=signup&entrypoint=uitour");
+});
--- a/browser/components/uitour/test/browser_UITour_toggleReaderMode.js
+++ b/browser/components/uitour/test/browser_UITour_toggleReaderMode.js
@@ -1,18 +1,16 @@
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
-function test() {
-  UITourTest();
-}
+add_task(setup_UITourTest);
 
-var tests = [
-  taskify(function*() {
-    ok(!gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"), "Should not be in reader mode at start of test.");
-    gContentAPI.toggleReaderMode();
-    yield waitForConditionPromise(() => gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"));
-    ok(gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"), "Should be in reader mode now.");
-  })
-];
+add_UITour_task(function*() {
+  ok(!gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"),
+     "Should not be in reader mode at start of test.");
+  yield gContentAPI.toggleReaderMode();
+  yield waitForConditionPromise(() => gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"));
+  ok(gBrowser.selectedBrowser.currentURI.spec.startsWith("about:reader"),
+     "Should be in reader mode now.");
+});
--- a/browser/components/uitour/test/browser_backgroundTab.js
+++ b/browser/components/uitour/test/browser_backgroundTab.js
@@ -1,47 +1,34 @@
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
-function test() {
-  requestLongerTimeout(2);
-  UITourTest();
-}
-
-var tests = [
-  function test_bg_getConfiguration(done) {
-    info("getConfiguration is on the allowed list so should work");
-    loadForegroundTab().then(() => {
-      gContentAPI.getConfiguration("availableTargets", (data) => {
-        ok(data, "Got data from getConfiguration");
-        gBrowser.removeCurrentTab();
-        done();
-      });
-    });
-  },
-  taskify(function* test_bg_showInfo() {
-    info("showInfo isn't on the allowed action list so should be denied");
-    yield loadForegroundTab();
-
-    yield showInfoPromise("appMenu", "Hello from the backgrund", "Surprise!").then(
-      () => ok(false, "panel shouldn't have shown from a background tab"),
-      () => ok(true, "panel wasn't shown from a background tab"));
-
-    gBrowser.removeCurrentTab();
-  }),
-];
+requestLongerTimeout(2);
+add_task(setup_UITourTest);
 
 
-function loadForegroundTab() {
-  let newTab = gBrowser.addTab("about:blank", {skipAnimation: true});
-  gBrowser.selectedTab = newTab;
+add_UITour_task(function* test_bg_getConfiguration() {
+  info("getConfiguration is on the allowed list so should work");
+  yield* loadForegroundTab();
+  let data = yield getConfigurationPromise("availableTargets");
+  ok(data, "Got data from getConfiguration");
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+add_UITour_task(function* test_bg_showInfo() {
+  info("showInfo isn't on the allowed action list so should be denied");
+  yield* loadForegroundTab();
 
-  return new Promise((resolve) => {
-    newTab.linkedBrowser.addEventListener("load", function onLoad() {
-      newTab.linkedBrowser.removeEventListener("load", onLoad, true);
-      isnot(gBrowser.selectedTab, gTestTab, "Make sure tour tab isn't selected");
-      resolve();
-    }, true);
-  });
+  yield showInfoPromise("appMenu", "Hello from the backgrund", "Surprise!").then(
+    () => ok(false, "panel shouldn't have shown from a background tab"),
+    () => ok(true, "panel wasn't shown from a background tab"));
+
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+
+function* loadForegroundTab() {
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser);
+  isnot(gBrowser.selectedTab, gTestTab, "Make sure tour tab isn't selected");
 }
--- a/browser/components/uitour/test/browser_closeTab.js
+++ b/browser/components/uitour/test/browser_closeTab.js
@@ -1,23 +1,16 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
 "use strict";
 
 var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
-function test() {
-  UITourTest();
-}
+add_task(setup_UITourTest);
 
-var tests = [
-  taskify(function* test_closeTab() {
-    // Setting gTestTab to null indicates that the tab has already been closed,
-    // and if this does not happen the test run will fail.
-    gContentAPI.closeTab();
-    gTestTab = null;
-  }),
-];
+add_UITour_task(function* test_closeTab() {
+  // Setting gTestTab to null indicates that the tab has already been closed,
+  // and if this does not happen the test run will fail.
+  yield gContentAPI.closeTab();
+  gTestTab = null;
+});
--- a/browser/components/uitour/test/browser_openPreferences.js
+++ b/browser/components/uitour/test/browser_openPreferences.js
@@ -1,43 +1,36 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
 "use strict";
 
 var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
-function test() {
-  UITourTest();
-}
+add_task(setup_UITourTest);
 
-var tests = [
-  taskify(function* test_openPreferences() {
-    let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences");
-    gContentAPI.openPreferences();
-    let tab = yield promiseTabOpened;
-    yield BrowserTestUtils.removeTab(tab);
-  }),
+add_UITour_task(function* test_openPreferences() {
+  let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences");
+  yield gContentAPI.openPreferences();
+  let tab = yield promiseTabOpened;
+  yield BrowserTestUtils.removeTab(tab);
+});
 
-  taskify(function* test_openInvalidPreferences() {
-    gContentAPI.openPreferences(999);
+add_UITour_task(function* test_openInvalidPreferences() {
+  yield gContentAPI.openPreferences(999);
 
-    try {
-      yield waitForConditionPromise(() => {
-        return gBrowser.selectedBrowser.currentURI.spec.startsWith("about:preferences");
-      }, "Check if about:preferences opened");
-      ok(false, "No about:preferences tab should have opened");
-    } catch (ex) {
-      ok(true, "No about:preferences tab opened: " + ex);
-    }
-  }),
+  try {
+    yield waitForConditionPromise(() => {
+      return gBrowser.selectedBrowser.currentURI.spec.startsWith("about:preferences");
+    }, "Check if about:preferences opened");
+    ok(false, "No about:preferences tab should have opened");
+  } catch (ex) {
+    ok(true, "No about:preferences tab opened: " + ex);
+  }
+});
 
-  taskify(function* test_openPrivacyPreferences() {
-    let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#privacy");
-    gContentAPI.openPreferences("privacy");
-    let tab = yield promiseTabOpened;
-    yield BrowserTestUtils.removeTab(tab);
-  }),
-];
+add_UITour_task(function* test_openPrivacyPreferences() {
+  let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#privacy");
+  yield gContentAPI.openPreferences("privacy");
+  let tab = yield promiseTabOpened;
+  yield BrowserTestUtils.removeTab(tab);
+});
--- a/browser/components/uitour/test/browser_showMenu_controlCenter.js
+++ b/browser/components/uitour/test/browser_showMenu_controlCenter.js
@@ -3,46 +3,42 @@
 var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 const CONTROL_CENTER_PANEL = gIdentityHandler._identityPopup;
 const CONTROL_CENTER_MENU_NAME = "controlCenter";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
-function test() {
-  UITourTest();
-}
+add_task(setup_UITourTest);
+
+add_UITour_task(function* test_showMenu() {
+  is_element_hidden(CONTROL_CENTER_PANEL, "Panel should initially be hidden");
+  yield showMenuPromise(CONTROL_CENTER_MENU_NAME);
+  is_element_visible(CONTROL_CENTER_PANEL, "Panel should be visible after showMenu");
 
-var tests = [
-  taskify(function* test_showMenu() {
-    is_element_hidden(CONTROL_CENTER_PANEL, "Panel should initially be hidden");
-    yield showMenuPromise(CONTROL_CENTER_MENU_NAME);
-    is_element_visible(CONTROL_CENTER_PANEL, "Panel should be visible after showMenu");
+  yield gURLBar.focus();
+  is_element_visible(CONTROL_CENTER_PANEL, "Panel should remain visible after focus outside");
 
-    yield gURLBar.focus();
-    is_element_visible(CONTROL_CENTER_PANEL, "Panel should remain visible after focus outside");
-
-    yield showMenuPromise(CONTROL_CENTER_MENU_NAME);
-    is_element_visible(CONTROL_CENTER_PANEL,
-                       "Panel should remain visible and callback called after a 2nd showMenu");
+  yield showMenuPromise(CONTROL_CENTER_MENU_NAME);
+  is_element_visible(CONTROL_CENTER_PANEL,
+                     "Panel should remain visible and callback called after a 2nd showMenu");
 
-    yield BrowserTestUtils.withNewTab({
-      gBrowser,
-      url: "about:blank"
-    }, function*() {
-      ok(true, "Tab opened");
-    });
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: "about:blank"
+  }, function*() {
+    ok(true, "Tab opened");
+  });
 
-    is_element_hidden(CONTROL_CENTER_PANEL, "Panel should hide upon tab switch");
-  }),
+  is_element_hidden(CONTROL_CENTER_PANEL, "Panel should hide upon tab switch");
+});
 
-  taskify(function* test_hideMenu() {
-    is_element_hidden(CONTROL_CENTER_PANEL, "Panel should initially be hidden");
-    yield showMenuPromise(CONTROL_CENTER_MENU_NAME);
-    is_element_visible(CONTROL_CENTER_PANEL, "Panel should be visible after showMenu");
-    let hidePromise = promisePanelElementHidden(window, CONTROL_CENTER_PANEL);
-    gContentAPI.hideMenu(CONTROL_CENTER_MENU_NAME);
-    yield hidePromise;
+add_UITour_task(function* test_hideMenu() {
+  is_element_hidden(CONTROL_CENTER_PANEL, "Panel should initially be hidden");
+  yield showMenuPromise(CONTROL_CENTER_MENU_NAME);
+  is_element_visible(CONTROL_CENTER_PANEL, "Panel should be visible after showMenu");
+  let hidePromise = promisePanelElementHidden(window, CONTROL_CENTER_PANEL);
+  gContentAPI.hideMenu(CONTROL_CENTER_MENU_NAME);
+  yield hidePromise;
 
-    is_element_hidden(CONTROL_CENTER_PANEL, "Panel should hide after hideMenu");
-  }),
-];
+  is_element_hidden(CONTROL_CENTER_PANEL, "Panel should hide after hideMenu");
+});
--- a/browser/components/uitour/test/browser_trackingProtection_tour.js
+++ b/browser/components/uitour/test/browser_trackingProtection_tour.js
@@ -1,53 +1,46 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
 const { UrlClassifierTestUtils } = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
 
 const TP_ENABLED_PREF = "privacy.trackingprotection.enabled";
 
-function test() {
-  UITourTest();
-}
+add_task(setup_UITourTest);
+
+add_task(function* test_setup() {
+  Services.prefs.setBoolPref("privacy.trackingprotection.enabled", true);
+  yield UrlClassifierTestUtils.addTestTrackers();
 
-var tests = [
-  taskify(function* test_setup() {
-    Services.prefs.setBoolPref("privacy.trackingprotection.enabled", true);
-    yield UrlClassifierTestUtils.addTestTrackers();
-
-    registerCleanupFunction(function() {
-      UrlClassifierTestUtils.cleanupTestTrackers();
-      Services.prefs.clearUserPref("privacy.trackingprotection.enabled");
-    });
-  }),
+  registerCleanupFunction(function() {
+    UrlClassifierTestUtils.cleanupTestTrackers();
+    Services.prefs.clearUserPref("privacy.trackingprotection.enabled");
+  });
+});
 
-  taskify(function* test_unblock_target() {
-    yield* checkToggleTarget("controlCenter-trackingUnblock");
-  }),
+add_UITour_task(function* test_unblock_target() {
+  yield* checkToggleTarget("controlCenter-trackingUnblock");
+});
 
-  taskify(function* setup_block_target() {
-    // Preparation for test_block_target. These are separate since the reload
-    // interferes with UITour as it does a teardown. All we really care about
-    // is the permission manager entry but UITour tests shouldn't rely on that
-    // implementation detail.
-    TrackingProtection.disableForCurrentPage();
-  }),
+add_UITour_task(function* setup_block_target() {
+  // Preparation for test_block_target. These are separate since the reload
+  // interferes with UITour as it does a teardown. All we really care about
+  // is the permission manager entry but UITour tests shouldn't rely on that
+  // implementation detail.
+  TrackingProtection.disableForCurrentPage();
+});
 
-  taskify(function* test_block_target() {
-    yield* checkToggleTarget("controlCenter-trackingBlock");
-    TrackingProtection.enableForCurrentPage();
-  }),
-];
+add_UITour_task(function* test_block_target() {
+  yield* checkToggleTarget("controlCenter-trackingBlock");
+  TrackingProtection.enableForCurrentPage();
+});
 
 
 function* checkToggleTarget(targetID) {
   let popup = document.getElementById("UITourTooltip");
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
     let doc = content.document;
     let iframe = doc.createElement("iframe");
@@ -67,17 +60,17 @@ function* checkToggleTarget(targetID) {
 
   yield showInfoPromise(targetID, "This is " + targetID,
                         "My arrow should be on the side");
   is(popup.popupBoxObject.alignmentPosition, "end_before",
      "Check " + targetID + " position");
 
   let hideMenuPromise =
         promisePanelElementHidden(window, gIdentityHandler._identityPopup);
-  gContentAPI.hideMenu("controlCenter");
+  yield gContentAPI.hideMenu("controlCenter");
   yield hideMenuPromise;
 
   ok(!is_visible(popup), "The tooltip should now be hidden.");
   yield testTargetAvailability(false);
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
     content.document.getElementById("tracking-element").remove();
   });
--- a/browser/components/uitour/test/head.js
+++ b/browser/components/uitour/test/head.js
@@ -1,8 +1,10 @@
+"use strict";
+
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UITour",
                                   "resource:///modules/UITour.jsm");
 
 
 const SINGLE_TRY_TIMEOUT = 100;
 const NUMBER_OF_TRIES = 30;
@@ -27,22 +29,22 @@ function waitForConditionPromise(conditi
     setTimeout(checkCondition, SINGLE_TRY_TIMEOUT);
   }
   setTimeout(checkCondition, SINGLE_TRY_TIMEOUT);
   return defer.promise;
 }
 
 function waitForCondition(condition, nextTest, errorMsg) {
   waitForConditionPromise(condition, errorMsg).then(nextTest, (reason) => {
-    ok(false, reason + (reason.stack ? "\n" + e.stack : ""));
+    ok(false, reason + (reason.stack ? "\n" + reason.stack : ""));
   });
 }
 
 /**
- * Wrapper to partially transition tests to Task.
+ * Wrapper to partially transition tests to Task. Use `add_UITour_task` instead for new tests.
  */
 function taskify(fun) {
   return (done) => {
     // Output the inner function name otherwise no name will be output.
     info("\t" + fun.name);
     return Task.spawn(fun).then(done, (reason) => {
       ok(false, reason);
       done();
@@ -119,49 +121,72 @@ function waitForPopupAtAnchor(popup, anc
                      ok(true, msg);
                      is_element_visible(popup, "Popup should be visible");
                      nextTest();
                    },
                    "Timeout waiting for popup at anchor: " + msg);
 }
 
 function getConfigurationPromise(configName) {
-  return new Promise(resolve => {
-    gContentAPI.getConfiguration(configName, data => resolve(data));
+  return ContentTask.spawn(gTestTab.linkedBrowser, configName, configName => {
+    return new Promise((resolve) => {
+      let contentWin = Components.utils.waiveXrays(content);
+      contentWin.Mozilla.UITour.getConfiguration(configName, resolve);
+    });
   });
 }
 
 function hideInfoPromise(...args) {
   let popup = document.getElementById("UITourTooltip");
   gContentAPI.hideInfo.apply(gContentAPI, args);
   return promisePanelElementHidden(window, popup);
 }
 
-function showInfoPromise(...args) {
+/**
+ * `buttons` and `options` require functions from the content scope so we take a
+ * function name to call to generate the buttons/options instead of the
+ * buttons/options themselves. This makes the signature differ from the content one.
+ */
+function showInfoPromise(target, title, text, icon, buttonsFunctionName, optionsFunctionName) {
   let popup = document.getElementById("UITourTooltip");
-  gContentAPI.showInfo.apply(gContentAPI, args);
-  return promisePanelElementShown(window, popup);
+  return ContentTask.spawn(gTestTab.linkedBrowser, [...arguments], args => {
+    let contentWin = Components.utils.waiveXrays(content);
+    let [target, title, text, icon, buttonsFunctionName, optionsFunctionName] = args;
+    let buttons = buttonsFunctionName ? contentWin[buttonsFunctionName]() : null;
+    let options = optionsFunctionName ? contentWin[optionsFunctionName]() : null;
+    contentWin.Mozilla.UITour.showInfo(target, title, text, icon, buttons, options);
+  }).then(() => promisePanelElementShown(window, popup));
 }
 
 function showHighlightPromise(...args) {
   let popup = document.getElementById("UITourHighlightContainer");
   gContentAPI.showHighlight.apply(gContentAPI, args);
   return promisePanelElementShown(window, popup);
 }
 
 function showMenuPromise(name) {
-  return new Promise(resolve => {
-    gContentAPI.showMenu(name, () => resolve());
+  return ContentTask.spawn(gTestTab.linkedBrowser, name, name => {
+    return new Promise((resolve) => {
+      let contentWin = Components.utils.waiveXrays(content);
+      contentWin.Mozilla.UITour.showMenu(name, resolve);
+    });
   });
 }
 
 function waitForCallbackResultPromise() {
-  return waitForConditionPromise(() => {
-    return gContentWindow.callbackResult;
-  }, "callback should be called");
+  return ContentTask.spawn(gTestTab.linkedBrowser, null, function*() {
+    let contentWin = Components.utils.waiveXrays(content);
+    yield ContentTaskUtils.waitForCondition(() => {
+      return contentWin.callbackResult;
+    }, "callback should be called");
+    return {
+      data: contentWin.callbackData,
+      result: contentWin.callbackResult,
+    };
+  });
 }
 
 function promisePanelShown(win) {
   let panelEl = win.PanelUI.panel;
   return promisePanelElementShown(win, panelEl);
 }
 
 function promisePanelElementEvent(win, aPanel, aEvent) {
@@ -218,45 +243,100 @@ function loadUITourTestPage(callback, ho
   url = url.replace("chrome://mochitests/content/", host);
 
   gTestTab = gBrowser.addTab(url);
   gBrowser.selectedTab = gTestTab;
 
   gTestTab.linkedBrowser.addEventListener("load", function onLoad() {
     gTestTab.linkedBrowser.removeEventListener("load", onLoad, true);
 
-    gContentWindow = Components.utils.waiveXrays(gTestTab.linkedBrowser.contentDocument.defaultView);
-    gContentAPI = gContentWindow.Mozilla.UITour;
+    if (gMultiProcessBrowser) {
+      // When e10s is enabled, make gContentAPI and gContentWindow proxies which has every property
+      // return a function which calls the method of the same name on
+      // contentWin.Mozilla.UITour/contentWin in a ContentTask.
+      let contentWinHandler = {
+        get(target, prop, receiver) {
+          return (...args) => {
+            let taskArgs = {
+              methodName: prop,
+              args,
+            };
+            return ContentTask.spawn(gTestTab.linkedBrowser, taskArgs, args => {
+              let contentWin = Components.utils.waiveXrays(content);
+              return contentWin[args.methodName].apply(contentWin, args.args);
+            });
+          };
+        },
+      };
+      gContentWindow = new Proxy({}, contentWinHandler);
 
-    waitForFocus(callback, gContentWindow);
+      let UITourHandler = {
+        get(target, prop, receiver) {
+          return (...args) => {
+            let taskArgs = {
+              methodName: prop,
+              args,
+            };
+            return ContentTask.spawn(gTestTab.linkedBrowser, taskArgs, args => {
+              let contentWin = Components.utils.waiveXrays(content);
+              return contentWin.Mozilla.UITour[args.methodName].apply(contentWin.Mozilla.UITour,
+                                                                      args.args);
+            });
+          };
+        },
+      };
+      gContentAPI = new Proxy({}, UITourHandler);
+    } else {
+      gContentWindow = Components.utils.waiveXrays(gTestTab.linkedBrowser.contentDocument.defaultView);
+      gContentAPI = gContentWindow.Mozilla.UITour;
+    }
+
+    waitForFocus(callback, gTestTab.linkedBrowser);
   }, true);
 }
 
-function UITourTest() {
+// Wrapper for UITourTest to be used by add_task tests.
+function* setup_UITourTest() {
+  return UITourTest(true);
+}
+
+// Use `add_task(setup_UITourTest);` instead as we will fold this into `setup_UITourTest` once all tests are using `add_UITour_task`.
+function UITourTest(usingAddTask = false) {
   Services.prefs.setBoolPref("browser.uitour.enabled", true);
   let testHttpsUri = Services.io.newURI("https://example.org", null, null);
   let testHttpUri = Services.io.newURI("http://example.org", null, null);
   Services.perms.add(testHttpsUri, "uitour", Services.perms.ALLOW_ACTION);
   Services.perms.add(testHttpUri, "uitour", Services.perms.ALLOW_ACTION);
 
-  waitForExplicitFinish();
+  // If a test file is using add_task, we don't need to have a test function or
+  // call `waitForExplicitFinish`.
+  if (!usingAddTask) {
+    waitForExplicitFinish();
+  }
 
   registerCleanupFunction(function() {
     delete window.gContentWindow;
     delete window.gContentAPI;
     if (gTestTab)
       gBrowser.removeTab(gTestTab);
     delete window.gTestTab;
-    Services.prefs.clearUserPref("browser.uitour.enabled", true);
+    Services.prefs.clearUserPref("browser.uitour.enabled");
     Services.perms.remove(testHttpsUri, "uitour");
     Services.perms.remove(testHttpUri, "uitour");
   });
 
-  function done() {
-    info("== Done test, doing shared checks before teardown ==");
+  // When using tasks, the harness will call the next added task for us.
+  if (!usingAddTask) {
+    nextTest();
+  }
+}
+
+function done(usingAddTask = false) {
+  info("== Done test, doing shared checks before teardown ==");
+  return new Promise((resolve) => {
     executeSoon(() => {
       if (gTestTab)
         gBrowser.removeTab(gTestTab);
       gTestTab = null;
 
       let highlight = document.getElementById("UITourHighlightContainer");
       is_element_hidden(highlight, "Highlight should be closed/hidden after UITour tab is closed");
 
@@ -264,28 +344,58 @@ function UITourTest() {
       is_element_hidden(tooltip, "Tooltip should be closed/hidden after UITour tab is closed");
 
       ok(!PanelUI.panel.hasAttribute("noautohide"), "@noautohide on the menu panel should have been cleaned up");
       ok(!PanelUI.panel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
       isnot(PanelUI.panel.state, "open", "The panel shouldn't be open");
       is(document.getElementById("PanelUI-menu-button").hasAttribute("open"), false, "Menu button should know that the menu is closed");
 
       info("Done shared checks");
-      executeSoon(nextTest);
+      if (usingAddTask) {
+        executeSoon(resolve);
+      } else {
+        executeSoon(nextTest);
+      }
     });
-  }
+  });
+}
 
-  function nextTest() {
-    if (tests.length == 0) {
-      info("finished tests in this file");
-      finish();
-      return;
-    }
-    let test = tests.shift();
-    info("Starting " + test.name);
-    waitForFocus(function() {
-      loadUITourTestPage(function() {
-        test(done);
+function nextTest() {
+  if (tests.length == 0) {
+    info("finished tests in this file");
+    finish();
+    return;
+  }
+  let test = tests.shift();
+  info("Starting " + test.name);
+  waitForFocus(function() {
+    loadUITourTestPage(function() {
+      test(done);
+    });
+  });
+}
+
+/**
+ * All new tests that need the help of `loadUITourTestPage` should use this
+ * wrapper around their test's generator function to reduce boilerplate.
+ */
+function add_UITour_task(func) {
+  let genFun = function*() {
+    yield new Promise((resolve) => {
+      waitForFocus(function() {
+        loadUITourTestPage(function() {
+          let funcPromise = Task.spawn(func)
+                                .then(() => done(true),
+                                      (reason) => {
+                                        ok(false, reason);
+                                        return done(true);
+                                      });
+          resolve(funcPromise);
+        });
       });
     });
-  }
-  nextTest();
+  };
+  Object.defineProperty(genFun, "name", {
+    configurable: true,
+    value: func.name,
+  });
+  add_task(genFun);
 }