Bug 1509888: Remove Browser Error Collection. r=Gijs,flod
authorMichael Kelly <mkelly@mozilla.com>
Tue, 15 Jan 2019 18:04:28 +0000
changeset 453955 a51d26029042fb10cca386c3150fc2c912a442a2
parent 453954 3965ba4c93b0abad99169221725928084d7f759e
child 453956 2bf2c209f520c75405b70ed5a5a12c2a8938dffe
child 453957 c7f31b684841a1302d98bbe0e45415d15668bd6f
push id35380
push userdluca@mozilla.com
push dateTue, 15 Jan 2019 22:13:12 +0000
treeherdermozilla-central@a51d26029042 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs, flod
bugs1509888
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1509888: Remove Browser Error Collection. r=Gijs,flod Differential Revision: https://phabricator.services.mozilla.com/D15879
browser/app/profile/firefox.js
browser/components/nsBrowserGlue.js
browser/components/preferences/in-content/privacy.js
browser/components/preferences/in-content/privacy.xul
browser/components/preferences/in-content/tests/browser.ini
browser/components/preferences/in-content/tests/browser_privacypane.js
browser/docs/BrowserErrorReporter.rst
browser/docs/index.rst
browser/locales/en-US/browser/preferences/preferences.ftl
browser/modules/BrowserErrorReporter.jsm
browser/modules/moz.build
browser/modules/test/browser/browser.ini
browser/modules/test/browser/browser_BrowserErrorReporter.html
browser/modules/test/browser/browser_BrowserErrorReporter.js
browser/modules/test/browser/browser_BrowserErrorReporter_nightly.js
browser/modules/test/browser/head_BrowserErrorReporter.js
toolkit/components/telemetry/Scalars.yaml
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1730,29 +1730,16 @@ pref("browser.sessionstore.restore_tabs_
 pref("browser.suppress_first_window_animation", true);
 
 // Preference that allows individual users to disable Screenshots.
 pref("extensions.screenshots.disabled", false);
 // Preference that allows individual users to leave Screenshots enabled, but
 // disable uploading to the server.
 pref("extensions.screenshots.upload-disabled", false);
 
-// Preferences for BrowserErrorReporter.jsm
-// Only collect errors on Nightly, and specifically not local builds
-#if defined(NIGHTLY_BUILD) && MOZ_UPDATE_CHANNEL != default
-pref("browser.chrome.errorReporter.enabled", true);
-#else
-pref("browser.chrome.errorReporter.enabled", false);
-#endif
-pref("browser.chrome.errorReporter.sampleRate", "0.001");
-pref("browser.chrome.errorReporter.publicKey", "c709cb7a2c0b4f0882fcc84a5af161ec");
-pref("browser.chrome.errorReporter.projectId", "339");
-pref("browser.chrome.errorReporter.submitUrl", "https://sentry.prod.mozaws.net/api/339/store/");
-pref("browser.chrome.errorReporter.logLevel", "Error");
-
 // URL for Learn More link for browser error logging in preferences
 pref("browser.chrome.errorReporter.infoURL",
      "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/nightly-error-collection");
 
 // Normandy client preferences
 pref("app.normandy.api_url", "https://normandy.cdn.mozilla.net/api/v1");
 pref("app.normandy.dev_mode", false);
 pref("app.normandy.enabled", true);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -379,17 +379,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   AddonManager: "resource://gre/modules/AddonManager.jsm",
   AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.jsm",
   AsyncPrefs: "resource://gre/modules/AsyncPrefs.jsm",
   AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
   AutoCompletePopup: "resource://gre/modules/AutoCompletePopup.jsm",
   Blocklist: "resource://gre/modules/Blocklist.jsm",
   BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.jsm",
   BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.jsm",
-  BrowserErrorReporter: "resource:///modules/BrowserErrorReporter.jsm",
   BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   ContentClick: "resource:///modules/ContentClick.jsm",
   ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
   CustomizableUI: "resource:///modules/CustomizableUI.jsm",
   DateTimePickerParent: "resource://gre/modules/DateTimePickerParent.jsm",
   Discovery: "resource:///modules/Discovery.jsm",
   ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
@@ -653,26 +652,16 @@ BrowserGlue.prototype = {
   get pingCentre() {
     const MAIN_TOPIC_ID = "main";
     Object.defineProperty(this, "pingCentre", {
       value: new PingCentre({ topic: MAIN_TOPIC_ID }),
     });
     return this.pingCentre;
   },
 
-  /**
-   * Lazily initialize BrowserErrorReporter
-   */
-  get browserErrorReporter() {
-    Object.defineProperty(this, "browserErrorReporter", {
-      value: new BrowserErrorReporter(),
-    });
-    return this.browserErrorReporter;
-  },
-
   _sendMainPingCentrePing() {
     let newTabSetting;
     let homePageSetting;
 
     // Check whether or not about:home and about:newtab have been overridden at this point.
     // Different settings are encoded as follows:
     //   * Value 0: default
     //   * Value 1: about:blank
@@ -1450,22 +1439,16 @@ BrowserGlue.prototype = {
     }
 
     PageThumbs.uninit();
     NewTabUtils.uninit();
     AboutPrivateBrowsingHandler.uninit();
     AutoCompletePopup.uninit();
     DateTimePickerParent.uninit();
 
-    // Browser errors are only collected on Nightly, but telemetry for
-    // them is collected on all channels.
-    if (AppConstants.MOZ_DATA_REPORTING) {
-      this.browserErrorReporter.uninit();
-    }
-
     Normandy.uninit();
   },
 
   // Set up a listener to enable/disable the screenshots extension
   // based on its preference.
   _monitorScreenshotsPref() {
     const PREF = "extensions.screenshots.disabled";
     const ID = "screenshots@mozilla.org";
@@ -1498,22 +1481,16 @@ BrowserGlue.prototype = {
 
   // All initial windows have opened.
   _onWindowsRestored: function BG__onWindowsRestored() {
     if (this._windowsWereRestored) {
       return;
     }
     this._windowsWereRestored = true;
 
-    // Browser errors are only collected on Nightly, but telemetry for
-    // them is collected on all channels.
-    if (AppConstants.MOZ_DATA_REPORTING) {
-      this.browserErrorReporter.init();
-    }
-
     BrowserUsageTelemetry.init();
     SearchTelemetry.init();
 
     // Show update notification, if needed.
     if (Services.prefs.prefHasUserValue("app.update.postupdate"))
       this._showUpdateNotification();
 
     ExtensionsUI.init();
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -125,19 +125,16 @@ if (AppConstants.MOZ_DATA_REPORTING) {
     // Preference instances for prefs that we need to monitor while the page is open.
     { id: PREF_OPT_OUT_STUDIES_ENABLED, type: "bool" },
     { id: PREF_ADDON_RECOMMENDATIONS_ENABLED, type: "bool" },
     { id: PREF_UPLOAD_ENABLED, type: "bool" },
   ]);
 }
 
 // Data Choices tab
-if (AppConstants.NIGHTLY_BUILD) {
-  Preferences.add({ id: "browser.chrome.errorReporter.enabled", type: "bool" });
-}
 if (AppConstants.MOZ_CRASHREPORTER) {
   Preferences.add({ id: "browser.crashReports.unsubmittedCheck.autoSubmit2", type: "bool" });
 }
 
 function setEventListener(aId, aEventType, aCallback) {
   document.getElementById(aId)
     .addEventListener(aEventType, aCallback.bind(gPrivacyPane));
 }
@@ -401,19 +398,16 @@ var gPrivacyPane = {
     if (!emeUIEnabled) {
       // Don't want to rely on .hidden for the toplevel groupbox because
       // of the pane hiding/showing code potentially interfering:
       document.getElementById("drmGroup").setAttribute("style", "display: none !important");
     }
 
     if (AppConstants.MOZ_DATA_REPORTING) {
       this.initDataCollection();
-      if (AppConstants.NIGHTLY_BUILD) {
-        this.initCollectBrowserErrors();
-      }
       if (AppConstants.MOZ_CRASHREPORTER) {
         this.initSubmitCrashes();
       }
       this.initSubmitHealthReport();
       setEventListener("submitHealthReportBox", "command",
         gPrivacyPane.updateSubmitHealthReport);
       this.initOptOutStudyCheckbox();
       this.initAddonRecommendationsCheckbox();
@@ -1480,21 +1474,16 @@ var gPrivacyPane = {
     gSubDialog.open("chrome://pippki/content/device_manager.xul");
   },
 
   initDataCollection() {
     this._setupLearnMoreLink("toolkit.datacollection.infoURL",
       "dataCollectionPrivacyNotice");
   },
 
-  initCollectBrowserErrors() {
-    this._setupLearnMoreLink("browser.chrome.errorReporter.infoURL",
-      "collectBrowserErrorsLearnMore");
-  },
-
   initSubmitCrashes() {
     this._setupLearnMoreLink("toolkit.crashreporter.infoURL",
       "crashReporterLearnMore");
   },
 
   /**
    * Set up or hide the Learn More links for various data collection options
    */
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -682,28 +682,16 @@
       </vbox>
     </description>
 #ifndef MOZ_TELEMETRY_REPORTING
   <description id="TelemetryDisabledDesc"
     class="indent tip-caption" control="telemetryGroup"
     data-l10n-id="collection-health-report-disabled"/>
 #endif
 
-#ifdef NIGHTLY_BUILD
-    <hbox align="center">
-      <checkbox id="collectBrowserErrorsBox"
-                class="tail-with-learn-more"
-                preference="browser.chrome.errorReporter.enabled"
-                data-l10n-id="collection-browser-errors"
-                flex="1"/>
-      <label id="collectBrowserErrorsLearnMore"
-             class="learnMore text-link" data-l10n-id="collection-browser-errors-link"/>
-    </hbox>
-#endif
-
 #ifdef MOZ_CRASHREPORTER
     <hbox align="center">
       <checkbox id="automaticallySubmitCrashesBox"
                 class="tail-with-learn-more"
                 preference="browser.crashReports.unsubmittedCheck.autoSubmit2"
                 data-l10n-id="collection-backlogged-crash-reports"
                 flex="1"/>
       <label id="crashReporterLearnMore"
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -65,21 +65,16 @@ skipif = ccov && os == 'win' # bug 14370
 [browser_performance.js]
 skip-if = !e10s
 [browser_performance_e10srollout.js]
 skip-if = !e10s
 [browser_performance_non_e10s.js]
 skip-if = e10s
 [browser_permissions_urlFieldHidden.js]
 [browser_proxy_backup.js]
-[browser_privacypane.js]
-run-if = nightly_build
-# browser_privacypane.js only has Browser Error collection tests currently,
-# which is disabled outside Nightly. Remove this once non-Nightly tests are
-# added.
 [browser_privacypane_2.js]
 [browser_privacypane_3.js]
 [browser_sanitizeOnShutdown_prefLocked.js]
 [browser_searchShowSuggestionsFirst.js]
 [browser_searchsuggestions.js]
 [browser_security-1.js]
 [browser_security-2.js]
 [browser_spotlight.js]
deleted file mode 100644
--- a/browser/components/preferences/in-content/tests/browser_privacypane.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-// Test the initial value of Browser Error collection checkbox
-add_task(async function testBrowserErrorInitialValue() {
-  // Skip if non-Nightly since the checkbox will be missing.
-  if (!AppConstants.NIGHTLY_BUILD) {
-    return;
-  }
-
-  await SpecialPowers.pushPrefEnv({
-    set: [["browser.chrome.errorReporter.enabled", true]],
-  });
-  await openPreferencesViaOpenPreferencesAPI("privacy-reports", {leaveOpen: true});
-
-  let doc = gBrowser.contentDocument;
-  ok(
-    doc.querySelector("#collectBrowserErrorsBox").checked,
-    "Checkbox for collecting browser errors should be checked when the pref is true"
-  );
-
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
-  await SpecialPowers.popPrefEnv();
-});
-
-// Test that the Learn More link is set to the correct, formatted URL from a
-// pref value
-add_task(async function testBrowserErrorLearnMore() {
-  // Skip if non-Nightly since the checkbox will be missing.
-  if (!AppConstants.NIGHTLY_BUILD) {
-    return;
-  }
-
-  await SpecialPowers.pushPrefEnv({
-    set: [["browser.chrome.errorReporter.infoURL", "https://example.com/%NAME%/"]],
-  });
-  await openPreferencesViaOpenPreferencesAPI("privacy-reports", {leaveOpen: true});
-
-  let doc = gBrowser.contentDocument;
-  is(
-    doc.querySelector("#collectBrowserErrorsLearnMore").href,
-    `https://example.com/${Services.appinfo.name}/`,
-    "Learn More link for browser error collection should have an href set by a pref"
-  );
-
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
-  await SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/browser/docs/BrowserErrorReporter.rst
+++ /dev/null
@@ -1,48 +0,0 @@
-.. _browsererrorreporter:
-
-=======================
-Browser Error Reporter
-=======================
-
-The `BrowserErrorReporter.jsm <https://dxr.mozilla.org/mozilla-central/source/browser/modules/BrowserErrorReporter.jsm>`_ module collects errors logged to the Browser Console and sends them to a remote error aggregation service.
-
-.. note::
-   This module and the related service is a prototype and will be removed from Firefox in later 2018.
-
-Opt-out
-=======
-Collection is enabled by default in the Nightly channel, except for local builds, where it is disabled. It is not available outside of the Nightly channel.
-
-To opt-out of collection:
-
-1. Open ``about:preferences``.
-2. Select the Privacy and Security panel and go to the Nightly Data Collection and Use section.
-3. Uncheck "Allow Nightly to send browser error reports (including error messages) to Mozilla".
-
-Collected Error Data
-====================
-Errors are first sampled at the rate specified by the ``browser.chrome.errorReporter.sampleRate`` preference.
-
-The payload sent to the remote collection service contains the following info:
-
-- Firefox version number
-- Firefox update channel (usually "Nightly")
-- Firefox build ID
-- Revision used to build Firefox
-- Timestamp when the error occurred
-- A project ID specified by the ``browser.chrome.errorReporter.projectId`` preference
-- The error message
-- The filename that the error was thrown from
-- A stacktrace, if available, of the code being executed when the error was thrown
-
-Privacy-sensitive info
-======================
-Error reports may contain sensitive information about the user:
-
-- Error messages may inadvertently contain personal info depending on the code that generated the error.
-- Filenames in the stack trace may contain add-on IDs of currently-installed add-ons. They may also contain local filesystem paths.
-
-.. seealso::
-
-   `Browser Error Collection wiki page <https://wiki.mozilla.org/Firefox/BrowserErrorCollection>`_
-      Wiki page with up-to-date information on error collection and how we restrict access to the collected data.
--- a/browser/docs/index.rst
+++ b/browser/docs/index.rst
@@ -4,9 +4,8 @@ Firefox
 
 This is the nascent documentation of the Firefox front-end code.
 
 .. toctree::
    :maxdepth: 2
 
    AddressBar
    BrowserUsageTelemetry
-   BrowserErrorReporter
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -945,21 +945,16 @@ collection-studies-link = View { -brand-
 addon-recommendations =
     .label = Allow { -brand-short-name } to make personalized extension recommendations
 addon-recommendations-link = Learn more
 
 # This message is displayed above disabled data sharing options in developer builds
 # or builds with no Telemetry support available.
 collection-health-report-disabled = Data reporting is disabled for this build configuration
 
-collection-browser-errors =
-    .label = Allow { -brand-short-name } to send browser error reports (including error messages) to { -vendor-short-name }
-    .accesskey = b
-collection-browser-errors-link = Learn more
-
 collection-backlogged-crash-reports =
     .label = Allow { -brand-short-name } to send backlogged crash reports on your behalf
     .accesskey = c
 collection-backlogged-crash-reports-link = Learn more
 
 ## Privacy Section - Security
 ##
 ## It is important that wording follows the guidelines outlined on this page:
deleted file mode 100644
--- a/browser/modules/BrowserErrorReporter.jsm
+++ /dev/null
@@ -1,474 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/Timer.jsm");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-ChromeUtils.defineModuleGetter(this, "Log", "resource://gre/modules/Log.jsm");
-ChromeUtils.defineModuleGetter(this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm");
-
-XPCOMUtils.defineLazyGlobalGetters(this, ["fetch", "URL"]);
-
-var EXPORTED_SYMBOLS = ["BrowserErrorReporter"];
-
-const CONTEXT_LINES = 5;
-const ERROR_PREFIX_RE = /^[^\W]+:/m;
-const PREF_ENABLED = "browser.chrome.errorReporter.enabled";
-const PREF_LOG_LEVEL = "browser.chrome.errorReporter.logLevel";
-const PREF_PROJECT_ID = "browser.chrome.errorReporter.projectId";
-const PREF_PUBLIC_KEY = "browser.chrome.errorReporter.publicKey";
-const PREF_SAMPLE_RATE = "browser.chrome.errorReporter.sampleRate";
-const PREF_SUBMIT_URL = "browser.chrome.errorReporter.submitUrl";
-const RECENT_BUILD_AGE = 1000 * 60 * 60 * 24 * 7; // 7 days
-const SDK_NAME = "firefox-error-reporter";
-const SDK_VERSION = "1.0.0";
-const TELEMETRY_ERROR_COLLECTED = "browser.errors.collected_count";
-const TELEMETRY_ERROR_COLLECTED_FILENAME = "browser.errors.collected_count_by_filename";
-const TELEMETRY_ERROR_COLLECTED_STACK = "browser.errors.collected_with_stack_count";
-const TELEMETRY_ERROR_REPORTED = "browser.errors.reported_success_count";
-const TELEMETRY_ERROR_REPORTED_FAIL = "browser.errors.reported_failure_count";
-const TELEMETRY_ERROR_SAMPLE_RATE = "browser.errors.sample_rate";
-
-
-// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIScriptError#Categories
-const REPORTED_CATEGORIES = new Set([
-  "XPConnect JavaScript",
-  "component javascript",
-  "chrome javascript",
-  "chrome registration",
-  "XBL",
-  "XBL Prototype Handler",
-  "XBL Content Sink",
-  "xbl javascript",
-  "FrameConstructor",
-]);
-
-const PLATFORM_NAMES = {
-  linux: "Linux",
-  win: "Windows",
-  macosx: "macOS",
-  android: "Android",
-};
-
-// Filename URI regexes that we are okay with reporting to Telemetry. URIs not
-// matching these patterns may contain local file paths.
-const TELEMETRY_REPORTED_PATTERNS = new Set([
-  /^resource:\/\/(?:\/|gre|devtools)/,
-  /^chrome:\/\/(?:global|browser|devtools)/,
-]);
-
-// Mapping of regexes to sample rates; if the regex matches the module an error
-// is thrown from, the matching sample rate is used instead of the default.
-// In case of a conflict, the first matching rate by insertion order is used.
-const MODULE_SAMPLE_RATES = new Map([
-  [/^(?:chrome|resource):\/\/devtools/, 1],
-  [/^moz-extension:\/\//, 0],
-]);
-
-/**
- * Collects nsIScriptError messages logged to the browser console and reports
- * them to a remotely-hosted error collection service.
- *
- * This is a PROTOTYPE; it will be removed in the future and potentially
- * replaced with a more robust implementation. It is meant to only collect
- * errors from Nightly (and local builds if enabled for development purposes)
- * and has not been reviewed for use outside of Nightly.
- *
- * The outgoing requests are designed to be compatible with Sentry. See
- * https://docs.sentry.io/clientdev/ for details on the data format that Sentry
- * expects.
- *
- * Errors may contain PII, such as in messages or local file paths in stack
- * traces; see bug 1426482 for privacy review and server-side mitigation.
- */
-class BrowserErrorReporter {
-  /**
-   * Generate a Date object corresponding to the date in the appBuildId.
-   */
-  static getAppBuildIdDate() {
-    const appBuildId = Services.appinfo.appBuildID;
-    const buildYear = Number.parseInt(appBuildId.slice(0, 4));
-    // Date constructor uses 0-indexed months
-    const buildMonth = Number.parseInt(appBuildId.slice(4, 6)) - 1;
-    const buildDay = Number.parseInt(appBuildId.slice(6, 8));
-    return new Date(buildYear, buildMonth, buildDay);
-  }
-
-  constructor(options = {}) {
-    // Test arguments for mocks and changing behavior
-    const defaultOptions = {
-      fetch: defaultFetch,
-      now: null,
-      chromeOnly: true,
-      sampleRates: MODULE_SAMPLE_RATES,
-      registerListener: () => Services.console.registerListener(this),
-      unregisterListener: () => Services.console.unregisterListener(this),
-    };
-    for (const [key, defaultValue] of Object.entries(defaultOptions)) {
-      this[key] = key in options ? options[key] : defaultValue;
-    }
-
-    XPCOMUtils.defineLazyGetter(this, "appBuildIdDate", BrowserErrorReporter.getAppBuildIdDate);
-
-    // Values that don't change between error reports.
-    this.requestBodyTemplate = {
-      logger: "javascript",
-      platform: "javascript",
-      release: Services.appinfo.appBuildID,
-      environment: UpdateUtils.getUpdateChannel(false),
-      contexts: {
-        os: {
-          name: PLATFORM_NAMES[AppConstants.platform],
-          version: (
-            Cc["@mozilla.org/network/protocol;1?name=http"]
-            .getService(Ci.nsIHttpProtocolHandler)
-            .oscpu
-          ),
-        },
-        browser: {
-          name: "Firefox",
-          version: Services.appinfo.version,
-        },
-      },
-      tags: {
-        changeset: AppConstants.SOURCE_REVISION_URL,
-      },
-      sdk: {
-        name: SDK_NAME,
-        version: SDK_VERSION,
-      },
-    };
-
-    XPCOMUtils.defineLazyPreferenceGetter(
-      this,
-      "collectionEnabled",
-      PREF_ENABLED,
-      false,
-      this.handleEnabledPrefChanged.bind(this),
-    );
-    XPCOMUtils.defineLazyPreferenceGetter(
-      this,
-      "sampleRatePref",
-      PREF_SAMPLE_RATE,
-      "0.0",
-      this.handleSampleRatePrefChanged.bind(this),
-    );
-
-    // Prefix mappings for the mangleFilePaths transform.
-    this.manglePrefixes = options.manglePrefixes || {
-      greDir: Services.dirsvc.get("GreD", Ci.nsIFile),
-      profileDir: Services.dirsvc.get("ProfD", Ci.nsIFile),
-    };
-    // File paths are encoded by nsIURI, so let's do the same for the prefixes
-    // we're comparing them to.
-    for (const [name, prefixFile] of Object.entries(this.manglePrefixes)) {
-      let filePath = Services.io.newFileURI(prefixFile).filePath;
-
-      // filePath might not have a trailing slash in some cases
-      if (!filePath.endsWith("/")) {
-        filePath += "/";
-      }
-
-      this.manglePrefixes[name] = filePath;
-    }
-  }
-
-  /**
-   * Lazily-created logger
-   */
-  get logger() {
-    const logger = Log.repository.getLogger("BrowserErrorReporter");
-    logger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
-    logger.manageLevelFromPref(PREF_LOG_LEVEL);
-
-    Object.defineProperty(this, "logger", {value: logger});
-    return this.logger;
-  }
-
-  init() {
-    if (this.collectionEnabled) {
-      this.registerListener();
-
-      // Processing already-logged messages in case any errors occurred before
-      // startup.
-      for (const message of Services.console.getMessageArray()) {
-        this.observe(message);
-      }
-    }
-  }
-
-  uninit() {
-    try {
-      this.unregisterListener();
-    } catch (err) {} // It probably wasn't registered.
-  }
-
-  handleEnabledPrefChanged(prefName, previousValue, newValue) {
-    if (newValue) {
-      this.registerListener();
-    } else {
-      try {
-        this.unregisterListener();
-      } catch (err) {} // It probably wasn't registered.
-    }
-  }
-
-  handleSampleRatePrefChanged(prefName, previousValue, newValue) {
-    Services.telemetry.scalarSet(TELEMETRY_ERROR_SAMPLE_RATE, newValue);
-  }
-
-  errorCollectedFilenameKey(filename) {
-    for (const pattern of TELEMETRY_REPORTED_PATTERNS) {
-      if (filename.match(pattern)) {
-        return filename;
-      }
-    }
-
-    // WebExtensions get grouped separately from other errors
-    if (filename.startsWith("moz-extension://")) {
-        return "MOZEXTENSION";
-    }
-
-    return "FILTERED";
-  }
-
-  isRecentBuild() {
-    // The local clock is not reliable, but this method doesn't need to be
-    // perfect.
-    const now = this.now || new Date();
-    return (now - this.appBuildIdDate) <= RECENT_BUILD_AGE;
-  }
-
-  observe(message) {
-    if (message instanceof Ci.nsIScriptError) {
-      ChromeUtils.idleDispatch(() => this.handleMessage(message));
-    }
-  }
-
-  async handleMessage(message) {
-    const isWarning = message.flags & message.warningFlag;
-    const isFromChrome = REPORTED_CATEGORIES.has(message.category);
-    if ((this.chromeOnly && !isFromChrome) || isWarning) {
-      return;
-    }
-
-    // Record that we collected an error prior to applying the sample rate
-    Services.telemetry.scalarAdd(TELEMETRY_ERROR_COLLECTED, 1);
-    if (message.stack) {
-      Services.telemetry.scalarAdd(TELEMETRY_ERROR_COLLECTED_STACK, 1);
-    }
-    if (message.sourceName) {
-      const key = this.errorCollectedFilenameKey(message.sourceName);
-      Services.telemetry.keyedScalarAdd(TELEMETRY_ERROR_COLLECTED_FILENAME, key.slice(0, 69), 1);
-    }
-
-    // We do not collect errors on non-Nightly channels, just telemetry.
-    // Also, old builds should not send errors to Sentry
-    if (!AppConstants.NIGHTLY_BUILD || !this.isRecentBuild()) {
-      return;
-    }
-
-    // Sample the amount of errors we send out
-    let sampleRate = Number.parseFloat(this.sampleRatePref);
-    for (const [regex, rate] of this.sampleRates) {
-      if (message.sourceName.match(regex)) {
-        sampleRate = rate;
-        break;
-      }
-    }
-    if (!Number.isFinite(sampleRate) || (Math.random() >= sampleRate)) {
-      return;
-    }
-
-    const exceptionValue = {};
-    const requestBody = {
-      ...this.requestBodyTemplate,
-      timestamp: new Date().toISOString().slice(0, -1), // Remove trailing "Z"
-      project: Services.prefs.getCharPref(PREF_PROJECT_ID),
-      exception: {
-        values: [exceptionValue],
-      },
-    };
-
-    const transforms = [
-      addErrorMessage,
-      addStacktrace,
-      addModule,
-      mangleExtensionUrls,
-      this.mangleFilePaths.bind(this),
-      tagExtensionErrors,
-    ];
-    for (const transform of transforms) {
-      await transform(message, exceptionValue, requestBody);
-    }
-
-    const url = new URL(Services.prefs.getCharPref(PREF_SUBMIT_URL));
-    url.searchParams.set("sentry_client", `${SDK_NAME}/${SDK_VERSION}`);
-    url.searchParams.set("sentry_version", "7");
-    url.searchParams.set("sentry_key", Services.prefs.getCharPref(PREF_PUBLIC_KEY));
-
-    try {
-      await this.fetch(url, {
-        method: "POST",
-        headers: {
-          "Content-Type": "application/json",
-          "Accept": "application/json",
-        },
-        // Sentry throws an auth error without a referrer specified.
-        referrer: "https://fake.mozilla.org",
-        body: JSON.stringify(requestBody),
-      });
-      Services.telemetry.scalarAdd(TELEMETRY_ERROR_REPORTED, 1);
-      this.logger.debug(`Sent error "${message.errorMessage}" successfully.`);
-    } catch (error) {
-      Services.telemetry.scalarAdd(TELEMETRY_ERROR_REPORTED_FAIL, 1);
-      this.logger.warn(`Failed to send error "${message.errorMessage}": ${error}`);
-    }
-  }
-
-  /**
-   * Alters file: and jar: paths to remove leading file paths that may contain
-   * user-identifying or platform-specific paths.
-   *
-   * prefixes is a mapping of replacementName -> filePath, where filePath is a
-   * path on the filesystem that should be replaced, and replacementName is the
-   * text that will replace it.
-   */
-  mangleFilePaths(message, exceptionValue) {
-    exceptionValue.module = this._transformFilePath(exceptionValue.module);
-    for (const frame of exceptionValue.stacktrace.frames) {
-      frame.module = this._transformFilePath(frame.module);
-    }
-  }
-
-  _transformFilePath(path) {
-    try {
-      const uri = Services.io.newURI(path);
-      if (uri.schemeIs("jar")) {
-        return uri.filePath;
-      }
-      if (uri.schemeIs("file")) {
-        for (const [name, prefix] of Object.entries(this.manglePrefixes)) {
-          if (uri.filePath.startsWith(prefix)) {
-            return uri.filePath.replace(prefix, `[${name}]/`);
-          }
-        }
-
-        return "[UNKNOWN_LOCAL_FILEPATH]";
-      }
-    } catch (err) {}
-
-    return path;
-  }
-}
-
-function defaultFetch(...args) {
-  // Do not make network requests while running in automation
-  if (Cu.isInAutomation) {
-    return null;
-  }
-
-  return fetch(...args);
-}
-
-function addErrorMessage(message, exceptionValue) {
-  // Parse the error type from the message if present (e.g. "TypeError: Whoops").
-  let errorMessage = message.errorMessage;
-  let errorName = "Error";
-  if (message.errorMessage.match(ERROR_PREFIX_RE)) {
-    const parts = message.errorMessage.split(":");
-    errorName = parts[0];
-    errorMessage = parts.slice(1).join(":").trim();
-  }
-
-  exceptionValue.type = errorName;
-  exceptionValue.value = errorMessage;
-}
-
-async function addStacktrace(message, exceptionValue) {
-  const frames = [];
-  let frame = message.stack;
-  // Avoid an infinite loop by limiting traces to 100 frames.
-  while (frame && frames.length < 100) {
-    const normalizedFrame = {
-      function: frame.functionDisplayName,
-      module: frame.source,
-      lineno: frame.line,
-      colno: frame.column,
-    };
-
-    try {
-      const response = await fetch(frame.source);
-      const sourceCode = await response.text();
-      const sourceLines = sourceCode.split(/\r?\n/);
-      // HTML pages and some inline event handlers have 0 as their line number
-      let lineIndex = Math.max(frame.line - 1, 0);
-
-      // XBL line numbers are off by one, and pretty much every XML file with JS
-      // in it is an XBL file.
-      if (frame.source.endsWith(".xml") && lineIndex > 0) {
-        lineIndex--;
-      }
-
-      normalizedFrame.context_line = sourceLines[lineIndex];
-      normalizedFrame.pre_context = sourceLines.slice(
-        Math.max(lineIndex - CONTEXT_LINES, 0),
-        lineIndex,
-      );
-      normalizedFrame.post_context = sourceLines.slice(
-        lineIndex + 1,
-        Math.min(lineIndex + 1 + CONTEXT_LINES, sourceLines.length),
-      );
-    } catch (err) {
-      // Could be a fetch issue, could be a line index issue. Not much we can
-      // do to recover in either case.
-    }
-
-    frames.push(normalizedFrame);
-    frame = frame.parent;
-  }
-  // Frames are sent in order from oldest to newest.
-  frames.reverse();
-
-  exceptionValue.stacktrace = {frames};
-}
-
-function addModule(message, exceptionValue) {
-  exceptionValue.module = message.sourceName;
-}
-
-function mangleExtensionUrls(message, exceptionValue) {
-  const extensions = new Map();
-  for (let extension of WebExtensionPolicy.getActiveExtensions()) {
-    extensions.set(extension.mozExtensionHostname, extension);
-  }
-
-  // Replaces any instances of moz-extension:// URLs with internal UUIDs to use
-  // the add-on ID instead.
-  function mangleExtURL(string, anchored = true) {
-    if (!string) {
-      return string;
-    }
-
-    const re = new RegExp(`${anchored ? "^" : ""}moz-extension://([^/]+)/`, "g");
-    return string.replace(re, (m0, m1) => {
-      const id = extensions.has(m1) ? extensions.get(m1).id : m1;
-      return `moz-extension://${id}/`;
-    });
-  }
-
-  exceptionValue.value = mangleExtURL(exceptionValue.value, false);
-  exceptionValue.module = mangleExtURL(exceptionValue.module);
-  for (const frame of exceptionValue.stacktrace.frames) {
-    frame.module = mangleExtURL(frame.module);
-  }
-}
-
-function tagExtensionErrors(message, exceptionValue, requestBody) {
-  requestBody.tags.isExtensionError = !!(
-      exceptionValue.module && exceptionValue.module.startsWith("moz-extension://")
-  );
-}
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -120,17 +120,16 @@ BROWSER_CHROME_MANIFESTS += [
     'test/browser/browser.ini',
     'test/browser/formValidation/browser.ini',
 ]
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 
 EXTRA_JS_MODULES += [
     'AboutNewTab.jsm',
     'AsyncTabSwitcher.jsm',
-    'BrowserErrorReporter.jsm',
     'BrowserUsageTelemetry.jsm',
     'BrowserWindowTracker.jsm',
     'ContentClick.jsm',
     'ContentCrashHandlers.jsm',
     'ContentMetaHandler.jsm',
     'ContentObservers.js',
     'ContentSearch.jsm',
     'Discovery.jsm',
--- a/browser/modules/test/browser/browser.ini
+++ b/browser/modules/test/browser/browser.ini
@@ -1,23 +1,14 @@
 [DEFAULT]
 support-files =
   head.js
 prefs =
   browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar=false
 
-[browser_BrowserErrorReporter.js]
-skip-if = (verify && !debug && (os == 'mac' || os == 'win'))
-support-files =
-  head_BrowserErrorReporter.js
-[browser_BrowserErrorReporter_nightly.js]
-skip-if = !nightly_build || (verify && !debug && (os == 'mac' || os == 'win'))
-support-files =
-  head_BrowserErrorReporter.js
-  browser_BrowserErrorReporter.html
 [browser_BrowserWindowTracker.js]
 [browser_ContentSearch.js]
 support-files =
   contentSearch.js
   contentSearchBadImage.xml
   contentSearchSuggestions.sjs
   contentSearchSuggestions.xml
   !/browser/components/search/test/browser/head.js
deleted file mode 100644
--- a/browser/modules/test/browser/browser_BrowserErrorReporter.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <title>Test page</title>
-  </head>
-  <body>
-    <script>
-      // Line and column numbers are significant and used in tests, make sure to
-      // update the tests if you make any changes to this file!
-      function madeToFail() {
-        madeToFail2();
-      }
-      function madeToFail2() {
-        throw new Error("testFetchArguments error");
-      }
-      madeToFail();
-    </script>
-  </body>
-</html>
deleted file mode 100644
--- a/browser/modules/test/browser/browser_BrowserErrorReporter.js
+++ /dev/null
@@ -1,202 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-"use strict";
-
-// This file contains BrowserErrorReporter tests that don't depend on
-// errors being collected, which is only enabled on Nightly builds.
-
-ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
-ChromeUtils.import("resource:///modules/BrowserErrorReporter.jsm", this);
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm", this);
-ChromeUtils.import("resource://gre/modules/FileUtils.jsm", this);
-ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
-
-/* global sinon */
-Services.scriptloader.loadSubScript(new URL("head_BrowserErrorReporter.js", gTestPath).href, this);
-
-add_task(async function testInitPrefDisabled() {
-  let listening = false;
-  const reporter = new BrowserErrorReporter({
-    registerListener() {
-      listening = true;
-    },
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, false],
-  ]});
-
-  reporter.init();
-  ok(!listening, "Reporter does not listen for errors if the enabled pref is false.");
-});
-
-add_task(async function testInitUninitPrefEnabled() {
-  let listening = false;
-  const reporter = new BrowserErrorReporter({
-    registerListener() {
-      listening = true;
-    },
-    unregisterListener() {
-      listening = false;
-    },
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-  ]});
-
-  reporter.init();
-  ok(listening, "Reporter listens for errors if the enabled pref is true.");
-
-  reporter.uninit();
-  ok(!listening, "Reporter does not listen for errors after uninit.");
-});
-
-add_task(async function testEnabledPrefWatcher() {
-  let listening = false;
-  const reporter = new BrowserErrorReporter({
-    registerListener() {
-      listening = true;
-    },
-    unregisterListener() {
-      listening = false;
-    },
-    now: BrowserErrorReporter.getAppBuildIdDate(),
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, false],
-  ]});
-
-  reporter.init();
-  ok(!listening, "Reporter does not collect errors if the enable pref is false.");
-
-  Services.console.logMessage(createScriptError({message: "Shouldn't report"}));
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-  ]});
-  ok(listening, "Reporter collects errors if the enabled pref switches to true.");
-});
-
-add_task(async function testScalars() {
-  // Do not bother testing telemetry scalars if they're already expired.
-  if (SCALARS_EXPIRED) {
-    return;
-  }
-
-  const fetchStub = sinon.stub();
-  const reporter = new BrowserErrorReporter({
-    fetch: fetchStub,
-    sampleRates: new Map(),
-    now: BrowserErrorReporter.getAppBuildIdDate(),
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-
-  Services.telemetry.clearScalars();
-
-  const messages = [
-    createScriptError({message: "No name"}),
-    createScriptError({message: "Also no name", sourceName: "resource://gre/modules/Foo.jsm"}),
-    createScriptError({message: "More no name", sourceName: "resource://gre/modules/Bar.jsm"}),
-    createScriptError({message: "Yeah sures", sourceName: "unsafe://gre/modules/Bar.jsm"}),
-    createScriptError({message: "Addon", sourceName: "moz-extension://foo/Bar.jsm"}),
-    createScriptError({
-      message: "long",
-      sourceName: "resource://gre/modules/long/long/long/long/long/long/long/long/long/long/",
-    }),
-    {message: "Not a scripterror instance."},
-    createScriptError({message: "Whatever", stack: [frame()]}),
-  ];
-
-  // Use handleMessage to avoid errors from other code messing up our counts.
-  for (const message of messages) {
-    await reporter.handleMessage(message);
-  }
-
-  const scalars = Services.telemetry.getSnapshotForScalars("main", false).parent;
-  is(
-    scalars[TELEMETRY_ERROR_COLLECTED],
-    7,
-    `${TELEMETRY_ERROR_COLLECTED} is incremented when an error is collected.`,
-  );
-  is(
-    scalars[TELEMETRY_ERROR_COLLECTED_STACK],
-    1,
-    `${TELEMETRY_ERROR_REPORTED_FAIL} is incremented when an error with a stack trace is collected.`,
-  );
-
-  const keyedScalars = Services.telemetry.getSnapshotForKeyedScalars("main", false).parent;
-  Assert.deepEqual(
-    keyedScalars[TELEMETRY_ERROR_COLLECTED_FILENAME],
-    {
-      "FILTERED": 1,
-      "MOZEXTENSION": 1,
-      "resource://gre/modules/Foo.jsm": 1,
-      "resource://gre/modules/Bar.jsm": 1,
-      // Cut off at 70-character limit
-      "resource://gre/modules/long/long/long/long/long/long/long/long/long/l": 1,
-    },
-    `${TELEMETRY_ERROR_COLLECTED_FILENAME} is incremented when an error is collected.`,
-  );
-
-  resetConsole();
-});
-
-add_task(async function testCollectedFilenameScalar() {
-  // Do not bother testing telemetry scalars if they're already expired.
-  if (SCALARS_EXPIRED) {
-    return;
-  }
-
-  const fetchStub = sinon.stub();
-  const reporter = new BrowserErrorReporter({
-    fetch: fetchStub,
-    now: BrowserErrorReporter.getAppBuildIdDate(),
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-
-  const testCases = [
-    ["chrome://unknown/module.jsm", false],
-    ["resource://unknown/module.jsm", false],
-    ["unknown://unknown/module.jsm", false],
-
-    ["resource://gre/modules/Foo.jsm", true],
-    ["resource:///modules/Foo.jsm", true],
-    ["chrome://global/Foo.jsm", true],
-    ["chrome://browser/Foo.jsm", true],
-    ["chrome://devtools/Foo.jsm", true],
-  ];
-
-  for (const [filename, shouldMatch] of testCases) {
-    Services.telemetry.clearScalars();
-
-    // Use handleMessage to avoid errors from other code messing up our counts.
-    await reporter.handleMessage(createScriptError({
-      message: "Fine",
-      sourceName: filename,
-    }));
-
-    const keyedScalars = (
-      Services.telemetry.getSnapshotForKeyedScalars("main", false).parent
-    );
-
-    let matched = null;
-    if (shouldMatch) {
-      matched = keyedScalars[TELEMETRY_ERROR_COLLECTED_FILENAME][filename] === 1;
-    } else {
-      matched = keyedScalars[TELEMETRY_ERROR_COLLECTED_FILENAME].FILTERED === 1;
-    }
-
-    ok(
-      matched,
-      shouldMatch
-        ? `${TELEMETRY_ERROR_COLLECTED_FILENAME} logs a key for ${filename}.`
-        : `${TELEMETRY_ERROR_COLLECTED_FILENAME} logs a FILTERED key for ${filename}.`,
-    );
-  }
-
-  resetConsole();
-});
deleted file mode 100644
--- a/browser/modules/test/browser/browser_BrowserErrorReporter_nightly.js
+++ /dev/null
@@ -1,548 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-"use strict";
-
-// This file contains BrowserErrorReporter tests that depend on errors
-// being collected, which is only enabled on Nightly builds.
-
-ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
-ChromeUtils.import("resource:///modules/BrowserErrorReporter.jsm", this);
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm", this);
-ChromeUtils.import("resource://gre/modules/FileUtils.jsm", this);
-ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
-
-/* global sinon */
-Services.scriptloader.loadSubScript(new URL("head_BrowserErrorReporter.js", gTestPath).href, this);
-
-add_task(async function testInitPastMessages() {
-  const fetchSpy = sinon.spy();
-  const reporter = new BrowserErrorReporter({
-    fetch: fetchSpy,
-    registerListener: noop,
-    unregisterListener: noop,
-    now: BrowserErrorReporter.getAppBuildIdDate(),
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-
-  resetConsole();
-  Services.console.logMessage(createScriptError({message: "Logged before init"}));
-  reporter.init();
-
-  // Include ok() to satisfy mochitest warning for test without any assertions
-  const errorWasLogged = await TestUtils.waitForCondition(
-    () => fetchPassedError(fetchSpy, "Logged before init"),
-    "Waiting for message to be logged",
-  );
-  ok(errorWasLogged, "Reporter collects errors logged before initialization.");
-
-  reporter.uninit();
-});
-
-add_task(async function testNonErrorLogs() {
-  const fetchSpy = sinon.spy();
-  const reporter = new BrowserErrorReporter({
-    fetch: fetchSpy,
-    now: BrowserErrorReporter.getAppBuildIdDate(),
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-
-  await reporter.handleMessage({message: "Not a scripterror instance."});
-  ok(
-    !fetchPassedError(fetchSpy, "Not a scripterror instance."),
-    "Reporter does not collect normal log messages or warnings.",
-  );
-
-  await reporter.handleMessage(createScriptError({
-    message: "Warning message",
-    flags: Ci.nsIScriptError.warningFlag,
-  }));
-  ok(
-    !fetchPassedError(fetchSpy, "Warning message"),
-    "Reporter does not collect normal log messages or warnings.",
-  );
-
-  await reporter.handleMessage(createScriptError({
-    message: "Non-chrome category",
-    category: "totally from a website",
-  }));
-  ok(
-    !fetchPassedError(fetchSpy, "Non-chrome category"),
-    "Reporter does not collect normal log messages or warnings.",
-  );
-
-  await reporter.handleMessage(createScriptError({message: "Is error"}));
-  ok(
-    fetchPassedError(fetchSpy, "Is error"),
-    "Reporter collects error messages.",
-  );
-});
-
-add_task(async function testSampling() {
-  const fetchSpy = sinon.spy();
-  const reporter = new BrowserErrorReporter({
-    fetch: fetchSpy,
-    now: BrowserErrorReporter.getAppBuildIdDate(),
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-
-  await reporter.handleMessage(createScriptError({message: "Should log"}));
-  ok(
-    fetchPassedError(fetchSpy, "Should log"),
-    "A 1.0 sample rate will cause the reporter to always collect errors.",
-  );
-
-  await reporter.handleMessage(createScriptError({message: "undefined", sourceName: undefined}));
-  ok(
-    fetchPassedError(fetchSpy, "undefined"),
-    "A missing sourceName doesn't break reporting.",
-  );
-
-  await reporter.handleMessage(createScriptError({
-    message: "mozextension",
-    sourceName: "moz-extension://Bar/Foo.jsm",
-  }));
-  ok(
-    !fetchPassedError(fetchSpy, "mozextension"),
-    "moz-extension:// paths are sampled at 0% even if the default rate is 1.0.",
-  );
-
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_SAMPLE_RATE, "0.0"],
-  ]});
-  await reporter.handleMessage(createScriptError({message: "Shouldn't log"}));
-  ok(
-    !fetchPassedError(fetchSpy, "Shouldn't log"),
-    "A 0.0 sample rate will cause the reporter to never collect errors.",
-  );
-
-  await reporter.handleMessage(createScriptError({
-    message: "chromedevtools",
-    sourceName: "chrome://devtools/Foo.jsm",
-  }));
-  ok(
-    fetchPassedError(fetchSpy, "chromedevtools"),
-    "chrome://devtools/ paths are sampled at 100% even if the default rate is 0.0.",
-  );
-
-  await reporter.handleMessage(createScriptError({
-    message: "resourcedevtools",
-    sourceName: "resource://devtools/Foo.jsm",
-  }));
-  ok(
-    fetchPassedError(fetchSpy, "resourcedevtools"),
-    "resource://devtools/ paths are sampled at 100% even if the default rate is 0.0.",
-  );
-
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_SAMPLE_RATE, ")fasdf"],
-  ]});
-  await reporter.handleMessage(createScriptError({message: "Also shouldn't log"}));
-  ok(
-    !fetchPassedError(fetchSpy, "Also shouldn't log"),
-    "An invalid sample rate will cause the reporter to never collect errors.",
-  );
-});
-
-add_task(async function testNameMessage() {
-  const fetchSpy = sinon.spy();
-  const reporter = new BrowserErrorReporter({
-    fetch: fetchSpy,
-    now: BrowserErrorReporter.getAppBuildIdDate(),
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-
-  await reporter.handleMessage(createScriptError({message: "No name"}));
-  let call = fetchCallForMessage(fetchSpy, "No name");
-  let body = JSON.parse(call.args[1].body);
-  is(
-    body.exception.values[0].type,
-    "Error",
-    "Reporter uses a generic type when no name is in the message.",
-  );
-  is(
-    body.exception.values[0].value,
-    "No name",
-    "Reporter uses error message as the exception value.",
-  );
-
-  await reporter.handleMessage(createScriptError({message: "FooError: Has name"}));
-  call = fetchCallForMessage(fetchSpy, "Has name");
-  body = JSON.parse(call.args[1].body);
-  is(
-    body.exception.values[0].type,
-    "FooError",
-    "Reporter uses the error type from the message.",
-  );
-  is(
-    body.exception.values[0].value,
-    "Has name",
-    "Reporter uses error message as the value parameter.",
-  );
-
-  await reporter.handleMessage(createScriptError({message: "FooError: Has :extra: colons"}));
-  call = fetchCallForMessage(fetchSpy, "Has :extra: colons");
-  body = JSON.parse(call.args[1].body);
-  is(
-    body.exception.values[0].type,
-    "FooError",
-    "Reporter uses the error type from the message.",
-  );
-  is(
-    body.exception.values[0].value,
-    "Has :extra: colons",
-    "Reporter uses error message as the value parameter.",
-  );
-});
-
-add_task(async function testRecentBuild() {
-  // Create date that is guaranteed to be a month newer than the build date.
-  const nowDate = BrowserErrorReporter.getAppBuildIdDate();
-  nowDate.setMonth(nowDate.getMonth() + 1);
-
-  const fetchSpy = sinon.spy();
-  const reporter = new BrowserErrorReporter({
-    fetch: fetchSpy,
-    now: nowDate,
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-
-  await reporter.handleMessage(createScriptError({message: "Is error"}));
-  ok(
-    !fetchPassedError(fetchSpy, "Is error"),
-    "Reporter does not collect errors from builds older than a week.",
-  );
-});
-
-add_task(async function testFetchArguments() {
-  const fetchSpy = sinon.spy();
-  const reporter = new BrowserErrorReporter({
-    fetch: fetchSpy,
-    now: BrowserErrorReporter.getAppBuildIdDate(),
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-    [PREF_PROJECT_ID, "123"],
-    [PREF_PUBLIC_KEY, "foobar"],
-    [PREF_SUBMIT_URL, "https://errors.example.com/api/123/store/"],
-  ]});
-
-  resetConsole();
-  reporter.init();
-  const testPageUrl = (
-    "chrome://mochitests/content/browser/browser/modules/test/browser/" +
-    "browser_BrowserErrorReporter.html"
-  );
-
-  SimpleTest.expectUncaughtException();
-  await BrowserTestUtils.withNewTab(testPageUrl, async () => {
-    const call = await TestUtils.waitForCondition(
-      () => fetchCallForMessage(fetchSpy, "testFetchArguments error"),
-      "Wait for error from browser_BrowserErrorReporter.html to be logged",
-    );
-    const body = JSON.parse(call.args[1].body);
-    const url = new URL(call.args[0]);
-
-    is(url.origin, "https://errors.example.com", "Reporter builds API url from DSN pref.");
-    is(url.pathname, "/api/123/store/", "Reporter builds API url from DSN pref.");
-    is(
-      url.searchParams.get("sentry_client"),
-      "firefox-error-reporter/1.0.0",
-      "Reporter identifies itself in the outgoing request",
-    );
-    is(url.searchParams.get("sentry_version"), "7", "Reporter is compatible with Sentry 7.");
-    is(url.searchParams.get("sentry_key"), "foobar", "Reporter pulls API key from DSN pref.");
-    is(body.project, "123", "Reporter pulls project ID from DSN pref.");
-    is(
-      body.tags.changeset,
-      AppConstants.SOURCE_REVISION_URL,
-      "Reporter pulls changeset tag from AppConstants",
-    );
-    is(call.args[1].referrer, "https://fake.mozilla.org", "Reporter uses a fake referer.");
-
-    const response = await fetch(testPageUrl);
-    const pageText = await response.text();
-    const pageLines = pageText.split("\n");
-    Assert.deepEqual(
-      body.exception,
-      {
-        values: [
-          {
-            type: "Error",
-            value: "testFetchArguments error",
-            module: testPageUrl,
-            stacktrace: {
-              frames: [
-                {
-                  function: null,
-                  module: testPageUrl,
-                  lineno: 17,
-                  colno: 7,
-                  pre_context: pageLines.slice(11, 16),
-                  context_line: pageLines[16],
-                  post_context: pageLines.slice(17, 22),
-                },
-                {
-                  function: "madeToFail",
-                  module: testPageUrl,
-                  lineno: 12,
-                  colno: 9,
-                  pre_context: pageLines.slice(6, 11),
-                  context_line: pageLines[11],
-                  post_context: pageLines.slice(12, 17),
-                },
-                {
-                  function: "madeToFail2",
-                  module: testPageUrl,
-                  lineno: 15,
-                  colno: 15,
-                  pre_context: pageLines.slice(9, 14),
-                  context_line: pageLines[14],
-                  post_context: pageLines.slice(15, 20),
-                },
-              ],
-            },
-          },
-        ],
-      },
-      "Reporter builds stack trace from scriptError correctly.",
-    );
-  });
-
-  reporter.uninit();
-});
-
-add_task(async function testAddonIDMangle() {
-  const fetchSpy = sinon.spy();
-  const reporter = new BrowserErrorReporter({
-    fetch: fetchSpy,
-    chromeOnly: false,
-    sampleRates: new Map(),
-    now: BrowserErrorReporter.getAppBuildIdDate(),
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-  resetConsole();
-  reporter.init();
-
-  // Create and install test add-on
-  const id = "browsererrorcollection@example.com";
-  const extension = ExtensionTestUtils.loadExtension({
-    manifest: {
-      applications: {
-        gecko: { id },
-      },
-    },
-    background() {
-      throw new Error("testAddonIDMangle error");
-    },
-  });
-  await extension.startup();
-
-  // Just in case the error hasn't been thrown before add-on startup.
-  const call = await TestUtils.waitForCondition(
-    () => fetchCallForMessage(fetchSpy, "testAddonIDMangle error"),
-    `Wait for error from ${id} to be logged`,
-  );
-  const body = JSON.parse(call.args[1].body);
-  const stackFrame = body.exception.values[0].stacktrace.frames[0];
-  ok(
-    stackFrame.module.startsWith(`moz-extension://${id}/`),
-    "Stack frame filenames use the proper add-on ID instead of internal UUIDs.",
-  );
-
-  await extension.unload();
-  reporter.uninit();
-});
-
-add_task(async function testExtensionTag() {
-  const fetchSpy = sinon.spy();
-  const reporter = new BrowserErrorReporter({
-    fetch: fetchSpy,
-    chromeOnly: false,
-    sampleRates: new Map(),
-    now: BrowserErrorReporter.getAppBuildIdDate(),
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-  resetConsole();
-  reporter.init();
-
-  // Create and install test add-on
-  const id = "browsererrorcollection@example.com";
-  const extension = ExtensionTestUtils.loadExtension({
-    manifest: {
-      applications: {
-        gecko: { id },
-      },
-    },
-    background() {
-      throw new Error("testExtensionTag error");
-    },
-  });
-  await extension.startup();
-
-  // Just in case the error hasn't been thrown before add-on startup.
-  let call = await TestUtils.waitForCondition(
-    () => fetchCallForMessage(fetchSpy, "testExtensionTag error"),
-    `Wait for error from ${id} to be logged`,
-  );
-  let body = JSON.parse(call.args[1].body);
-  ok(body.tags.isExtensionError, "Errors from extensions have an isExtensionError=true tag.");
-
-  await extension.unload();
-  reporter.uninit();
-
-  await reporter.handleMessage(createScriptError({message: "testExtensionTag not from extension"}));
-  call = fetchCallForMessage(fetchSpy, "testExtensionTag not from extension");
-  body = JSON.parse(call.args[1].body);
-  is(body.tags.isExtensionError, false, "Normal errors have an isExtensionError=false tag.");
-});
-
-add_task(async function testScalars() {
-  // Do not bother testing telemetry scalars if they're already expired.
-  if (SCALARS_EXPIRED) {
-    return;
-  }
-
-  const fetchStub = sinon.stub();
-  const reporter = new BrowserErrorReporter({
-    fetch: fetchStub,
-    now: BrowserErrorReporter.getAppBuildIdDate(),
-  });
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-
-  Services.telemetry.clearScalars();
-
-  // Basic count
-  await reporter.handleMessage(createScriptError({message: "No name"}));
-
-  // Sample rate affects counts
-  await SpecialPowers.pushPrefEnv({set: [[PREF_SAMPLE_RATE, "0.0"]]});
-  await reporter.handleMessage(createScriptError({message: "Additionally no name"}));
-
-  // Failed fetches should be counted too
-  await SpecialPowers.pushPrefEnv({set: [[PREF_SAMPLE_RATE, "1.0"]]});
-  fetchStub.rejects(new Error("Could not report"));
-  await reporter.handleMessage(createScriptError({message: "Maybe name?"}));
-
-  const scalars = Services.telemetry.getSnapshotForScalars("main", false).parent;
-  is(
-    scalars[TELEMETRY_ERROR_COLLECTED],
-    3,
-    `${TELEMETRY_ERROR_COLLECTED} is incremented when an error is collected.`,
-  );
-  is(
-    scalars[TELEMETRY_ERROR_SAMPLE_RATE],
-    "1.0",
-    `${TELEMETRY_ERROR_SAMPLE_RATE} contains the last sample rate used.`,
-  );
-  is(
-    scalars[TELEMETRY_ERROR_REPORTED],
-    1,
-    `${TELEMETRY_ERROR_REPORTED} is incremented when an error is reported.`,
-  );
-  is(
-    scalars[TELEMETRY_ERROR_REPORTED_FAIL],
-    1,
-    `${TELEMETRY_ERROR_REPORTED_FAIL} is incremented when an error fails to be reported.`,
-  );
-
-  resetConsole();
-});
-
-add_task(async function testFilePathMangle() {
-  const fetchSpy = sinon.spy();
-  const reporter = new BrowserErrorReporter({fetch: fetchSpy});
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-
-  const greDir = Services.dirsvc.get("GreD", Ci.nsIFile).path;
-  const profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile).path;
-
-  const message = createScriptError({
-    message: "Whatever",
-    sourceName: "file:///path/to/main.jsm",
-    stack: [
-      frame({source: "jar:file:///path/to/jar!/inside/jar.jsm"}),
-      frame({source: `file://${greDir}/defaults/prefs/channel-prefs.js`}),
-      frame({source: `file://${profileDir}/prefs.js`}),
-    ],
-  });
-  await reporter.handleMessage(message);
-
-  const call = fetchCallForMessage(fetchSpy, "Whatever");
-  const body = JSON.parse(call.args[1].body);
-  const exception = body.exception.values[0];
-  is(exception.module, "[UNKNOWN_LOCAL_FILEPATH]", "Unrecognized local file paths are mangled");
-
-  // Stackframe order is reversed from what is in the message.
-  const stackFrames = exception.stacktrace.frames;
-  is(
-    stackFrames[0].module, "[profileDir]/prefs.js",
-    "Paths within the profile directory are preserved but mangled",
-  );
-  is(
-    stackFrames[1].module, "[greDir]/defaults/prefs/channel-prefs.js",
-    "Paths within the GRE directory are preserved but mangled",
-  );
-  is(
-    stackFrames[2].module, "/inside/jar.jsm",
-    "Paths within jarfiles are extracted from the full jar: URL",
-  );
-});
-
-add_task(async function testFilePathMangleWhitespace() {
-  const fetchSpy = sinon.spy();
-
-  const greDir = Services.dirsvc.get("GreD", Ci.nsIFile);
-  const whitespaceDir = greDir.clone();
-  whitespaceDir.append("with whitespace");
-  const manglePrefixes = {
-    whitespace: whitespaceDir,
-  };
-
-  const reporter = new BrowserErrorReporter({fetch: fetchSpy, manglePrefixes});
-  await SpecialPowers.pushPrefEnv({set: [
-    [PREF_ENABLED, true],
-    [PREF_SAMPLE_RATE, "1.0"],
-  ]});
-
-  const message = createScriptError({
-    message: "Whatever",
-    sourceName: `file://${greDir.path}/with whitespace/remaining/file.jsm`,
-  });
-  await reporter.handleMessage(message);
-
-  const call = fetchCallForMessage(fetchSpy, "Whatever");
-  const body = JSON.parse(call.args[1].body);
-  const exception = body.exception.values[0];
-  is(
-    exception.module, "[whitespace]/remaining/file.jsm",
-    "Prefixes with whitespace are correctly mangled",
-  );
-});
deleted file mode 100644
--- a/browser/modules/test/browser/head_BrowserErrorReporter.js
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-"use strict";
-
-/* exported sinon */
-Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
-registerCleanupFunction(function() {
-  delete window.sinon;
-});
-
-const PREF_ENABLED = "browser.chrome.errorReporter.enabled";
-const PREF_PROJECT_ID = "browser.chrome.errorReporter.projectId";
-const PREF_PUBLIC_KEY = "browser.chrome.errorReporter.publicKey";
-const PREF_SAMPLE_RATE = "browser.chrome.errorReporter.sampleRate";
-const PREF_SUBMIT_URL = "browser.chrome.errorReporter.submitUrl";
-const TELEMETRY_ERROR_COLLECTED = "browser.errors.collected_count";
-const TELEMETRY_ERROR_COLLECTED_FILENAME = "browser.errors.collected_count_by_filename";
-const TELEMETRY_ERROR_COLLECTED_STACK = "browser.errors.collected_with_stack_count";
-const TELEMETRY_ERROR_REPORTED = "browser.errors.reported_success_count";
-const TELEMETRY_ERROR_REPORTED_FAIL = "browser.errors.reported_failure_count";
-const TELEMETRY_ERROR_SAMPLE_RATE = "browser.errors.sample_rate";
-
-const currentVersion = Services.appinfo.platformVersion;
-const expiringVersion = "64.0";
-const SCALARS_EXPIRED = Services.vc.compare(currentVersion, expiringVersion) === 1;
-
-add_task(async function testSetup() {
-  const canRecordExtended = Services.telemetry.canRecordExtended;
-  Services.telemetry.canRecordExtended = true;
-  registerCleanupFunction(() => Services.telemetry.canRecordExtended = canRecordExtended);
-});
-
-function createScriptError(options = {}) {
-  let scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
-  scriptError.init(
-    options.message || "",
-    "sourceName" in options ? options.sourceName : null,
-    options.sourceLine || null,
-    options.lineNumber || null,
-    options.columnNumber || null,
-    options.flags || Ci.nsIScriptError.errorFlag,
-    options.category || "chrome javascript",
-  );
-
-  // You can't really set the stack of a scriptError in JS, so we shadow it instead.
-   if (options.stack) {
-     scriptError = Object.create(scriptError, {
-       stack: {
-         value: createStack(options.stack),
-       },
-     });
-   }
-
-  return scriptError;
-}
-
-function createStack(frames) {
-  for (let k = 0; k < frames.length - 1; k++) {
-    frames[k].parent = frames[k + 1];
-  }
-  return frames[0];
-}
-
-function frame(options = {}) {
-  return Object.assign({
-    functionDisplayName: "fooFunction",
-    source: "resource://modules/BrowserErrorReporter.jsm",
-    line: 5,
-    column: 10,
-  }, options);
-}
-
-function noop() {
-  // Do nothing
-}
-
-// Clears the console of any previous messages. Should be called at the end of
-// each test that logs to the console.
-function resetConsole() {
-  Services.console.logStringMessage("");
-  Services.console.reset();
-}
-
-// Finds the fetch spy call for an error with a matching message.
-function fetchCallForMessage(fetchSpy, message) {
-  for (const call of fetchSpy.getCalls()) {
-    const body = JSON.parse(call.args[1].body);
-    if (body.exception.values[0].value.includes(message)) {
-      return call;
-    }
-  }
-
-  return null;
-}
-
-// Helper to test if a fetch spy was called with the given error message.
-// Used in tests where unrelated JS errors from other code are logged.
-function fetchPassedError(fetchSpy, message) {
-  return fetchCallForMessage(fetchSpy, message) !== null;
-}
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -2105,104 +2105,16 @@ sw:
     notification_emails:
       - sw-telemetry@mozilla.com
       - echuang@mozilla.com
     release_channel_collection: opt-out
     record_in_processes:
       - 'main'
       - 'content'
 
-# The following section contains the BrowserErrorReporter scalars.
-browser.errors:
-  collected_count:
-    bug_numbers:
-      - 1444554
-    description: >
-      The count of all browser chrome JS errors that were collected locally.
-    expires: "67"
-    kind: uint
-    notification_emails:
-      - nightly-js-errors@mozilla.com
-      - mkelly@mozilla.com
-    record_in_processes:
-      - 'main'
-
-  collected_with_stack_count:
-    bug_numbers:
-      - 1444554
-    description: >
-      The count of browser chrome JS errors that were collected locally and had
-      a usable stack trace.
-    expires: "67"
-    kind: uint
-    notification_emails:
-      - nightly-js-errors@mozilla.com
-      - mkelly@mozilla.com
-    record_in_processes:
-      - 'main'
-
-  reported_success_count:
-    bug_numbers:
-      - 1444554
-    description: >
-      The count of all browser chrome JS errors that were reported to the
-      remote collection service.
-    expires: "67"
-    kind: uint
-    notification_emails:
-      - nightly-js-errors@mozilla.com
-      - mkelly@mozilla.com
-    record_in_processes:
-      - 'main'
-
-  reported_failure_count:
-    bug_numbers:
-      - 1444554
-    description: >
-      The count of all browser chrome JS errors that we attempted to report to
-      the remote collection service, but failed to.
-    expires: "67"
-    kind: uint
-    notification_emails:
-      - nightly-js-errors@mozilla.com
-      - mkelly@mozilla.com
-    record_in_processes:
-      - 'main'
-
-  sample_rate:
-    bug_numbers:
-      - 1444554
-    description: >
-      The sample rate at which collected errors were reported.
-    expires: "67"
-    kind: string
-    notification_emails:
-      - nightly-js-errors@mozilla.com
-      - mkelly@mozilla.com
-    record_in_processes:
-      - 'main'
-
-  collected_count_by_filename:
-    bug_numbers:
-      - 1444554
-    description: >
-      The count of all browser chrome JS errors that were collected locally,
-      keyed by the filename of the file in which the error occurred. Collected
-      filenames are limited to specific paths under the resource:// and
-      chrome:// protocols; non-matching filenames are reported as "FILTERED".
-      Long filenames are truncated to the first 70 characters.
-    keyed: true
-    expires: "67"
-    kind: uint
-    notification_emails:
-      - nightly-js-errors@mozilla.com
-      - mkelly@mozilla.com
-    record_in_processes:
-      - 'main'
-
 widget:
   ime_name_on_windows:
     bug_numbers:
       - 1215818
     description: >
       Locale ID and name of IME which was selected by users on Windows.  This
       does NOT collect legacy IMM-IME names since we cannot get readable names
       and we do not support IMM-IME so aggressively because IME vendors