Bug 1366005 - Remove UITour.showHeartbeat. r=MattN draft
authorMike Cooper <mcooper@mozilla.com>
Thu, 18 May 2017 10:53:38 -0700
changeset 586660 006794e29814e091e145a0925d98919f850dfe57
parent 586536 39d5cc0fda5e16c49a59d29d4ca186a5534cc88b
child 586661 b9fe8a1425d916299e83fb8dd23f22bed870d70f
push id61479
push userbmo:mcooper@mozilla.com
push dateTue, 30 May 2017 21:27:52 +0000
reviewersMattN
bugs1366005
milestone55.0a1
Bug 1366005 - Remove UITour.showHeartbeat. r=MattN MozReview-Commit-ID: 7VCJsJK9Bph
browser/components/uitour/UITour-lib.js
browser/components/uitour/UITour.jsm
browser/components/uitour/test/browser.ini
browser/components/uitour/test/browser_UITour_heartbeat.js
browser/themes/shared/UITour.inc.css
browser/themes/shared/heartbeat-icon.svg
browser/themes/shared/heartbeat-star-lit.svg
browser/themes/shared/heartbeat-star-off.svg
browser/themes/shared/jar.inc.mn
--- a/browser/components/uitour/UITour-lib.js
+++ b/browser/components/uitour/UITour-lib.js
@@ -177,58 +177,16 @@ if (typeof Mozilla == "undefined") {
    */
   Mozilla.UITour.registerPageID = function(pageID) {
     _sendEvent("registerPageID", {
       pageID
     });
   };
 
   /**
-   * Show a global notification bar with a prompt and optional buttons.
-   *
-   * Only intended for use by Self Support.
-   *
-   * @deprecated Use Heartbeat from
-   * {@link https://wiki.mozilla.org/Firefox/Shield/Heartbeat|Shield} instead.
-   *
-   * @param {String} message - Text to show in the notification bar before an action is taken.
-   * @param {String} thankyouMessage - Text to show in the notification bar after a vote.
-   * @param {String} flowId - An identifier for this rating flow. Please note that this is only used
-   *                          to identify the notification box.
-   * @param {String} engagementURL - URL to open in a new tab once the user has engaged.
-   * @param {String} learnMoreLabel - The label of the learn more link. No link will be shown if
-   *                                  this is null.
-   * @param {String} learnMoreURL - URL to open when clicking on the learn more link. No link will be
-   *                                shown if this is an invalid URL.
-   * @param {Object} options - Options to control behavior.
-   */
-  Mozilla.UITour.showHeartbeat = function(message, thankyouMessage, flowId, engagementURL,
-					  learnMoreLabel, learnMoreURL, options) {
-    var args = {
-      message,
-      thankyouMessage,
-      flowId,
-      engagementURL,
-      learnMoreLabel,
-      learnMoreURL,
-    };
-
-    if (options) {
-      for (var option in options) {
-	if (!options.hasOwnProperty(option)) {
-	  continue;
-	}
-	args[option] = options[option];
-      }
-    }
-
-    _sendEvent("showHeartbeat", args);
-  };
-
-  /**
    * @typedef {String} Mozilla.UITour.HighlightEffect
    *
    * Specifies the effect/animation to use when highlighting UI elements.
    * @description Valid values:<ul>
    * <li>random
    * <li>wobble
    * <li>zoom
    * <li>color
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -8,17 +8,16 @@ this.EXPORTED_SYMBOLS = ["UITour"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource:///modules/RecentWindow.jsm");
 Cu.import("resource://gre/modules/TelemetryController.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 
 Cu.importGlobalProperties(["URL"]);
 
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
   "resource://gre/modules/LightweightThemeManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ResetProfile",
@@ -32,17 +31,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ReaderParent",
   "resource:///modules/ReaderParent.jsm");
 
 // See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
 const PREF_LOG_LEVEL      = "browser.uitour.loglevel";
 const PREF_SEENPAGEIDS    = "browser.uitour.seenPageIDs";
-const PREF_SURVEY_DURATION = "browser.uitour.surveyDuration";
 
 const BACKGROUND_PAGE_ACTIONS_ALLOWED = new Set([
   "forceShowReaderIcon",
   "getConfiguration",
   "getTreatmentTag",
   "hideHighlight",
   "hideInfo",
   "hideMenu",
@@ -356,52 +354,16 @@ this.UITour = {
 
         this.addSeenPageID(data.pageID);
         this.pageIDSourceBrowsers.set(browser, data.pageID);
         this.setTelemetryBucket(data.pageID);
 
         break;
       }
 
-      case "showHeartbeat": {
-        // Validate the input parameters.
-        if (typeof data.message !== "string" || data.message === "") {
-          log.error("showHeartbeat: Invalid message specified.");
-          return false;
-        }
-
-        if (typeof data.thankyouMessage !== "string" || data.thankyouMessage === "") {
-          log.error("showHeartbeat: Invalid thank you message specified.");
-          return false;
-        }
-
-        if (typeof data.flowId !== "string" || data.flowId === "") {
-          log.error("showHeartbeat: Invalid flowId specified.");
-          return false;
-        }
-
-        if (data.engagementButtonLabel && typeof data.engagementButtonLabel != "string") {
-          log.error("showHeartbeat: Invalid engagementButtonLabel specified");
-          return false;
-        }
-
-        let heartbeatWindow = window;
-        if (data.privateWindowsOnly && !PrivateBrowsingUtils.isWindowPrivate(heartbeatWindow)) {
-          heartbeatWindow = RecentWindow.getMostRecentBrowserWindow({ private: true });
-          if (!heartbeatWindow) {
-            log.debug("showHeartbeat: No private window found");
-            return false;
-          }
-        }
-
-        // Finally show the Heartbeat UI.
-        this.showHeartbeat(heartbeatWindow, data);
-        break;
-      }
-
       case "showHighlight": {
         let targetPromise = this.getTarget(window, data.target);
         targetPromise.then(target => {
           if (!target.node) {
             log.error("UITour: Target could not be resolved: " + data.target);
             return;
           }
           let effect = undefined;
@@ -1037,347 +999,16 @@ this.UITour = {
       LightweightThemeManager.previewTheme(data);
   },
 
   resetTheme() {
     LightweightThemeManager.resetPreview();
   },
 
   /**
-   * Show the Heartbeat UI to request user feedback. This function reports back to the
-   * caller using |notify|. The notification event name reflects the current status the UI
-   * is in (either "Heartbeat:NotificationOffered", "Heartbeat:NotificationClosed",
-   * "Heartbeat:LearnMore", "Heartbeat:Engaged", "Heartbeat:Voted",
-   * "Heartbeat:SurveyExpired" or "Heartbeat:WindowClosed").
-   * When a "Heartbeat:Voted" event is notified
-   * the data payload contains a |score| field which holds the rating picked by the user.
-   * Please note that input parameters are already validated by the caller.
-   *
-   * @param aChromeWindow
-   *        The chrome window that the heartbeat notification is displayed in.
-   * @param {Object} aOptions Options object.
-   * @param {String} aOptions.message
-   *        The message, or question, to display on the notification.
-   * @param {String} aOptions.thankyouMessage
-   *        The thank you message to display after user votes.
-   * @param {String} aOptions.flowId
-   *        An identifier for this rating flow. Please note that this is only used to
-   *        identify the notification box.
-   * @param {String} [aOptions.engagementButtonLabel=null]
-   *        The text of the engagement button to use instad of stars. If this is null
-   *        or invalid, rating stars are used.
-   * @param {String} [aOptions.engagementURL=null]
-   *        The engagement URL to open in a new tab once user has engaged. If this is null
-   *        or invalid, no new tab is opened.
-   * @param {String} [aOptions.learnMoreLabel=null]
-   *        The label of the learn more link. No link will be shown if this is null.
-   * @param {String} [aOptions.learnMoreURL=null]
-   *        The learn more URL to open when clicking on the learn more link. No learn more
-   *        will be shown if this is an invalid URL.
-   * @param {boolean} [aOptions.privateWindowsOnly=false]
-   *        Whether the heartbeat UI should only be targeted at a private window (if one exists).
-   *        No notifications should be fired when this is true.
-   * @param {String} [aOptions.surveyId]
-   *        An ID for the survey, reflected in the Telemetry ping.
-   * @param {Number} [aOptions.surveyVersion]
-   *        Survey's version number, reflected in the Telemetry ping.
-   * @param {boolean} [aOptions.testing]
-   *        Whether this is a test survey, reflected in the Telemetry ping.
-   */
-  showHeartbeat(aChromeWindow, aOptions) {
-    // Initialize survey state
-    let pingSent = false;
-    let surveyResults = {};
-    let surveyEndTimer = null;
-
-    /**
-     * Accumulates survey events and submits to Telemetry after the survey ends.
-     *
-     * @param {String} aEventName
-     *        Heartbeat event name
-     * @param {Object} aParams
-     *        Additional parameters and their values
-     */
-    let maybeNotifyHeartbeat = (aEventName, aParams = {}) => {
-      // Return if event occurred after the ping was sent
-      if (pingSent) {
-        log.warn("maybeNotifyHeartbeat: event occurred after ping sent:", aEventName, aParams);
-        return;
-      }
-
-      // No Telemetry from private-window-only Heartbeats
-      if (aOptions.privateWindowsOnly) {
-        return;
-      }
-
-      let ts = Date.now();
-      let sendPing = false;
-      switch (aEventName) {
-        case "Heartbeat:NotificationOffered":
-          surveyResults.flowId = aOptions.flowId;
-          surveyResults.offeredTS = ts;
-          break;
-        case "Heartbeat:LearnMore":
-          // record only the first click
-          if (!surveyResults.learnMoreTS) {
-            surveyResults.learnMoreTS = ts;
-          }
-          break;
-        case "Heartbeat:Engaged":
-          surveyResults.engagedTS = ts;
-          break;
-        case "Heartbeat:Voted":
-          surveyResults.votedTS = ts;
-          surveyResults.score = aParams.score;
-          break;
-        case "Heartbeat:SurveyExpired":
-          surveyResults.expiredTS = ts;
-          break;
-        case "Heartbeat:NotificationClosed":
-          // this is the final event in most surveys
-          surveyResults.closedTS = ts;
-          sendPing = true;
-          break;
-        case "Heartbeat:WindowClosed":
-          surveyResults.windowClosedTS = ts;
-          sendPing = true;
-          break;
-        default:
-          log.error("maybeNotifyHeartbeat: unrecognized event:", aEventName);
-          break;
-      }
-
-      aParams.timestamp = ts;
-      aParams.flowId = aOptions.flowId;
-      this.notify(aEventName, aParams);
-
-      if (!sendPing) {
-        return;
-      }
-
-      // Send the ping to Telemetry
-      let payload = Object.assign({}, surveyResults);
-      payload.version = 1;
-      for (let meta of ["surveyId", "surveyVersion", "testing"]) {
-        if (aOptions.hasOwnProperty(meta)) {
-          payload[meta] = aOptions[meta];
-        }
-      }
-
-      log.debug("Sending payload to Telemetry: aEventName:", aEventName,
-                "payload:", payload);
-
-      TelemetryController.submitExternalPing("heartbeat", payload, {
-        addClientId: true,
-        addEnvironment: true,
-      });
-
-      // only for testing
-      this.notify("Heartbeat:TelemetrySent", payload);
-
-      // Survey is complete, clear out the expiry timer & survey configuration
-      if (surveyEndTimer) {
-        clearTimeout(surveyEndTimer);
-        surveyEndTimer = null;
-      }
-
-      pingSent = true;
-      surveyResults = {};
-    };
-
-    let nb = aChromeWindow.document.getElementById("high-priority-global-notificationbox");
-    let buttons = null;
-
-    if (aOptions.engagementButtonLabel) {
-      buttons = [{
-        label: aOptions.engagementButtonLabel,
-        callback: () => {
-          // Let the consumer know user engaged.
-          maybeNotifyHeartbeat("Heartbeat:Engaged");
-
-          userEngaged(new Map([
-            ["type", "button"],
-            ["flowid", aOptions.flowId]
-          ]));
-
-          // Return true so that the notification bar doesn't close itself since
-          // we have a thank you message to show.
-          return true;
-        },
-      }];
-    }
-
-    let defaultIcon = "chrome://browser/skin/heartbeat-icon.svg";
-    let iconURL = defaultIcon;
-    try {
-      // Take the optional icon URL if specified
-      if (aOptions.iconURL) {
-        iconURL = new URL(aOptions.iconURL);
-        // For now, only allow chrome URIs.
-        if (iconURL.protocol != "chrome:") {
-          iconURL = defaultIcon;
-          throw new Error("Invalid protocol");
-        }
-      }
-    } catch (error) {
-      log.error("showHeartbeat: Invalid icon URL specified.");
-    }
-
-    // Create the notification. Prefix its ID to decrease the chances of collisions.
-    let notice = nb.appendNotification(aOptions.message, "heartbeat-" + aOptions.flowId,
-                                       iconURL,
-                                       nb.PRIORITY_INFO_HIGH, buttons,
-                                       (aEventType) => {
-                                         if (aEventType != "removed") {
-                                           return;
-                                         }
-                                         // Let the consumer know the notification bar was closed.
-                                         // This also happens after voting.
-                                         maybeNotifyHeartbeat("Heartbeat:NotificationClosed");
-                                       });
-
-    // Get the elements we need to style.
-    let messageImage =
-      aChromeWindow.document.getAnonymousElementByAttribute(notice, "anonid", "messageImage");
-    let messageText =
-      aChromeWindow.document.getAnonymousElementByAttribute(notice, "anonid", "messageText");
-
-    function userEngaged(aEngagementParams) {
-      // Make the heartbeat icon pulse twice.
-      notice.label = aOptions.thankyouMessage;
-      messageImage.classList.remove("pulse-onshow");
-      messageImage.classList.add("pulse-twice");
-
-      // Remove all the children of the notice (rating container
-      // and the flex).
-      while (notice.firstChild) {
-        notice.firstChild.remove();
-      }
-
-      // Make sure that we have a valid URL. If we haven't, do not open the engagement page.
-      let engagementURL = null;
-      try {
-        engagementURL = new URL(aOptions.engagementURL);
-      } catch (error) {
-        log.error("showHeartbeat: Invalid URL specified.");
-      }
-
-      // Just open the engagement tab if we have a valid engagement URL.
-      if (engagementURL) {
-        for (let [param, value] of aEngagementParams) {
-          engagementURL.searchParams.append(param, value);
-        }
-
-        // Open the engagement URL in a new tab.
-        aChromeWindow.gBrowser.selectedTab =
-          aChromeWindow.gBrowser.addTab(engagementURL.toString(), {
-            owner: aChromeWindow.gBrowser.selectedTab,
-            relatedToCurrent: true
-          });
-      }
-
-      // Remove the notification bar after 3 seconds.
-      aChromeWindow.setTimeout(() => {
-        nb.removeNotification(notice);
-      }, 3000);
-    }
-
-    // Create the fragment holding the rating UI.
-    let frag = aChromeWindow.document.createDocumentFragment();
-
-    // Build the Heartbeat star rating.
-    const numStars = aOptions.engagementButtonLabel ? 0 : 5;
-    let ratingContainer = aChromeWindow.document.createElement("hbox");
-    ratingContainer.id = "star-rating-container";
-
-    for (let i = 0; i < numStars; i++) {
-      // Create a star rating element.
-      let ratingElement = aChromeWindow.document.createElement("toolbarbutton");
-
-      // Style it.
-      let starIndex = numStars - i;
-      ratingElement.className = "plain star-x";
-      ratingElement.id = "star" + starIndex;
-      ratingElement.setAttribute("data-score", starIndex);
-
-      // Add the click handler.
-      ratingElement.addEventListener("click", function(evt) {
-        let rating = Number(evt.target.getAttribute("data-score"), 10);
-
-        // Let the consumer know user voted.
-        maybeNotifyHeartbeat("Heartbeat:Voted", { score: rating });
-
-        // Append the score data to the engagement URL.
-        userEngaged(new Map([
-          ["type", "stars"],
-          ["score", rating],
-          ["flowid", aOptions.flowId]
-        ]));
-      });
-
-      // Add it to the container.
-      ratingContainer.appendChild(ratingElement);
-    }
-
-    frag.appendChild(ratingContainer);
-
-    // Make sure the stars are not pushed to the right by the spacer.
-    let rightSpacer = aChromeWindow.document.createElement("spacer");
-    rightSpacer.flex = 20;
-    frag.appendChild(rightSpacer);
-
-    messageText.flex = 0; // Collapse the space before the stars.
-    let leftSpacer = messageText.nextSibling;
-    leftSpacer.flex = 0;
-
-    // Make sure that we have a valid learn more URL.
-    let learnMoreURL = null;
-    try {
-      learnMoreURL = new URL(aOptions.learnMoreURL);
-    } catch (error) {
-      log.error("showHeartbeat: Invalid learnMore URL specified.");
-    }
-
-    // Add the learn more link.
-    if (aOptions.learnMoreLabel && learnMoreURL) {
-      let learnMore = aChromeWindow.document.createElement("label");
-      learnMore.className = "text-link";
-      learnMore.href = learnMoreURL.toString();
-      learnMore.setAttribute("value", aOptions.learnMoreLabel);
-      learnMore.addEventListener("click", () => maybeNotifyHeartbeat("Heartbeat:LearnMore"));
-      frag.appendChild(learnMore);
-    }
-
-    // Append the fragment and apply the styling.
-    notice.appendChild(frag);
-    notice.classList.add("heartbeat");
-    messageImage.classList.add("heartbeat", "pulse-onshow");
-    messageText.classList.add("heartbeat");
-
-    // Let the consumer know the notification was shown.
-    maybeNotifyHeartbeat("Heartbeat:NotificationOffered");
-
-    // End the survey if the user quits, closes the window, or
-    // hasn't responded before expiration.
-    if (!aOptions.privateWindowsOnly) {
-      function handleWindowClosed(aTopic) {
-        maybeNotifyHeartbeat("Heartbeat:WindowClosed");
-        aChromeWindow.removeEventListener("SSWindowClosing", handleWindowClosed);
-      }
-      aChromeWindow.addEventListener("SSWindowClosing", handleWindowClosed);
-
-      let surveyDuration = Services.prefs.getIntPref(PREF_SURVEY_DURATION) * 1000;
-      surveyEndTimer = setTimeout(() => {
-        maybeNotifyHeartbeat("Heartbeat:SurveyExpired");
-        nb.removeNotification(notice);
-      }, surveyDuration);
-    }
-  },
-
-  /**
    * The node to which a highlight or notification(-popup) is anchored is sometimes
    * obscured because it may be inside an overflow menu. This function should figure
    * that out and offer the overflow chevron as an alternative.
    *
    * @param {Node} aAnchor The element that's supposed to be the anchor
    * @type {Node}
    */
   _correctAnchor(aAnchor) {
--- a/browser/components/uitour/test/browser.ini
+++ b/browser/components/uitour/test/browser.ini
@@ -28,18 +28,16 @@ skip-if = os == "linux" # Intermittent f
 [browser_UITour2.js]
 [browser_UITour3.js]
 skip-if = os == "linux" # Linux: Bug 986760, Bug 989101.
 [browser_UITour_availableTargets.js]
 [browser_UITour_annotation_size_attributes.js]
 [browser_UITour_defaultBrowser.js]
 [browser_UITour_detach_tab.js]
 [browser_UITour_forceReaderMode.js]
-[browser_UITour_heartbeat.js]
-skip-if = os == "win" # Bug 1277107
 [browser_UITour_modalDialog.js]
 skip-if = os != "mac" # modal dialog disabling only working on OS X.
 [browser_UITour_observe.js]
 [browser_UITour_panel_close_annotation.js]
 skip-if = true # Disabled due to frequent failures, bugs 1026310 and 1032137
 [browser_UITour_pocket.js]
 skip-if = true # Disabled pending removal of pocket UI Tour
 [browser_UITour_registerPageID.js]
deleted file mode 100644
--- a/browser/components/uitour/test/browser_UITour_heartbeat.js
+++ /dev/null
@@ -1,753 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-var gTestTab;
-var gContentAPI;
-var gContentWindow;
-
-function getHeartbeatNotification(aId, aChromeWindow = window) {
-  let notificationBox = aChromeWindow.document.getElementById("high-priority-global-notificationbox");
-  // UITour.jsm prefixes the notification box ID with "heartbeat-" to prevent collisions.
-  return notificationBox.getNotificationWithValue("heartbeat-" + aId);
-}
-
-/**
- * Simulate a click on a rating element in the Heartbeat notification.
- *
- * @param aId
- *        The id of the notification box.
- * @param aScore
- *        The score related to the rating element we want to click on.
- */
-function simulateVote(aId, aScore) {
-  let notification = getHeartbeatNotification(aId);
-
-  let ratingContainer = notification.childNodes[0];
-  ok(ratingContainer, "The notification has a valid rating container.");
-
-  let ratingElement = ratingContainer.getElementsByAttribute("data-score", aScore);
-  ok(ratingElement[0], "The rating container contains the requested rating element.");
-
-  ratingElement[0].click();
-}
-
-/**
- * Simulate a click on the learn-more link.
- *
- * @param aId
- *        The id of the notification box.
- */
-function clickLearnMore(aId) {
-  let notification = getHeartbeatNotification(aId);
-
-  let learnMoreLabel = notification.childNodes[2];
-  ok(learnMoreLabel, "The notification has a valid learn more label.");
-
-  learnMoreLabel.click();
-}
-
-/**
- * Remove the notification box.
- *
- * @param aId
- *        The id of the notification box to remove.
- * @param [aChromeWindow=window]
- *        The chrome window the notification box is in.
- */
-function cleanUpNotification(aId, aChromeWindow = window) {
-  let notification = getHeartbeatNotification(aId, aChromeWindow);
-  notification.close();
-}
-
-/**
- * Check telemetry payload for proper format and expected content.
- *
- * @param aPayload
- *        The Telemetry payload to verify
- * @param aFlowId
- *        Expected value of the flowId field.
- * @param aExpectedFields
- *        Array of expected fields. No other fields are allowed.
- */
-function checkTelemetry(aPayload, aFlowId, aExpectedFields) {
-  // Basic payload format
-  is(aPayload.version, 1, "Telemetry ping must have heartbeat version=1");
-  is(aPayload.flowId, aFlowId, "Flow ID in the Telemetry ping must match");
-
-  // Check for superfluous fields
-  let extraKeys = new Set(Object.keys(aPayload));
-  extraKeys.delete("version");
-  extraKeys.delete("flowId");
-
-  // Check for expected fields
-  for (let field of aExpectedFields) {
-    ok(field in aPayload, "The payload should have the field '" + field + "'");
-    if (field.endsWith("TS")) {
-      let ts = aPayload[field];
-      ok(Number.isInteger(ts) && ts > 0, "Timestamp '" + field + "' must be a natural number");
-    }
-    extraKeys.delete(field);
-  }
-
-  is(extraKeys.size, 0, "No unexpected fields in the Telemetry payload");
-}
-
-/**
- * Waits for an UITour notification dispatched through |UITour.notify|. This should be
- * done with |gContentAPI.observe|. Unfortunately, in e10s, |gContentAPI.observe| doesn't
- * allow for multiple calls to the same callback, allowing to catch just the first
- * notification.
- *
- * @param aEventName
- *        The notification name to wait for.
- * @return {Promise} Resolved with the data that comes with the event.
- */
-function promiseWaitHeartbeatNotification(aEventName) {
-  return ContentTask.spawn(gTestTab.linkedBrowser, aEventName, (aContentEventName) => {
-        return new Promise(resolve => {
-          addEventListener("mozUITourNotification", function listener(event) {
-            if (event.detail.event !== aContentEventName) {
-              return;
-            }
-            removeEventListener("mozUITourNotification", listener, false);
-            resolve(event.detail.params);
-          }, false);
-        });
-      });
-}
-
-/**
- * Waits for UITour notifications dispatched through |UITour.notify|. This works like
- * |promiseWaitHeartbeatNotification|, but waits for all the passed notifications to
- * be received before resolving. If it receives an unaccounted notification, it rejects.
- *
- * @param events
- *        An array of expected notification names to wait for.
- * @return {Promise} Resolved with the data that comes with the event. Rejects with the
- *         name of an undesired notification if received.
- */
-function promiseWaitExpectedNotifications(events) {
-  return ContentTask.spawn(gTestTab.linkedBrowser, events, contentEvents => {
-        let stillToReceive = contentEvents;
-        return new Promise((res, rej) => {
-          addEventListener("mozUITourNotification", function listener(event) {
-            if (stillToReceive.includes(event.detail.event)) {
-              // Filter out the received event.
-              stillToReceive = stillToReceive.filter(x => x !== event.detail.event);
-            } else {
-              removeEventListener("mozUITourNotification", listener, false);
-              rej(event.detail.event);
-            }
-            // We still need to catch some notifications. Don't do anything.
-            if (stillToReceive.length > 0) {
-              return;
-            }
-            // We don't need to listen for other notifications. Resolve the promise.
-            removeEventListener("mozUITourNotification", listener, false);
-            res();
-          }, false);
-        });
-      });
-}
-
-function validateTimestamp(eventName, timestamp) {
-  info("'" + eventName + "' notification received (timestamp " + timestamp.toString() + ").");
-  ok(Number.isFinite(timestamp), "Timestamp must be a number.");
-}
-
-add_task(async function test_setup() {
-  await setup_UITourTest();
-  requestLongerTimeout(2);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("browser.uitour.surveyDuration");
-  });
-});
-
-/**
- * Check that the "stars" heartbeat UI correctly shows and closes.
- */
-add_UITour_task(async function test_heartbeat_stars_show() {
-  let flowId = "ui-ratefirefox-" + Math.random();
-  let engagementURL = "http://example.com";
-
-  // We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
-  // in UITour-lib.js, otherwise no message will get propagated.
-  gContentAPI.observe(() => {});
-
-  let receivedExpectedPromise = promiseWaitExpectedNotifications(
-    ["Heartbeat:NotificationOffered", "Heartbeat:NotificationClosed", "Heartbeat:TelemetrySent"]);
-
-  // Show the Heartbeat notification and wait for it to be displayed.
-  let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
-  gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL);
-
-  // Validate the returned timestamp.
-  let data = await shownPromise;
-  validateTimestamp("Heartbeat:Offered", data.timestamp);
-
-  // Close the heartbeat notification.
-  let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
-  let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
-  cleanUpNotification(flowId);
-
-  data = await closedPromise;
-  validateTimestamp("Heartbeat:NotificationClosed", data.timestamp);
-
-  data = await pingSentPromise;
-  info("'Heartbeat:TelemetrySent' notification received");
-  checkTelemetry(data, flowId, ["offeredTS", "closedTS"]);
-
-  // This rejects whenever an unexpected notification is received.
-  await receivedExpectedPromise;
-})
-
-/**
- * Check that the heartbeat UI correctly takes optional icon URL.
- */
-add_UITour_task(async function test_heartbeat_take_optional_icon_URL() {
-  let flowId = "ui-ratefirefox-" + Math.random();
-  let engagementURL = "http://example.com";
-  let iconURL = "chrome://branding/content/icon48.png";
-
-  // We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
-  // in UITour-lib.js, otherwise no message will get propagated.
-  gContentAPI.observe(() => {});
-
-  let receivedExpectedPromise = promiseWaitExpectedNotifications(
-    ["Heartbeat:NotificationOffered", "Heartbeat:NotificationClosed", "Heartbeat:TelemetrySent"]);
-
-  // Show the Heartbeat notification and wait for it to be displayed.
-  let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
-  gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL, null, null, {
-    iconURL
-  });
-
-  // Validate the returned timestamp.
-  let data = await shownPromise;
-  validateTimestamp("Heartbeat:Offered", data.timestamp);
-
-  // Check the icon URL
-  let notification = getHeartbeatNotification(flowId);
-  is(notification.image, iconURL, "The optional icon URL is not taken correctly");
-
-  // Close the heartbeat notification.
-  let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
-  let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
-  cleanUpNotification(flowId);
-
-  data = await closedPromise;
-  validateTimestamp("Heartbeat:NotificationClosed", data.timestamp);
-
-  data = await pingSentPromise;
-  info("'Heartbeat:TelemetrySent' notification received");
-  checkTelemetry(data, flowId, ["offeredTS", "closedTS"]);
-
-  // This rejects whenever an unexpected notification is received.
-  await receivedExpectedPromise;
-})
-
-/**
- * Test that the heartbeat UI correctly works with null engagement URL.
- */
-add_UITour_task(async function test_heartbeat_null_engagementURL() {
-  let flowId = "ui-ratefirefox-" + Math.random();
-  let originalTabCount = gBrowser.tabs.length;
-
-  // We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
-  // in UITour-lib.js, otherwise no message will get propagated.
-  gContentAPI.observe(() => {});
-
-  let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
-    "Heartbeat:NotificationClosed", "Heartbeat:Voted", "Heartbeat:TelemetrySent"]);
-
-  // Show the Heartbeat notification and wait for it to be displayed.
-  let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
-  gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, null);
-
-  // Validate the returned timestamp.
-  let data = await shownPromise;
-  validateTimestamp("Heartbeat:Offered", data.timestamp);
-
-  // Wait an the Voted, Closed and Telemetry Sent events. They are fired together, so
-  // wait for them here.
-  let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
-  let votedPromise = promiseWaitHeartbeatNotification("Heartbeat:Voted");
-  let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
-
-  // The UI was just shown. We can simulate a click on a rating element (i.e., "star").
-  simulateVote(flowId, 2);
-  data = await votedPromise;
-  validateTimestamp("Heartbeat:Voted", data.timestamp);
-
-  // Validate the closing timestamp.
-  data = await closedPromise;
-  validateTimestamp("Heartbeat:NotificationClosed", data.timestamp);
-  is(gBrowser.tabs.length, originalTabCount, "No engagement tab should be opened.");
-
-  // Validate the data we send out.
-  data = await pingSentPromise;
-  info("'Heartbeat:TelemetrySent' notification received.");
-  checkTelemetry(data, flowId, ["offeredTS", "votedTS", "closedTS", "score"]);
-  is(data.score, 2, "Checking Telemetry payload.score");
-
-  // This rejects whenever an unexpected notification is received.
-  await receivedExpectedPromise;
-})
-
-/**
- * Test that the heartbeat UI correctly works with an invalid, but non null, engagement URL.
- */
-add_UITour_task(async function test_heartbeat_invalid_engagement_URL() {
-  let flowId = "ui-ratefirefox-" + Math.random();
-  let originalTabCount = gBrowser.tabs.length;
-  let invalidEngagementURL = "invalidEngagement";
-
-  // We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
-  // in UITour-lib.js, otherwise no message will get propagated.
-  gContentAPI.observe(() => {});
-
-  let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
-    "Heartbeat:NotificationClosed", "Heartbeat:Voted", "Heartbeat:TelemetrySent"]);
-
-  // Show the Heartbeat notification and wait for it to be displayed.
-  let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
-  gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, invalidEngagementURL);
-
-  // Validate the returned timestamp.
-  let data = await shownPromise;
-  validateTimestamp("Heartbeat:Offered", data.timestamp);
-
-  // Wait an the Voted, Closed and Telemetry Sent events. They are fired together, so
-  // wait for them here.
-  let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
-  let votedPromise = promiseWaitHeartbeatNotification("Heartbeat:Voted");
-  let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
-
-  // The UI was just shown. We can simulate a click on a rating element (i.e., "star").
-  simulateVote(flowId, 2);
-  data = await votedPromise;
-  validateTimestamp("Heartbeat:Voted", data.timestamp);
-
-  // Validate the closing timestamp.
-  data = await closedPromise;
-  validateTimestamp("Heartbeat:NotificationClosed", data.timestamp);
-  is(gBrowser.tabs.length, originalTabCount, "No engagement tab should be opened.");
-
-  // Validate the data we send out.
-  data = await pingSentPromise;
-  info("'Heartbeat:TelemetrySent' notification received.");
-  checkTelemetry(data, flowId, ["offeredTS", "votedTS", "closedTS", "score"]);
-  is(data.score, 2, "Checking Telemetry payload.score");
-
-  // This rejects whenever an unexpected notification is received.
-  await receivedExpectedPromise;
-})
-
-/**
- * Test that the score is correctly reported.
- */
-add_UITour_task(async function test_heartbeat_stars_vote() {
-  const expectedScore = 4;
-  let originalTabCount = gBrowser.tabs.length;
-  let flowId = "ui-ratefirefox-" + Math.random();
-
-  // We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
-  // in UITour-lib.js, otherwise no message will get propagated.
-  gContentAPI.observe(() => {});
-
-  let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
-    "Heartbeat:NotificationClosed", "Heartbeat:Voted", "Heartbeat:TelemetrySent"]);
-
-  // Show the Heartbeat notification and wait for it to be displayed.
-  let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
-  gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, null);
-
-  // Validate the returned timestamp.
-  let data = await shownPromise;
-  validateTimestamp("Heartbeat:Offered", data.timestamp);
-
-  // Wait an the Voted, Closed and Telemetry Sent events. They are fired together, so
-  // wait for them here.
-  let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
-  let votedPromise = promiseWaitHeartbeatNotification("Heartbeat:Voted");
-  let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
-
-  // The UI was just shown. We can simulate a click on a rating element (i.e., "star").
-  simulateVote(flowId, expectedScore);
-  data = await votedPromise;
-  validateTimestamp("Heartbeat:Voted", data.timestamp);
-  is(data.score, expectedScore, "Should report a score of " + expectedScore);
-
-  // Validate the closing timestamp and vote.
-  data = await closedPromise;
-  validateTimestamp("Heartbeat:NotificationClosed", data.timestamp);
-  is(gBrowser.tabs.length, originalTabCount, "No engagement tab should be opened.");
-
-  // Validate the data we send out.
-  data = await pingSentPromise;
-  info("'Heartbeat:TelemetrySent' notification received.");
-  checkTelemetry(data, flowId, ["offeredTS", "votedTS", "closedTS", "score"]);
-  is(data.score, expectedScore, "Checking Telemetry payload.score");
-
-  // This rejects whenever an unexpected notification is received.
-  await receivedExpectedPromise;
-})
-
-/**
- * Test that the engagement page is correctly opened when voting.
- */
-add_UITour_task(async function test_heartbeat_engagement_tab() {
-  let engagementURL = "http://example.com";
-  let flowId = "ui-ratefirefox-" + Math.random();
-  let originalTabCount = gBrowser.tabs.length;
-  const expectedTabCount = originalTabCount + 1;
-
-  // We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
-  // in UITour-lib.js, otherwise no message will get propagated.
-  gContentAPI.observe(() => {});
-
-  let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
-    "Heartbeat:NotificationClosed", "Heartbeat:Voted", "Heartbeat:TelemetrySent"]);
-
-  // Show the Heartbeat notification and wait for it to be displayed.
-  let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
-  gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL);
-
-  // Validate the returned timestamp.
-  let data = await shownPromise;
-  validateTimestamp("Heartbeat:Offered", data.timestamp);
-
-  // Wait an the Voted, Closed and Telemetry Sent events. They are fired together, so
-  // wait for them here.
-  let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
-  let votedPromise = promiseWaitHeartbeatNotification("Heartbeat:Voted");
-  let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
-
-  // The UI was just shown. We can simulate a click on a rating element (i.e., "star").
-  simulateVote(flowId, 1);
-  data = await votedPromise;
-  validateTimestamp("Heartbeat:Voted", data.timestamp);
-
-  // Validate the closing timestamp, vote and make sure the engagement page was opened.
-  data = await closedPromise;
-  validateTimestamp("Heartbeat:NotificationClosed", data.timestamp);
-  is(gBrowser.tabs.length, expectedTabCount, "Engagement URL should open in a new tab.");
-  gBrowser.removeCurrentTab();
-
-  // Validate the data we send out.
-  data = await pingSentPromise;
-  info("'Heartbeat:TelemetrySent' notification received.");
-  checkTelemetry(data, flowId, ["offeredTS", "votedTS", "closedTS", "score"]);
-  is(data.score, 1, "Checking Telemetry payload.score");
-
-  // This rejects whenever an unexpected notification is received.
-  await receivedExpectedPromise;
-})
-
-/**
- * Test that the engagement button opens the engagement URL.
- */
-add_UITour_task(async function test_heartbeat_engagement_button() {
-  let engagementURL = "http://example.com";
-  let flowId = "ui-engagewithfirefox-" + Math.random();
-  let originalTabCount = gBrowser.tabs.length;
-  const expectedTabCount = originalTabCount + 1;
-
-  // We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
-  // in UITour-lib.js, otherwise no message will get propagated.
-  gContentAPI.observe(() => {});
-
-  let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
-    "Heartbeat:NotificationClosed", "Heartbeat:Engaged", "Heartbeat:TelemetrySent"]);
-
-  // Show the Heartbeat notification and wait for it to be displayed.
-  let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
-  gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL, null, null, {
-    engagementButtonLabel: "Engage Me",
-  });
-
-  let data = await shownPromise;
-  validateTimestamp("Heartbeat:Offered", data.timestamp);
-
-  // Wait an the Engaged, Closed and Telemetry Sent events. They are fired together, so
-  // wait for them here.
-  let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
-  let engagedPromise = promiseWaitHeartbeatNotification("Heartbeat:Engaged");
-  let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
-
-  // Simulate user engagement.
-  let notification = getHeartbeatNotification(flowId);
-  is(notification.querySelectorAll(".star-x").length, 0, "No stars should be present");
-  // The UI was just shown. We can simulate a click on the engagement button.
-  let engagementButton = notification.querySelector(".notification-button");
-  is(engagementButton.label, "Engage Me", "Check engagement button text");
-  engagementButton.doCommand();
-
-  data = await engagedPromise;
-  validateTimestamp("Heartbeat:Engaged", data.timestamp);
-
-  // Validate the closing timestamp, vote and make sure the engagement page was opened.
-  data = await closedPromise;
-  validateTimestamp("Heartbeat:NotificationClosed", data.timestamp);
-  is(gBrowser.tabs.length, expectedTabCount, "Engagement URL should open in a new tab.");
-  gBrowser.removeCurrentTab();
-
-  // Validate the data we send out.
-  data = await pingSentPromise;
-  info("'Heartbeat:TelemetrySent' notification received.");
-  checkTelemetry(data, flowId, ["offeredTS", "engagedTS", "closedTS"]);
-
-  // This rejects whenever an unexpected notification is received.
-  await receivedExpectedPromise;
-})
-
-/**
- * Test that the learn more link is displayed and that the page is correctly opened when
- * clicking on it.
- */
-add_UITour_task(async function test_heartbeat_learnmore() {
-  let dummyURL = "http://example.com";
-  let flowId = "ui-ratefirefox-" + Math.random();
-  let originalTabCount = gBrowser.tabs.length;
-  const expectedTabCount = originalTabCount + 1;
-
-  // We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
-  // in UITour-lib.js, otherwise no message will get propagated.
-  gContentAPI.observe(() => {});
-
-  let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
-    "Heartbeat:NotificationClosed", "Heartbeat:LearnMore", "Heartbeat:TelemetrySent"]);
-
-  // Show the Heartbeat notification and wait for it to be displayed.
-  let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
-  gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, dummyURL,
-                            "What is this?", dummyURL);
-
-  let data = await shownPromise;
-  validateTimestamp("Heartbeat:Offered", data.timestamp);
-
-  // Wait an the LearnMore, Closed and Telemetry Sent events. They are fired together, so
-  // wait for them here.
-  let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
-  let learnMorePromise = promiseWaitHeartbeatNotification("Heartbeat:LearnMore");
-  let pingSentPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
-
-  // The UI was just shown. Simulate a click on the learn more link.
-  clickLearnMore(flowId);
-
-  data = await learnMorePromise;
-  validateTimestamp("Heartbeat:LearnMore", data.timestamp);
-  cleanUpNotification(flowId);
-
-  // The notification was closed.
-  data = await closedPromise;
-  validateTimestamp("Heartbeat:NotificationClosed", data.timestamp);
-  is(gBrowser.tabs.length, expectedTabCount, "Learn more URL should open in a new tab.");
-  gBrowser.removeCurrentTab();
-
-  // Validate the data we send out.
-  data = await pingSentPromise;
-  info("'Heartbeat:TelemetrySent' notification received.");
-  checkTelemetry(data, flowId, ["offeredTS", "learnMoreTS", "closedTS"]);
-
-  // This rejects whenever an unexpected notification is received.
-  await receivedExpectedPromise;
-})
-
-add_UITour_task(async 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,
-                            });
-
-  await eventPromise;
-  ok(!isTourBrowser(gBrowser.selectedBrowser),
-     "Invalid engagementButtonLabel should prevent init");
-
-})
-
-add_UITour_task(async 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,
-                            });
-
-  await eventPromise;
-  ok(!isTourBrowser(gBrowser.selectedBrowser),
-     "If there are no private windows opened, tour init should be prevented");
-})
-
-add_UITour_task(async function test_privateWindowsOnly_notMostRecent() {
-  let engagementURL = "http://example.com";
-  let flowId = "notMostRecent-" + Math.random();
-
-  let privateWin = await BrowserTestUtils.openNewBrowserWindow({ private: true });
-  let mostRecentWin = await BrowserTestUtils.openNewBrowserWindow();
-
-  let eventPromise = promisePageEvent();
-
-  gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL,
-                            null, null, {
-                              engagementButtonLabel: "Yes!",
-                              privateWindowsOnly: true,
-                            });
-
-  await eventPromise;
-  is(getHeartbeatNotification(flowId, window), null,
-     "Heartbeat shouldn't appear in the default window");
-  is(!!getHeartbeatNotification(flowId, privateWin), true,
-     "Heartbeat should appear in the most recent private window");
-  is(getHeartbeatNotification(flowId, mostRecentWin), null,
-     "Heartbeat shouldn't appear in the most recent non-private window");
-
-  await BrowserTestUtils.closeWindow(mostRecentWin);
-  await BrowserTestUtils.closeWindow(privateWin);
-})
-
-add_UITour_task(async function test_privateWindowsOnly() {
-  let engagementURL = "http://example.com";
-  let learnMoreURL = "http://example.org/learnmore/";
-  let flowId = "ui-privateWindowsOnly-" + Math.random();
-
-  let privateWin = await BrowserTestUtils.openNewBrowserWindow({ private: true });
-
-  await new Promise((resolve) => {
-    gContentAPI.observe(function(aEventName, aData) {
-      info(aEventName + " notification received: " + JSON.stringify(aData, null, 2));
-      ok(false, "No heartbeat notifications should arrive for privateWindowsOnly");
-    }, resolve);
-  });
-
-  gContentAPI.showHeartbeat("Do you want to engage with us?", "Thank you!", flowId, engagementURL,
-                            "Learn More", learnMoreURL, {
-                              engagementButtonLabel: "Yes!",
-                              privateWindowsOnly: true,
-                            });
-
-  await promisePageEvent();
-
-  ok(isTourBrowser(gBrowser.selectedBrowser), "UITour should have been init for the browser");
-
-  let notification = getHeartbeatNotification(flowId, privateWin);
-
-  is(notification.querySelectorAll(".star-x").length, 0, "No stars should be present");
-
-  info("Test the learn more link.");
-  let learnMoreLink = notification.querySelector(".text-link");
-  is(learnMoreLink.value, "Learn More", "Check learn more label");
-  let learnMoreTabPromise = BrowserTestUtils.waitForNewTab(privateWin.gBrowser, null);
-  learnMoreLink.click();
-  let learnMoreTab = await learnMoreTabPromise;
-  is(learnMoreTab.linkedBrowser.currentURI.host, "example.org", "Check learn more site opened");
-  ok(PrivateBrowsingUtils.isBrowserPrivate(learnMoreTab.linkedBrowser), "Ensure the learn more tab is private");
-  await BrowserTestUtils.removeTab(learnMoreTab);
-
-  info("Test the engagement button's new tab.");
-  let engagementButton = notification.querySelector(".notification-button");
-  is(engagementButton.label, "Yes!", "Check engagement button text");
-  let engagementTabPromise = BrowserTestUtils.waitForNewTab(privateWin.gBrowser, null);
-  engagementButton.doCommand();
-  let engagementTab = await engagementTabPromise;
-  is(engagementTab.linkedBrowser.currentURI.host, "example.com", "Check enagement site opened");
-  ok(PrivateBrowsingUtils.isBrowserPrivate(engagementTab.linkedBrowser), "Ensure the engagement tab is private");
-  await BrowserTestUtils.removeTab(engagementTab);
-
-  await BrowserTestUtils.closeWindow(privateWin);
-})
-
-/**
- * Test that the survey closes itself after a while and submits Telemetry
- */
-add_UITour_task(async function test_telemetry_surveyExpired() {
-  let flowId = "survey-expired-" + Math.random();
-  let engagementURL = "http://example.com";
-  let surveyDuration = 1; // 1 second (pref is in seconds)
-  Services.prefs.setIntPref("browser.uitour.surveyDuration", surveyDuration);
-
-  // We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
-  // in UITour-lib.js, otherwise no message will get propagated.
-  gContentAPI.observe(() => {});
-
-  let receivedExpectedPromise = promiseWaitExpectedNotifications(["Heartbeat:NotificationOffered",
-    "Heartbeat:NotificationClosed", "Heartbeat:SurveyExpired", "Heartbeat:TelemetrySent"]);
-
-  // Show the Heartbeat notification and wait for it to be displayed.
-  let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
-  gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL);
-
-  let expiredPromise = promiseWaitHeartbeatNotification("Heartbeat:SurveyExpired");
-  let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
-  let pingPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
-
-  await Promise.all([shownPromise, expiredPromise, closedPromise]);
-  // Validate the ping data.
-  let data = await pingPromise;
-  checkTelemetry(data, flowId, ["offeredTS", "expiredTS", "closedTS"]);
-
-  Services.prefs.clearUserPref("browser.uitour.surveyDuration");
-
-  // This rejects whenever an unexpected notification is received.
-  await receivedExpectedPromise;
-})
-
-/**
- * Check that certain whitelisted experiment parameters get reflected in the
- * Telemetry ping
- */
-add_UITour_task(async function test_telemetry_params() {
-  let flowId = "telemetry-params-" + Math.random();
-  let engagementURL = "http://example.com";
-  let extraParams = {
-    "surveyId": "foo",
-    "surveyVersion": 1.5,
-    "testing": true,
-    "notWhitelisted": 123,
-  };
-  let expectedFields = ["surveyId", "surveyVersion", "testing"];
-
-  // We need to call |gContentAPI.observe| at least once to set a valid |notificationListener|
-  // in UITour-lib.js, otherwise no message will get propagated.
-  gContentAPI.observe(() => {});
-
-  let receivedExpectedPromise = promiseWaitExpectedNotifications(
-    ["Heartbeat:NotificationOffered", "Heartbeat:NotificationClosed", "Heartbeat:TelemetrySent"]);
-
-  // Show the Heartbeat notification and wait for it to be displayed.
-  let shownPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationOffered");
-  gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!",
-                            flowId, engagementURL, null, null, extraParams);
-  await shownPromise;
-
-  let closedPromise = promiseWaitHeartbeatNotification("Heartbeat:NotificationClosed");
-  let pingPromise = promiseWaitHeartbeatNotification("Heartbeat:TelemetrySent");
-  cleanUpNotification(flowId);
-
-  // The notification was closed.
-  let data = await closedPromise;
-  validateTimestamp("Heartbeat:NotificationClosed", data.timestamp);
-
-  // Validate the data we send out.
-  data = await pingPromise;
-  info("'Heartbeat:TelemetrySent' notification received.");
-  checkTelemetry(data, flowId, ["offeredTS", "closedTS"].concat(expectedFields));
-  for (let param of expectedFields) {
-    is(data[param], extraParams[param],
-       "Whitelisted experiment configs should be copied into Telemetry pings");
-  }
-
-  // This rejects whenever an unexpected notification is received.
-  await receivedExpectedPromise;
-})
--- a/browser/themes/shared/UITour.inc.css
+++ b/browser/themes/shared/UITour.inc.css
@@ -148,146 +148,8 @@
   color: white;
   padding-left: 30px;
   padding-right: 30px;
 }
 
 #UITourTooltipButtons > button.button-primary:not(:active):hover {
   background-color: rgb(105,173,61);
 }
-
-/* Notification overrides for Heartbeat UI */
-
-notification.heartbeat {
-%ifdef XP_MACOSX
-  background-image: linear-gradient(-179deg, #FBFBFB 0%, #EBEBEB 100%);
-%else
-  background-color: #F1F1F1;
-%endif
-  border-bottom: 1px solid #C1C1C1;
-  height: 40px;
-}
-
-/* In themes/osx/global/notification.css the close icon is inverted because notifications
-   on OSX are usually dark. Heartbeat is light, so override that behaviour. */
-
-%ifdef XP_MACOSX
-notification.heartbeat[type="info"] .close-icon:not(:hover) {
-  -moz-image-region: rect(0, 16px, 16px, 0px) !important;
-}
-@media (min-resolution: 2dppx) {
-  notification.heartbeat[type="info"] .close-icon:not(:hover) {
-    -moz-image-region: rect(0, 32px, 32px, 0px) !important;
-  }
-}
-%endif
-
-@keyframes pulse-onshow {
- 0% {
-   opacity: 0;
-   transform: scale(1.0);
- }
- 25% {
-   opacity: 1;
-   transform: scale(1.1);
- }
- 50% {
-   transform: scale(1.0);
- }
- 75% {
-   transform: scale(1.1);
- }
- 100% {
-   transform: scale(1.0);
- }
-}
-
-@keyframes pulse-twice {
- 0% {
-   transform: scale(1.1);
- }
- 50% {
-   transform: scale(0.8);
- }
- 100% {
-   transform: scale(1);
- }
-}
-
-.messageText.heartbeat {
-  color: #333333;
-  text-shadow: none;
-  margin-inline-start: 0px;
-  /* The !important is required to override OSX default style. */
-  margin-inline-end: 12px !important;
-}
-
-.messageImage.heartbeat {
-  width: 24px;
-  height: 24px;
-  margin-inline-start: 8px;
-  margin-inline-end: 8px;
-}
-
-.messageImage.heartbeat.pulse-onshow {
-  animation-name: pulse-onshow;
-  animation-duration: 1.5s;
-  animation-iteration-count: 1;
-  animation-timing-function: cubic-bezier(.7,1.8,.9,1.1);
-}
-
-.messageImage.heartbeat.pulse-twice {
-  animation-name: pulse-twice;
-  animation-duration: 1s;
-  animation-iteration-count: 2;
-  animation-timing-function: linear;
-}
-
-/* Learn More link styles */
-.heartbeat > .text-link {
-  color: #0095DD;
-  margin-inline-start: 0px;
-}
-
-.heartbeat > .text-link:hover {
-  color: #008ACB;
-  text-decoration: none;
-}
-
-.heartbeat > .text-link:hover:active {
-  color: #006B9D;
-}
-
-/* Heartbeat UI Rating Star Classes */
-.heartbeat > #star-rating-container {
-  display: -moz-box;
-  margin-bottom: 4px;
-}
-
-.heartbeat > #star-rating-container > #star5 {
-  -moz-box-ordinal-group: 5;
-}
-
-.heartbeat > #star-rating-container > #star4 {
-  -moz-box-ordinal-group: 4;
-}
-
-.heartbeat > #star-rating-container > #star3 {
-  -moz-box-ordinal-group: 3;
-}
-
-.heartbeat > #star-rating-container > #star2 {
-  -moz-box-ordinal-group: 2;
-}
-
-.heartbeat > #star-rating-container > .star-x  {
-  background: url("chrome://browser/skin/heartbeat-star-off.svg");
-  cursor: pointer;
-  /* Overrides the margin-inline-end for all platforms defined in the .plain class */
-  margin-inline-end: 4px !important;
-  width: 16px;
-  height: 16px;
-}
-
-.heartbeat > #star-rating-container > .star-x:hover,
-.heartbeat > #star-rating-container > .star-x:hover ~ .star-x {
-  background: url("chrome://browser/skin/heartbeat-star-lit.svg");
-}
deleted file mode 100644
--- a/browser/themes/shared/heartbeat-icon.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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/. -->
-<svg width="288px" height="248px" viewBox="0 0 288 248" xmlns="http://www.w3.org/2000/svg">
-  <path fill="#d74345" d="M144,248.571429 C141.214272,248.571429 138.857152,247.607152 136.928571,245.678571 L36.6428571,148.928571 C35.5714232,148.071424 34.0982237,146.678581 32.2232143,144.75 C30.3482049,142.821419 27.3750204,139.312525 23.3035714,134.223214 C19.2321225,129.133903 15.5893018,123.910741 12.375,118.553571 C9.16069821,113.196402 6.29465545,106.714324 3.77678571,99.1071429 C1.25891598,91.499962 0,84.1071788 0,76.9285714 C0,53.357025 6.80350339,34.9286379 20.4107143,21.6428571 C34.0179252,8.35707643 52.8213086,1.71428571 76.8214286,1.71428571 C83.4643189,1.71428571 90.2410369,2.86605991 97.1517857,5.16964286 C104.062535,7.4732258 110.491042,10.5803376 116.4375,14.4910714 C122.383958,18.4018053 127.499979,22.0714114 131.785714,25.5 C136.07145,28.9285886 140.142838,32.5714093 144,36.4285714 C147.857162,32.5714093 151.92855,28.9285886 156.214286,25.5 C160.500021,22.0714114 165.616042,18.4018053 171.5625,14.4910714 C177.508958,10.5803376 183.937465,7.4732258 190.848214,5.16964286 C197.758963,2.86605991 204.535681,1.71428571 211.178571,1.71428571 C235.178691,1.71428571 253.982075,8.35707643 267.589286,21.6428571 C281.196497,34.9286379 288,53.357025 288,76.9285714 C288,100.607261 275.732266,124.714163 251.196429,149.25 L151.071429,245.678571 C149.142847,247.607152 146.785728,248.571429 144,248.571429 L144,248.571429 Z" transform="translate(0,-1)"/>
-  <g transform="translate(0,-0.29)">
-    <mask id="mask" fill="#fff">
-      <path d="M144,246.857143 C141.214272,246.857143 138.857152,245.892867 136.928571,243.964286 L36.6428571,147.214286 C35.5714232,146.357139 34.0982237,144.964295 32.2232143,143.035714 C30.3482049,141.107133 27.3750204,137.59824 23.3035714,132.508929 C19.2321225,127.419617 15.5893018,122.196455 12.375,116.839286 C9.16069821,111.482116 6.29465545,105.000038 3.77678571,97.3928571 C1.25891598,89.7856763 0,82.392893 0,75.2142857 C0,51.6427393 6.80350339,33.2143521 20.4107143,19.9285714 C34.0179252,6.64279071 52.8213086,0 76.8214286,0 C83.4643189,0 90.2410369,1.1517742 97.1517857,3.45535714 C104.062535,5.75894009 110.491042,8.86605187 116.4375,12.7767857 C122.383958,16.6875196 127.499979,20.3571257 131.785714,23.7857143 C136.07145,27.2143029 140.142838,30.8571236 144,34.7142857 C147.857162,30.8571236 151.92855,27.2143029 156.214286,23.7857143 C160.500021,20.3571257 165.616042,16.6875196 171.5625,12.7767857 C177.508958,8.86605187 183.937465,5.75894009 190.848214,3.45535714 C197.758963,1.1517742 204.535681,0 211.178571,0 C235.178691,0 253.982075,6.64279071 267.589286,19.9285714 C281.196497,33.2143521 288,51.6427393 288,75.2142857 C288,98.8929755 275.732266,122.999877 251.196429,147.535714 L151.071429,243.964286 C149.142847,245.892867 146.785728,246.857143 144,246.857143 L144,246.857143 Z"/>
-    </mask>
-    <path fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" mask="url(#mask)" d="M-166,115.135254 C-166,115.135254 0.595052083,115.135254 2.9765625,115.135254 L91.9101562,115.135254 L97.9638977,100.101562 L105.430695,115.135254 L114.893585,115.135254 L131.129913,189.53125 L148.161163,57 L165.348663,131.027344 L172.272491,115.135254 L250.84967,115.135254 L428.259813,115.135254"/>
-  </g>
-</svg>
deleted file mode 100644
--- a/browser/themes/shared/heartbeat-star-lit.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="100%" height="100%">
-  <path fill="#0095dd" d="M8,0C7.7,0,7.4,0.2,7.2,0.7l-2,4.1L0.9,5.5c-1,0.2-1.2,0.9-0.5,1.6l3.1,3.3l-0.7,4.6C2.7,15.6,3,16,3.4,16c0.2,0,0.4-0.1,0.6-0.2L8,13.7l3.9,2.1c0.2,0.1,0.5,0.2,0.6,0.2c0.5,0,0.8-0.4,0.7-1.1l-0.7-4.6l3.1-3.3c0.7-0.7,0.4-1.4-0.5-1.6l-4.3-0.7l-2-4.1C8.6,0.2,8.3,0,8,0L8,0z"/>
-</svg>
deleted file mode 100644
--- a/browser/themes/shared/heartbeat-star-off.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="100%" height="100%">
-  <path fill="#c0c0c0" d="M8,0C7.7,0,7.4,0.2,7.2,0.7l-2,4.1L0.9,5.5c-1,0.2-1.2,0.9-0.5,1.6l3.1,3.3l-0.7,4.6C2.7,15.6,3,16,3.4,16c0.2,0,0.4-0.1,0.6-0.2L8,13.7l3.9,2.1c0.2,0.1,0.5,0.2,0.6,0.2c0.5,0,0.8-0.4,0.7-1.1l-0.7-4.6l3.1-3.3c0.7-0.7,0.4-1.4-0.5-1.6l-4.3-0.7l-2-4.1C8.6,0.2,8.3,0,8,0L8,0z"/>
-</svg>
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -51,19 +51,16 @@
   skin/classic/browser/customizableui/whimsy.png               (../shared/customizableui/whimsy.png)
   skin/classic/browser/customizableui/whimsy@2x.png            (../shared/customizableui/whimsy@2x.png)
   skin/classic/browser/downloads/contentAreaDownloadsView.css  (../shared/downloads/contentAreaDownloadsView.css)
   skin/classic/browser/downloads/download-blocked.svg          (../shared/downloads/download-blocked.svg)
   skin/classic/browser/downloads/download-summary.svg          (../shared/downloads/download-summary.svg)
   skin/classic/browser/drm-icon.svg                            (../shared/drm-icon.svg)
   skin/classic/browser/fullscreen/insecure.svg                 (../shared/fullscreen/insecure.svg)
   skin/classic/browser/fullscreen/secure.svg                   (../shared/fullscreen/secure.svg)
-  skin/classic/browser/heartbeat-icon.svg                      (../shared/heartbeat-icon.svg)
-  skin/classic/browser/heartbeat-star-lit.svg                  (../shared/heartbeat-star-lit.svg)
-  skin/classic/browser/heartbeat-star-off.svg                  (../shared/heartbeat-star-off.svg)
   skin/classic/browser/connection-secure.svg                   (../shared/identity-block/connection-secure.svg)
   skin/classic/browser/connection-mixed-passive-loaded.svg     (../shared/identity-block/connection-mixed-passive-loaded.svg)
   skin/classic/browser/connection-mixed-active-loaded.svg      (../shared/identity-block/connection-mixed-active-loaded.svg)
   skin/classic/browser/identity-icon.svg                       (../shared/identity-block/identity-icon.svg)
   skin/classic/browser/identity-icon-hover.svg                 (../shared/identity-block/identity-icon-hover.svg)
   skin/classic/browser/identity-icon-notice.svg                (../shared/identity-block/identity-icon-notice.svg)
   skin/classic/browser/identity-icon-notice-hover.svg          (../shared/identity-block/identity-icon-notice-hover.svg)
   skin/classic/browser/info.svg                                (../shared/info.svg)