Bug 1557793 part 2. Stop using [array] in nsIStringBundle. r=Pike
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 11 Jun 2019 15:51:51 +0000
changeset 478254 33686f46a5ba418320b24efcbef537f889895419
parent 478253 17869bc540e5fde8961a19497c3cbb4a4de631f3
child 478255 89b5e0496b75de48a77f44ab25953bb54b2e0a4b
push id36138
push userrgurzau@mozilla.com
push dateTue, 11 Jun 2019 21:33:19 +0000
treeherdermozilla-central@ee33e076cbcb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersPike
bugs1557793
milestone69.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 1557793 part 2. Stop using [array] in nsIStringBundle. r=Pike Differential Revision: https://phabricator.services.mozilla.com/D34196
accessible/android/AccessibleWrap.cpp
accessible/jsat/Utils.jsm
browser/actors/NetErrorChild.jsm
browser/actors/PluginChild.jsm
browser/base/content/aboutDialog-appUpdater.js
browser/base/content/browser-pageActions.js
browser/base/content/browser-sync.js
browser/base/content/browser.js
browser/base/content/nsContextMenu.js
browser/base/content/tabbrowser.js
browser/base/content/test/sync/browser_sync.js
browser/base/content/test/webextensions/head.js
browser/base/content/webrtcIndicator.js
browser/components/BrowserGlue.jsm
browser/components/customizableui/CustomizableUI.jsm
browser/components/downloads/DownloadsCommon.jsm
browser/components/migration/MigrationUtils.jsm
browser/components/migration/tests/marionette/test_refresh_firefox.py
browser/components/payments/content/paymentDialogWrapper.js
browser/components/places/PlacesUIUtils.jsm
browser/components/pocket/content/main.js
browser/components/preferences/in-content/sync.js
browser/components/protocolhandler/WebProtocolHandlerRegistrar.jsm
browser/components/search/content/autocomplete-popup.js
browser/components/search/content/search-one-offs.js
browser/components/search/test/browser/browser_webapi.js
browser/components/translation/content/translation-notification.js
browser/components/urlbar/UrlbarProviderUnifiedComplete.jsm
browser/components/urlbar/UrlbarView.jsm
browser/components/urlbar/tests/browser/browser_search_favicon.js
browser/components/urlbar/tests/browser/browser_urlbarPlaceholder.js
browser/extensions/formautofill/FormAutofillDoorhanger.jsm
browser/extensions/formautofill/FormAutofillHandler.jsm
browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
browser/extensions/formautofill/content/customElements.js
browser/extensions/formautofill/content/manageDialog.js
browser/extensions/fxmonitor/privileged/api.js
browser/modules/ContentSearch.jsm
browser/modules/ExtensionsUI.jsm
browser/modules/LiveBookmarkMigrator.jsm
browser/modules/OpenInTabsUtils.jsm
browser/modules/PermissionUI.jsm
browser/modules/SiteDataManager.jsm
browser/modules/test/browser/browser_ContentSearch.js
browser/modules/webrtcUI.jsm
caps/nsScriptSecurityManager.cpp
devtools/client/aboutdebugging/components/Aboutdebugging.js
devtools/client/aboutdebugging/components/addons/InstallError.js
devtools/client/inspector/test/browser_inspector_navigate_to_errors.js
devtools/client/scratchpad/scratchpad.js
devtools/client/scratchpad/test/browser_scratchpad_run_error_goto_line.js
devtools/client/styleeditor/StyleEditorUtil.jsm
devtools/client/webide/content/webide.js
devtools/client/webide/modules/app-manager.js
devtools/client/webide/modules/app-validator.js
devtools/client/webide/modules/runtime-list.js
devtools/client/webide/test/test_app_validator.html
docshell/base/nsDocShell.cpp
dom/base/EventSource.cpp
dom/base/nsContentUtils.cpp
dom/browser-element/mochitest/browserElement_ReloadPostRequest.js
dom/html/HTMLFormElement.cpp
dom/html/ImageDocument.cpp
dom/html/MediaDocument.cpp
dom/manifest/ValueExtractor.jsm
dom/push/PushCrypto.jsm
dom/security/FramingChecker.cpp
dom/security/nsCSPContext.cpp
dom/security/nsCSPContext.h
dom/security/nsCSPParser.cpp
dom/security/nsCSPParser.h
dom/security/nsCSPUtils.cpp
dom/security/nsCSPUtils.h
dom/security/nsMixedContentBlocker.cpp
dom/security/test/csp/test_report_uri_missing_in_report_only_header.html
dom/security/test/csp/test_self_none_as_hostname_confusion.html
dom/serviceworkers/test/error_reporting_helpers.js
dom/serviceworkers/test/test_fetch_integrity.html
dom/webbrowserpersist/nsWebBrowserPersist.cpp
dom/websocket/WebSocket.cpp
dom/xslt/xslt/txMozillaXSLTProcessor.cpp
extensions/pref/autoconfig/src/prefcalls.js
intl/strres/nsIStringBundle.idl
intl/strres/nsStringBundle.cpp
intl/strres/nsStringBundle.h
intl/strres/nsStringBundleService.h
intl/strres/tests/unit/test_bug378839.js
layout/mathml/tests/test_bug553917.html
layout/mathml/tests/test_bug827713-2.html
layout/style/ErrorReporter.cpp
layout/style/ErrorReporter.h
layout/style/GeckoBindings.cpp
mobile/android/chrome/content/ConsoleAPI.js
mobile/android/chrome/content/OfflineApps.js
mobile/android/chrome/content/RemoteDebugger.js
mobile/android/chrome/content/aboutAddons.js
mobile/android/chrome/content/browser.js
mobile/android/chrome/content/content.js
mobile/android/components/ContentPermissionPrompt.js
mobile/android/components/LoginManagerPrompter.js
mobile/android/components/NSSDialogService.js
mobile/android/components/PromptService.js
mobile/android/components/geckoview/GeckoViewPrompt.js
mobile/android/modules/ActionBarHandler.jsm
mobile/android/modules/DownloadNotifications.jsm
mobile/android/modules/FxAccountsWebChannel.jsm
mobile/android/modules/WebrtcUI.jsm
mobile/android/modules/geckoview/GeckoViewConsole.jsm
netwerk/base/nsNetUtil.cpp
netwerk/protocol/gio/nsGIOProtocolHandler.cpp
netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
netwerk/streamconv/converters/nsIndexedToHTML.cpp
security/manager/ssl/nsNSSCallbacks.cpp
security/manager/ssl/nsNSSCertHelper.cpp
security/manager/ssl/nsNSSCertHelper.h
services/fxaccounts/FxAccountsWebChannel.jsm
services/fxaccounts/tests/browser/browser_device_connected.js
services/sync/modules/util.js
toolkit/components/alerts/resources/content/alert.js
toolkit/components/alerts/test/test_principal.html
toolkit/components/downloads/DownloadUIHelper.jsm
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/parent/ext-management.js
toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js
toolkit/components/narrate/NarrateControls.jsm
toolkit/components/normandy/content/ShieldFrameChild.jsm
toolkit/components/passwordmgr/LoginAutoCompleteResult.jsm
toolkit/components/passwordmgr/LoginManagerContextMenu.jsm
toolkit/components/passwordmgr/LoginManagerPrompter.jsm
toolkit/components/places/PlacesUtils.jsm
toolkit/components/places/nsNavHistory.cpp
toolkit/components/processsingleton/MainProcessSingleton.jsm
toolkit/components/prompts/src/Prompter.jsm
toolkit/components/resistfingerprinting/RFPHelper.jsm
toolkit/components/search/SearchEngine.jsm
toolkit/content/aboutTelemetry.js
toolkit/content/aboutwebrtc/aboutWebrtc.js
toolkit/content/contentAreaUtils.js
toolkit/content/widgets/autocomplete-richlistitem.js
toolkit/content/widgets/stringbundle.js
toolkit/content/widgets/wizard.xml
toolkit/mozapps/downloads/DownloadUtils.jsm
toolkit/mozapps/extensions/AddonContentPolicy.cpp
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/content/extensions.xml
toolkit/mozapps/extensions/test/browser/head.js
toolkit/mozapps/handling/ContentDispatchChooser.jsm
toolkit/mozapps/update/UpdateService.jsm
toolkit/xre/ProfileReset.cpp
toolkit/xre/nsAppRunner.cpp
uriloader/exthandler/nsExternalHelperAppService.cpp
widget/cocoa/OSXNotificationCenter.mm
widget/windows/ToastNotificationHandler.cpp
--- a/accessible/android/AccessibleWrap.cpp
+++ b/accessible/android/AccessibleWrap.cpp
@@ -319,21 +319,21 @@ void AccessibleWrap::GetRoleDescription(
   rv = sbs->CreateBundle(ROLE_STRINGS_URL, getter_AddRefs(bundle));
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to get string bundle");
     return;
   }
 
   if (aRole == roles::HEADING && aAttributes) {
     // The heading level is an attribute, so we need that.
-    nsString level;
-    rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("level"), level);
+    AutoTArray<nsString, 1> formatString;
+    rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("level"),
+                                        *formatString.AppendElement());
     if (NS_SUCCEEDED(rv)) {
-      const char16_t* formatString[] = {level.get()};
-      rv = bundle->FormatStringFromName("headingLevel", formatString, 1,
+      rv = bundle->FormatStringFromName("headingLevel", formatString,
                                         aRoleDescription);
       if (NS_SUCCEEDED(rv)) {
         return;
       }
     }
   }
 
   GetAccService()->GetStringRole(aRole, aGeckoRole);
--- a/accessible/jsat/Utils.jsm
+++ b/accessible/jsat/Utils.jsm
@@ -108,17 +108,17 @@ var Utils = { // jshint ignore:line
         let string = aDetails.string;
         if (!string) {
           return str;
         }
         try {
           let args = aDetails.args;
           let count = aDetails.count;
           if (args) {
-            str = bundle.formatStringFromName(string, args, args.length);
+            str = bundle.formatStringFromName(string, args);
           } else {
             str = bundle.GetStringFromName(string);
           }
           if (count) {
             str = PluralForm.get(count, str);
             str = str.replace("#1", count);
           }
         } catch (e) {
--- a/browser/actors/NetErrorChild.jsm
+++ b/browser/actors/NetErrorChild.jsm
@@ -184,17 +184,17 @@ class NetErrorChild extends ActorChild {
 
       // This error code currently only exists for the Symantec distrust
       // in Firefox 63, so we add copy explaining that to the user.
       // In case of future distrusts of that scale we might need to add
       // additional parameters that allow us to identify the affected party
       // without replicating the complex logic from certverifier code.
       case MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED:
         let description = gPipNSSBundle.formatStringFromName(
-          "certErrorSymantecDistrustDescription1", [doc.location.hostname], 1);
+          "certErrorSymantecDistrustDescription1", [doc.location.hostname]);
         let descriptionContainer = doc.getElementById("errorShortDescText2");
         descriptionContainer.textContent = description;
 
         let adminDescription = doc.createElement("p");
         adminDescription.textContent =
           gPipNSSBundle.GetStringFromName("certErrorSymantecDistrustAdministrator");
         descriptionContainer.append(adminDescription);
 
@@ -454,21 +454,21 @@ class NetErrorChild extends ActorChild {
     }
 
     if (!msg2) {
       // We couldn't get an error message. Use the error string.
       // Note that this is different from before where we used PR_ErrorToString.
       msg2 = nss_error_id_str;
     }
     let msg = gPipNSSBundle.formatStringFromName("SSLConnectionErrorPrefix2",
-                                                 [hostString, msg2], 2);
+                                                 [hostString, msg2]);
 
     if (nss_error_id_str && msg2 != nss_error_id_str) {
       msg += gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
-                                                [nss_error_id_str], 1) + "\n";
+                                                [nss_error_id_str]) + "\n";
     }
     return msg;
   }
 
   onPageLoad(originalTarget, win) {
     // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
     const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;
 
--- a/browser/actors/PluginChild.jsm
+++ b/browser/actors/PluginChild.jsm
@@ -495,17 +495,17 @@ class PluginChild extends ActorChild {
         this.addLinkClickCallback(updateLink, "forwardCallback",
                                   "openPluginUpdatePage", pluginTag);
         /* FALLTHRU */
 
       case "PluginVulnerableNoUpdate":
       case "PluginClickToPlay":
         this._handleClickToPlayEvent(plugin);
         let pluginName = this._getPluginInfo(plugin).pluginName;
-        let messageString = gNavigatorBundle.formatStringFromName("PluginClickToActivate2", [pluginName], 1);
+        let messageString = gNavigatorBundle.formatStringFromName("PluginClickToActivate2", [pluginName]);
         let overlayText = this.getPluginUI(plugin, "clickToPlay");
         overlayText.textContent = messageString;
         if (eventType == "PluginVulnerableUpdatable" ||
             eventType == "PluginVulnerableNoUpdate") {
           let vulnerabilityString = gNavigatorBundle.GetStringFromName(eventType);
           let vulnerabilityText = this.getPluginUI(plugin, "vulnerabilityStatus");
           vulnerabilityText.textContent = vulnerabilityString;
         }
@@ -880,17 +880,17 @@ class PluginChild extends ActorChild {
       state: crashData.state,
       message: crashData.message,
     });
   }
 
   NPAPIPluginProcessCrashed({pluginName, runID, state}) {
     let message =
       gNavigatorBundle.formatStringFromName("crashedpluginsMessage.title",
-                                            [pluginName], 1);
+                                            [pluginName]);
 
     let contentWindow = this.content;
     let cwu = contentWindow.windowUtils;
     let plugins = cwu.plugins;
 
     for (let plugin of plugins) {
       if (plugin instanceof Ci.nsIObjectLoadingContent &&
           plugin.runID == runID) {
@@ -1005,17 +1005,17 @@ class PluginChild extends ActorChild {
 
     if (!gmpPlugin || !doc) {
       // TODO: Throw exception? How did we get here?
       return;
     }
 
     let messageString =
       gNavigatorBundle.formatStringFromName("crashedpluginsMessage.title",
-                                            [pluginName], 1);
+                                            [pluginName]);
 
     this.mm.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
                                  { messageString, pluginID });
 
     // Remove the notification when the page is reloaded.
     doc.defaultView.top.addEventListener("unload", event => {
       this.hideNotificationBar("plugin-crashed");
     });
--- a/browser/base/content/aboutDialog-appUpdater.js
+++ b/browser/base/content/aboutDialog-appUpdater.js
@@ -187,17 +187,17 @@ appUpdater.prototype =
         // Include the build ID if this is an "a#" (nightly or aurora) build
         if (/a\d+$/.test(updateVersion)) {
           let buildID = gAppUpdater.update.buildID;
           let year = buildID.slice(0, 4);
           let month = buildID.slice(4, 6);
           let day = buildID.slice(6, 8);
           updateVersion += ` (${year}-${month}-${day})`;
         }
-        button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1);
+        button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion]);
         button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey");
       }
       this.updateDeck.selectedPanel = panel;
       if (this.options.buttonAutoFocus &&
           (!document.commandDispatcher.focusedElement || // don't steal the focus
            document.commandDispatcher.focusedElement.localName == "button")) { // except from the other buttons
         button.focus();
       }
--- a/browser/base/content/browser-pageActions.js
+++ b/browser/base/content/browser-pageActions.js
@@ -1185,17 +1185,17 @@ BrowserPageActions.addSearchEngine = {
           return;
         }
         const kSearchBundleURI = "chrome://global/locale/search/search.properties";
         let searchBundle = Services.strings.createBundle(kSearchBundleURI);
         let brandBundle = document.getElementById("bundle_brand");
         let brandName = brandBundle.getString("brandShortName");
         let title = searchBundle.GetStringFromName("error_invalid_engine_title");
         let text = searchBundle.formatStringFromName("error_duplicate_engine_msg",
-                                                     [brandName, uri], 2);
+                                                     [brandName, uri]);
         Services.prompt.QueryInterface(Ci.nsIPromptFactory);
         let prompt = Services.prompt.getPrompt(gBrowser.contentWindow, Ci.nsIPrompt);
         prompt.QueryInterface(Ci.nsIWritablePropertyBag2);
         prompt.setPropertyAsBool("allowTabModal", true);
         prompt.alert(title, text);
       }
     );
   },
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -399,24 +399,24 @@ var gSync = {
     this.appMenuLabel.classList.remove("subviewbutton-nav");
 
     if (status == UIState.STATUS_NOT_CONFIGURED) {
       return;
     }
 
     // At this point we consider sync to be configured (but still can be in an error state).
     if (status == UIState.STATUS_LOGIN_FAILED) {
-      let tooltipDescription = this.fxaStrings.formatStringFromName("reconnectDescription", [state.email], 1);
+      let tooltipDescription = this.fxaStrings.formatStringFromName("reconnectDescription", [state.email]);
       let errorLabel = this.appMenuStatus.getAttribute("errorlabel");
       this.appMenuContainer.setAttribute("fxastatus", "login-failed");
       this.appMenuLabel.setAttribute("label", errorLabel);
       this.appMenuStatus.setAttribute("tooltiptext", tooltipDescription);
       return;
     } else if (status == UIState.STATUS_NOT_VERIFIED) {
-      let tooltipDescription = this.fxaStrings.formatStringFromName("verifyDescription", [state.email], 1);
+      let tooltipDescription = this.fxaStrings.formatStringFromName("verifyDescription", [state.email]);
       let unverifiedLabel = this.appMenuStatus.getAttribute("unverifiedlabel");
       this.appMenuContainer.setAttribute("fxastatus", "unverified");
       this.appMenuLabel.setAttribute("label", unverifiedLabel);
       this.appMenuStatus.setAttribute("tooltiptext", tooltipDescription);
       return;
     }
 
     // At this point we consider sync to be logged-in.
@@ -932,23 +932,23 @@ var gSync = {
     const status = state.status;
 
     // This is a little messy as the Sync buttons are 1/2 Sync related and
     // 1/2 FxA related - so for some strings we use Sync strings, but for
     // others we reach into gSync for strings.
     let tooltiptext;
     if (status == UIState.STATUS_NOT_VERIFIED) {
       // "needs verification"
-      tooltiptext = this.fxaStrings.formatStringFromName("verifyDescription", [state.email], 1);
+      tooltiptext = this.fxaStrings.formatStringFromName("verifyDescription", [state.email]);
     } else if (status == UIState.STATUS_NOT_CONFIGURED) {
       // "needs setup".
       tooltiptext = this.syncStrings.GetStringFromName("signInToSync.description");
     } else if (status == UIState.STATUS_LOGIN_FAILED) {
       // "need to reconnect/re-enter your password"
-      tooltiptext = this.fxaStrings.formatStringFromName("reconnectDescription", [state.email], 1);
+      tooltiptext = this.fxaStrings.formatStringFromName("reconnectDescription", [state.email]);
     } else {
       // Sync appears configured - format the "last synced at" time.
       tooltiptext = this.formatLastSyncDate(state.lastSync);
     }
 
     if (this.appMenuLabel) {
       let syncNow = document.getElementById("PanelUI-remotetabs-syncnow");
       if (tooltiptext) {
@@ -964,17 +964,17 @@ var gSync = {
     return this.relativeTimeFormat = new Services.intl.RelativeTimeFormat(undefined, {style: "long"});
   },
 
   formatLastSyncDate(date) {
     if (!date) { // Date can be null before the first sync!
       return null;
     }
     const relativeDateStr = this.relativeTimeFormat.formatBestUnit(date);
-    return this.syncStrings.formatStringFromName("lastSync2.label", [relativeDateStr], 1);
+    return this.syncStrings.formatStringFromName("lastSync2.label", [relativeDateStr]);
   },
 
   onClientsSynced() {
     let element = document.getElementById("PanelUI-remotetabs-main");
     if (element) {
       if (Weave.Service.clientsEngine.stats.numClients > 1) {
         element.setAttribute("devices-status", "multi");
       } else {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -497,17 +497,17 @@ function browserWindows() {
 
 // This is a stringbundle-like interface to gBrowserBundle, formerly a getter for
 // the "bundle_browser" element.
 var gNavigatorBundle = {
   getString(key) {
     return gBrowserBundle.GetStringFromName(key);
   },
   getFormattedString(key, array) {
-    return gBrowserBundle.formatStringFromName(key, array, array.length);
+    return gBrowserBundle.formatStringFromName(key, array);
   },
 };
 
 function showFxaToolbarMenu(enable) {
   // We only show the Firefox Account toolbar menu if the feature is enabled and
   // if sync is enabled.
   const syncEnabled = Services.prefs.getBoolPref("identity.fxaccounts.enabled", false);
   const mainWindowEl = document.documentElement;
@@ -4126,17 +4126,17 @@ const BrowserSearch = {
    *
    * @param {String} name The name of the engine to use, an empty string if to
    *                      use the default placeholder.
    */
   _setURLBarPlaceholder(name) {
     let placeholder;
     if (name) {
       placeholder = gBrowserBundle.formatStringFromName("urlbar.placeholder",
-        [name], 1);
+        [name]);
     } else {
       placeholder = gURLBar.getAttribute("defaultPlaceholder");
     }
     gURLBar.setAttribute("placeholder", placeholder);
   },
 
   addEngine(browser, engine, uri) {
     if (!this._searchInitComplete) {
@@ -7732,17 +7732,17 @@ function ReportFalseDeceptiveSite() {
       if (reportUrl) {
         openTrustedLinkIn(reportUrl, "tab");
       } else {
         let bundle =
           Services.strings.createBundle("chrome://browser/locale/safebrowsing/safebrowsing.properties");
         Services.prompt.alert(window,
                               bundle.GetStringFromName("errorReportFalseDeceptiveTitle"),
                               bundle.formatStringFromName("errorReportFalseDeceptiveMessage",
-                                                          [message.data.blockedInfo.provider], 1));
+                                                          [message.data.blockedInfo.provider]));
         }
     };
     mm.addMessageListener("DeceptiveBlockedDetails:Result", onMessage);
 
     mm.sendAsyncMessage("DeceptiveBlockedDetails");
   }
 }
 
@@ -8483,17 +8483,17 @@ TabModalPromptBox.prototype = {
       hostForAllowFocusCheckbox = principalToAllowFocusFor.URI.host;
     } catch (ex) { /* Ignore exceptions for host-less URIs */ }
     if (hostForAllowFocusCheckbox) {
       let allowFocusRow = document.createXULElement("row");
       allowFocusCheckbox = document.createXULElement("checkbox");
       let spacer = document.createXULElement("spacer");
       allowFocusRow.appendChild(spacer);
       let label = gTabBrowserBundle.formatStringFromName("tabs.allowTabFocusByPromptForSite",
-                                                      [hostForAllowFocusCheckbox], 1);
+                                                      [hostForAllowFocusCheckbox]);
       allowFocusCheckbox.setAttribute("label", label);
       allowFocusRow.appendChild(allowFocusCheckbox);
       newPrompt.ui.rows.append(allowFocusRow);
     }
 
     let tab = gBrowser.getTabForBrowser(browser);
     let closeCB = this._promptCloseCallback.bind(null, onCloseCallback, principalToAllowFocusFor,
                                                  allowFocusCheckbox);
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -345,17 +345,17 @@ nsContextMenu.prototype = {
       var item = document.getElementById("context-openlinkincontainertab");
 
       item.setAttribute("data-usercontextid", gContextMenuContentData.userContextId);
 
       var label =
         ContextualIdentityService.getUserContextLabel(gContextMenuContentData.userContextId);
       item.setAttribute("label",
          gBrowserBundle.formatStringFromName("userContextOpenLink.label",
-                                             [label], 1));
+                                             [label]));
     }
 
     var shouldShow = this.onSaveableLink || isMailtoInternal || this.onPlainTextLink;
     var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
     var showContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
     this.showItem("context-openlink", shouldShow && !isWindowPrivate);
     this.showItem("context-openlinkprivate", shouldShow && PrivateBrowsingUtils.enabled);
     this.showItem("context-openlinkintab", shouldShow && !inContainer);
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -4278,17 +4278,17 @@ window._gBrowser = {
           label += " (pid " + tab.linkedBrowser.frameLoader.remoteTab.osPid + ")";
 
           if (window.docShell.QueryInterface(Ci.nsILoadContext).useRemoteSubframes) {
             label += " [F]";
           }
         }
       }
       if (tab.userContextId) {
-        label = gTabBrowserBundle.formatStringFromName("tabs.containers.tooltip", [label, ContextualIdentityService.getUserContextLabel(tab.userContextId)], 2);
+        label = gTabBrowserBundle.formatStringFromName("tabs.containers.tooltip", [label, ContextualIdentityService.getUserContextLabel(tab.userContextId)]);
       }
     }
 
     event.target.setAttribute("label", label);
   },
 
   handleEvent(aEvent) {
     switch (aEvent.type) {
--- a/browser/base/content/test/sync/browser_sync.js
+++ b/browser/base/content/test/sync/browser_sync.js
@@ -100,17 +100,17 @@ add_task(async function test_ui_state_un
     email: "foo@bar.com",
     lastSync: new Date(),
     syncing: false,
   };
 
   gSync.updateAllUI(state);
 
   let expectedLabel = gSync.appMenuStatus.getAttribute("unverifiedlabel");
-  let tooltipText = gSync.fxaStrings.formatStringFromName("verifyDescription", [state.email], 1);
+  let tooltipText = gSync.fxaStrings.formatStringFromName("verifyDescription", [state.email]);
   checkPanelUIStatusBar({
     label: expectedLabel,
     tooltip: tooltipText,
     fxastatus: "unverified",
     syncing: false,
     syncNowTooltip: tooltipText,
   });
   checkRemoteTabsPanel("PanelUI-remotetabs-unverified", false);
@@ -123,17 +123,17 @@ add_task(async function test_ui_state_lo
   let state = {
     status: UIState.STATUS_LOGIN_FAILED,
     email: "foo@bar.com",
   };
 
   gSync.updateAllUI(state);
 
   let expectedLabel = gSync.appMenuStatus.getAttribute("errorlabel");
-  let tooltipText = gSync.fxaStrings.formatStringFromName("reconnectDescription", [state.email], 1);
+  let tooltipText = gSync.fxaStrings.formatStringFromName("reconnectDescription", [state.email]);
   checkPanelUIStatusBar({
     label: expectedLabel,
     tooltip: tooltipText,
     fxastatus: "login-failed",
     syncing: false,
     syncNowTooltip: tooltipText,
   });
   checkRemoteTabsPanel("PanelUI-remotetabs-reauthsync", false);
--- a/browser/base/content/test/webextensions/head.js
+++ b/browser/base/content/test/webextensions/head.js
@@ -171,17 +171,17 @@ function isDefaultIcon(icon) {
  *        compare with.
  * @param {string|null} param
  *        Optional string to substitute for %S in the localized string.
  * @param {string} msg
  *        The message to be emitted as part of the actual test.
  */
 function checkPermissionString(string, key, param, msg) {
   let localizedString = param ?
-                        gBrowserBundle.formatStringFromName(key, [param], 1) :
+                        gBrowserBundle.formatStringFromName(key, [param]) :
                         gBrowserBundle.GetStringFromName(key);
 
   // If this is a parameterized string and the parameter isn't given,
   // just do a simple comparison of the text before and after the %S
   if (localizedString.includes("%S")) {
     let i = localizedString.indexOf("%S");
     ok(string.startsWith(localizedString.slice(0, i)), msg);
     ok(string.endsWith(localizedString.slice(i + 2)), msg);
--- a/browser/base/content/webrtcIndicator.js
+++ b/browser/base/content/webrtcIndicator.js
@@ -10,17 +10,17 @@ var gStringBundle;
 
 function init(event) {
   gStringBundle = Services.strings.createBundle(BUNDLE_URL);
 
   let brand = Services.strings.createBundle("chrome://branding/locale/brand.properties");
   let brandShortName = brand.GetStringFromName("brandShortName");
   document.title =
     gStringBundle.formatStringFromName("webrtcIndicator.windowtitle",
-                                       [brandShortName], 1);
+                                       [brandShortName]);
 
   for (let id of ["audioVideoButton", "screenSharePopup"]) {
     let popup = document.getElementById(id);
     popup.addEventListener("popupshowing", onPopupMenuShowing);
     popup.addEventListener("popuphiding", onPopupMenuHiding);
     popup.addEventListener("command", onPopupMenuCommand);
   }
 
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -1254,25 +1254,25 @@ BrowserGlue.prototype = {
       return;
 
     let productName = gBrandBundle.GetStringFromName("brandShortName");
     let resetBundle = Services.strings
                               .createBundle("chrome://global/locale/resetProfile.properties");
 
     let message;
     if (reason == "unused") {
-      message = resetBundle.formatStringFromName("resetUnusedProfile.message", [productName], 1);
+      message = resetBundle.formatStringFromName("resetUnusedProfile.message", [productName]);
     } else if (reason == "uninstall") {
-      message = resetBundle.formatStringFromName("resetUninstalled.message", [productName], 1);
+      message = resetBundle.formatStringFromName("resetUninstalled.message", [productName]);
     } else {
       throw new Error(`Unknown reason (${reason}) given to _resetProfileNotification.`);
     }
     let buttons = [
       {
-        label:     resetBundle.formatStringFromName("refreshProfile.resetButton.label", [productName], 1),
+        label:     resetBundle.formatStringFromName("refreshProfile.resetButton.label", [productName]),
         accessKey: resetBundle.GetStringFromName("refreshProfile.resetButton.accesskey"),
         callback() {
           ResetProfile.openConfirmationDialog(win);
         },
       },
     ];
 
     win.gNotificationBox.appendNotification(message, "reset-profile-notification",
@@ -2170,17 +2170,17 @@ BrowserGlue.prototype = {
 
   /**
    * Show the notificationBox for a locked places database.
    */
   _showPlacesLockedNotificationBox: function BG__showPlacesLockedNotificationBox() {
     var applicationName = gBrandBundle.GetStringFromName("brandShortName");
     var placesBundle = Services.strings.createBundle("chrome://browser/locale/places/places.properties");
     var title = placesBundle.GetStringFromName("lockPrompt.title");
-    var text = placesBundle.formatStringFromName("lockPrompt.text", [applicationName], 1);
+    var text = placesBundle.formatStringFromName("lockPrompt.text", [applicationName]);
     var buttonText = placesBundle.GetStringFromName("lockPromptInfoButton.label");
     var accessKey = placesBundle.GetStringFromName("lockPromptInfoButton.accessKey");
 
     var helpTopic = "places-locked";
     var url = Services.urlFormatter.formatURLPref("app.support.baseURL");
     url += helpTopic;
 
     var win = BrowserWindowTracker.getTopWindow();
@@ -2203,17 +2203,17 @@ BrowserGlue.prototype = {
     notification.persistence = -1; // Until user closes it
   },
 
   _showSyncStartedDoorhanger() {
     let bundle = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
     let productName = gBrandBundle.GetStringFromName("brandShortName");
     let title = bundle.GetStringFromName("syncStartNotification.title");
     let body = bundle.formatStringFromName("syncStartNotification.body2",
-                                            [productName], 1);
+                                            [productName]);
 
     let clickCallback = (subject, topic, data) => {
       if (topic != "alertclickcallback")
         return;
       this._openPreferences("sync");
     };
     this.AlertsService.showAlertNotification(null, title, body, true, null, clickCallback);
   },
@@ -2783,30 +2783,30 @@ BrowserGlue.prototype = {
       const deviceName = URIs[0].sender && URIs[0].sender.name;
       let title, body;
       const bundle = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
       if (URIs.length == 1) {
         // Due to bug 1305895, tabs from iOS may not have device information, so
         // we have separate strings to handle those cases. (See Also
         // unnamedTabsArrivingNotificationNoDevice.body below)
         if (deviceName) {
-          title = bundle.formatStringFromName("tabArrivingNotificationWithDevice.title", [deviceName], 1);
+          title = bundle.formatStringFromName("tabArrivingNotificationWithDevice.title", [deviceName]);
         } else {
           title = bundle.GetStringFromName("tabArrivingNotification.title");
         }
         // Use the page URL as the body. We strip the fragment and query (after
         // the `?` and `#` respectively) to reduce size, and also format it the
         // same way that the url bar would.
         body = URIs[0].uri.replace(/([?#]).*$/, "$1");
         let wasTruncated = body.length < URIs[0].uri.length;
         if (win.gURLBar) {
           body = win.gURLBar.trimValue(body);
         }
         if (wasTruncated) {
-          body = bundle.formatStringFromName("singleTabArrivingWithTruncatedURL.body", [body], 1);
+          body = bundle.formatStringFromName("singleTabArrivingWithTruncatedURL.body", [body]);
         }
       } else {
         title = bundle.GetStringFromName("multipleTabsArrivingNotification.title");
         const allKnownSender = URIs.every(URI => URI.sender != null);
         const allSameDevice = allKnownSender && URIs.every(URI => URI.sender.id == URIs[0].sender.id);
         let tabArrivingBody;
         if (allSameDevice) {
           if (deviceName) {
@@ -2871,17 +2871,17 @@ BrowserGlue.prototype = {
 
   _onDeviceConnected(deviceName) {
     let accountsBundle = Services.strings.createBundle(
       "chrome://browser/locale/accounts.properties"
     );
     let title = accountsBundle.GetStringFromName("deviceConnectedTitle");
     let body = accountsBundle.formatStringFromName("deviceConnectedBody" +
                                                    (deviceName ? "" : ".noDeviceName"),
-                                                   [deviceName], 1);
+                                                   [deviceName]);
 
     let clickCallback = async (subject, topic, data) => {
       if (topic != "alertclickcallback")
         return;
       let url = await FxAccounts.config.promiseManageDevicesURI("device-connected-notification");
       let win = BrowserWindowTracker.getTopWindow({private: false});
       if (!win) {
         this._openURLInNewWindow(url);
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -1616,18 +1616,17 @@ var CustomizableUIInternal = {
       name = aWidget.id + "." + aProp;
       def = aDef || "";
     }
     if (aWidget.localized === false) {
       return def;
     }
     try {
       if (Array.isArray(aFormatArgs) && aFormatArgs.length) {
-        return gWidgetsBundle.formatStringFromName(name, aFormatArgs,
-          aFormatArgs.length) || def;
+        return gWidgetsBundle.formatStringFromName(name, aFormatArgs) || def;
       }
       return gWidgetsBundle.GetStringFromName(name) || def;
     } catch (ex) {
       // If an empty string was explicitly passed, treat it as an actual
       // value rather than a missing property.
       if (!def && (name != "" || kReqStringProps.includes(aProp))) {
         log.error("Could not localize property '" + name + "'.");
       }
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -148,25 +148,23 @@ var DownloadsCommon = {
     let strings = {};
     let sb = Services.strings.createBundle(kDownloadsStringBundleUrl);
     for (let string of sb.getSimpleEnumeration()) {
       let stringName = string.key;
       if (stringName in kDownloadsStringsRequiringFormatting) {
         strings[stringName] = function() {
           // Convert "arguments" to a real array before calling into XPCOM.
           return sb.formatStringFromName(stringName,
-                                         Array.from(arguments),
-                                         arguments.length);
+                                         Array.from(arguments));
         };
       } else if (stringName in kDownloadsStringsRequiringPluralForm) {
         strings[stringName] = function(aCount) {
           // Convert "arguments" to a real array before calling into XPCOM.
           let formattedString = sb.formatStringFromName(stringName,
-                                         Array.from(arguments),
-                                         arguments.length);
+                                         Array.from(arguments));
           return PluralForm.get(aCount, formattedString);
         };
       } else {
         strings[stringName] = string.value;
       }
     }
     delete this.strings;
     return this.strings = strings;
--- a/browser/components/migration/MigrationUtils.jsm
+++ b/browser/components/migration/MigrationUtils.jsm
@@ -523,18 +523,17 @@ var MigrationUtils = Object.freeze({
     const OVERRIDES = {
       "4_firefox": "4_firefox_history_and_bookmarks",
       "64_firefox": "64_firefox_other",
     };
     aKey = OVERRIDES[aKey] || aKey;
 
     if (aReplacements === undefined)
       return getMigrationBundle().GetStringFromName(aKey);
-    return getMigrationBundle().formatStringFromName(
-      aKey, aReplacements, aReplacements.length);
+    return getMigrationBundle().formatStringFromName(aKey, aReplacements);
   },
 
   _getLocalePropertyForBrowser(browserId) {
     switch (browserId) {
       case "edge":
         return "sourceNameEdge";
       case "ie":
         return "sourceNameIE";
--- a/browser/components/migration/tests/marionette/test_refresh_firefox.py
+++ b/browser/components/migration/tests/marionette/test_refresh_firefox.py
@@ -561,17 +561,17 @@ class TestFirefoxRefresh(MarionetteTestC
         self.desktop_backup_path = self.runCode("""
           let container;
           try {
             container = Services.dirsvc.get("Desk", Ci.nsIFile);
           } catch (ex) {
             container = Services.dirsvc.get("Home", Ci.nsIFile);
           }
           let bundle = Services.strings.createBundle("chrome://mozapps/locale/profile/profileSelection.properties");
-          let dirName = bundle.formatStringFromName("resetBackupDirectory", [Services.appinfo.name], 1);
+          let dirName = bundle.formatStringFromName("resetBackupDirectory", [Services.appinfo.name]);
           container.append(dirName);
           container.append(arguments[0]);
           return container.path;
         """, script_args=(profileLeafName,))  # NOQA: E501
 
         self.assertTrue(os.path.isdir(self.reset_profile_path),
                         "Reset profile path should be present")
         self.assertTrue(os.path.isdir(self.desktop_backup_path),
--- a/browser/components/payments/content/paymentDialogWrapper.js
+++ b/browser/components/payments/content/paymentDialogWrapper.js
@@ -42,17 +42,17 @@ XPCOMUtils.defineLazyGetter(this, "formA
   }
 
   return storage;
 });
 
 XPCOMUtils.defineLazyGetter(this, "reauthPasswordPromptMessage", () => {
   const brandShortName = FormAutofillUtils.brandBundle.GetStringFromName("brandShortName");
   return FormAutofillUtils.stringBundle.formatStringFromName(
-    `useCreditCardPasswordPrompt.${AppConstants.platform}`, [brandShortName], 1);
+    `useCreditCardPasswordPrompt.${AppConstants.platform}`, [brandShortName]);
 });
 
 /**
  * Temporary/transient storage for address and credit card records
  *
  * Implements a subset of the FormAutofillStorage collection class interface, and delegates to
  * those classes for some utility methods
  */
--- a/browser/components/places/PlacesUIUtils.jsm
+++ b/browser/components/places/PlacesUIUtils.jsm
@@ -221,17 +221,17 @@ var PlacesUIUtils = {
    *          The string spec of the URI
    * @return A URI object for the spec.
    */
   createFixedURI: function PUIU_createFixedURI(aSpec) {
     return Services.uriFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
   },
 
   getFormattedString: function PUIU_getFormattedString(key, params) {
-    return bundle.formatStringFromName(key, params, params.length);
+    return bundle.formatStringFromName(key, params);
   },
 
   /**
    * Get a localized plural string for the specified key name and numeric value
    * substituting parameters.
    *
    * @param   aKey
    *          String, key for looking up the localized string in the bundle
--- a/browser/components/pocket/content/main.js
+++ b/browser/components/pocket/content/main.js
@@ -456,17 +456,17 @@ var pktUI = (function() {
         });
 
         var _initL10NMessageId = "initL10N";
         pktUIMessaging.addMessageListener(iframe, _initL10NMessageId, function(panelId, data) {
             var strings = {};
             var bundle = Services.strings.createBundle("chrome://browser/locale/pocket.properties");
             for (let str of bundle.getSimpleEnumeration()) {
                 if (str.key in data) {
-                    strings[str.key] = bundle.formatStringFromName(str.key, data[str.key], data[str.key].length);
+                    strings[str.key] = bundle.formatStringFromName(str.key, data[str.key]);
                 } else {
                     strings[str.key] = str.value;
                 }
             }
             pktUIMessaging.sendResponseMessageToPanel(panelId, _initL10NMessageId, { strings });
         });
     }
 
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -431,17 +431,17 @@ var gSyncPane = {
 
   verifyFirefoxAccount() {
     let showVerifyNotification = (data) => {
       let isError = !data;
       let maybeNot = isError ? "Not" : "";
       let sb = this._accountsStringBundle;
       let title = sb.GetStringFromName("verification" + maybeNot + "SentTitle");
       let email = !isError && data ? data.email : "";
-      let body = sb.formatStringFromName("verification" + maybeNot + "SentBody", [email], 1);
+      let body = sb.formatStringFromName("verification" + maybeNot + "SentBody", [email]);
       new Notification(title, { body });
     };
 
     let onError = () => {
       showVerifyNotification();
     };
 
     let onSuccess = data => {
--- a/browser/components/protocolhandler/WebProtocolHandlerRegistrar.jsm
+++ b/browser/components/protocolhandler/WebProtocolHandlerRegistrar.jsm
@@ -15,17 +15,17 @@ function WebProtocolHandlerRegistrar() {
 WebProtocolHandlerRegistrar.prototype = {
   get stringBundle() {
     let sb = Services.strings.createBundle(STRING_BUNDLE_URI);
     delete WebProtocolHandlerRegistrar.prototype.stringBundle;
     return WebProtocolHandlerRegistrar.prototype.stringBundle = sb;
   },
 
   _getFormattedString(key, params) {
-    return this.stringBundle.formatStringFromName(key, params, params.length);
+    return this.stringBundle.formatStringFromName(key, params);
   },
 
   _getString(key) {
     return this.stringBundle.GetStringFromName(key);
   },
 
   /**
    * See nsIWebProtocolHandlerRegistrar
--- a/browser/components/search/content/autocomplete-popup.js
+++ b/browser/components/search/content/autocomplete-popup.js
@@ -213,17 +213,17 @@ class MozSearchAutocompleteRichlistboxPo
       if (uri) {
         this.setAttribute("src", uri.spec);
       } else {
         // If the default has just been changed to a provider without icon,
         // avoid showing the icon of the previous default provider.
         this.removeAttribute("src");
       }
 
-      let headerText = this.bundle.formatStringFromName("searchHeader", [currentEngine.name], 1);
+      let headerText = this.bundle.formatStringFromName("searchHeader", [currentEngine.name]);
       this.searchbarEngineName.setAttribute("value", headerText);
       this.searchbarEngine.engine = currentEngine;
     });
   }
 
   /**
    * This is called when a one-off is clicked and when "search in new tab"
    * is selected from a one-off context menu.
--- a/browser/components/search/content/search-one-offs.js
+++ b/browser/components/search/content/search-one-offs.js
@@ -647,17 +647,17 @@ class SearchOneOffs {
     for (let engine of engines) {
       let button = document.createXULElement(eltType);
       button.classList.add("addengine-item");
       if (!tooManyEngines) {
         button.classList.add("badged-button");
       }
       button.id = this.telemetryOrigin + "-add-engine-" +
         this._fixUpEngineNameForID(engine.title);
-      let label = this.bundle.formatStringFromName("cmd_addFoundEngine", [engine.title], 1);
+      let label = this.bundle.formatStringFromName("cmd_addFoundEngine", [engine.title]);
       button.setAttribute("label", label);
       button.setAttribute("crop", "end");
       button.setAttribute("tooltiptext", engine.title + "\n" + engine.uri);
       button.setAttribute("uri", engine.uri);
       button.setAttribute("title", engine.title);
       if (engine.icon) {
         button.setAttribute("image", engine.icon);
       }
@@ -1205,18 +1205,17 @@ class SearchOneOffs {
           let searchBundle = Services.strings.createBundle(kSearchBundleURI);
           let brandBundle = document.getElementById("bundle_brand");
           let brandName = brandBundle.getString("brandShortName");
           let title = searchBundle.GetStringFromName(
             "error_invalid_engine_title"
           );
           let text = searchBundle.formatStringFromName(
             "error_duplicate_engine_msg",
-            [brandName, target.getAttribute("uri")],
-            2
+            [brandName, target.getAttribute("uri")]
           );
           Services.prompt.QueryInterface(Ci.nsIPromptFactory);
           let prompt = Services.prompt.getPrompt(
             gBrowser.contentWindow,
             Ci.nsIPrompt
           );
           prompt.QueryInterface(Ci.nsIWritablePropertyBag2);
           prompt.setPropertyAsBool("allowTabModal", true);
--- a/browser/components/search/test/browser/browser_webapi.js
+++ b/browser/components/search/test/browser/browser_webapi.js
@@ -1,15 +1,15 @@
 var ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
 const searchBundle = Services.strings.createBundle("chrome://global/locale/search/search.properties");
 const brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
 const brandName = brandBundle.GetStringFromName("brandShortName");
 
 function getString(key, ...params) {
-  return searchBundle.formatStringFromName(key, params, params.length);
+  return searchBundle.formatStringFromName(key, params);
 }
 
 function AddSearchProvider(...args) {
   return BrowserTestUtils.addTab(gBrowser, ROOT + "webapi.html?" + encodeURIComponent(JSON.stringify(args)));
 }
 
 function promiseDialogOpened() {
   return new Promise((resolve, reject) => {
--- a/browser/components/translation/content/translation-notification.js
+++ b/browser/components/translation/content/translation-notification.js
@@ -295,17 +295,17 @@ class MozTranslationNotification extends
     let langName = Services.intl.getLanguageDisplayNames(undefined, [lang])[0];
 
     // Set the label and accesskey on the menuitem.
     let bundle =
       Services.strings.createBundle("chrome://browser/locale/translation.properties");
     let item = this._getAnonElt("neverForLanguage");
     const kStrId = "translation.options.neverForLanguage";
     item.setAttribute("label",
-      bundle.formatStringFromName(kStrId + ".label", [langName], 1));
+      bundle.formatStringFromName(kStrId + ".label", [langName]));
     item.setAttribute("accesskey",
       bundle.GetStringFromName(kStrId + ".accesskey"));
     item.langCode = lang;
 
     // We may need to disable the menuitems if they have already been used.
     // Check if translation is already disabled for this language:
     let neverForLangs =
       Services.prefs.getCharPref("browser.translation.neverForLanguages");
--- a/browser/components/urlbar/UrlbarProviderUnifiedComplete.jsm
+++ b/browser/components/urlbar/UrlbarProviderUnifiedComplete.jsm
@@ -263,17 +263,17 @@ function makeUrlbarResult(tokens, info) 
               false,
             ],
           })
         );
       case "keyword": {
         let title = info.comment;
         if (tokens && tokens.length > 1) {
           title = bundle.formatStringFromName("bookmarkKeywordSearch",
-            [info.comment, tokens.slice(1).map(t => t.value).join(" ")], 2);
+            [info.comment, tokens.slice(1).map(t => t.value).join(" ")]);
         }
         return new UrlbarResult(
           UrlbarUtils.RESULT_TYPE.KEYWORD,
           UrlbarUtils.RESULT_SOURCE.BOOKMARKS,
           ...UrlbarResult.payloadAndSimpleHighlights(tokens, {
             title: [title, true],
             url: [action.params.url, true],
             keyword: [info.firstToken.value, true],
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -570,17 +570,17 @@ class UrlbarView {
         setURL = true;
         break;
       case UrlbarUtils.RESULT_TYPE.REMOTE_TAB:
         action = result.payload.device;
         setURL = true;
         break;
       case UrlbarUtils.RESULT_TYPE.SEARCH:
         action = bundle.formatStringFromName("searchWithEngine",
-                                             [result.payload.engine], 1);
+                                             [result.payload.engine]);
         break;
       case UrlbarUtils.RESULT_TYPE.KEYWORD:
         isVisitAction = result.payload.input.trim() == result.payload.keyword;
         break;
       case UrlbarUtils.RESULT_TYPE.OMNIBOX:
         action = result.payload.content;
         break;
       default:
@@ -783,17 +783,17 @@ class UrlbarView {
       } else if (result.payload.originalEngine) {
         result.payload.engine = result.payload.originalEngine;
         delete result.payload.originalEngine;
       }
       let item = this._rows.children[i];
       let action = item.querySelector(".urlbarView-action");
       action.textContent =
         bundle.formatStringFromName("searchWithEngine",
-          [(engine && engine.name) || result.payload.engine], 1);
+          [(engine && engine.name) || result.payload.engine]);
       // If we just changed the engine from the original engine and it had an
       // icon, then make sure the result now uses the new engine's icon or
       // failing that the default icon.  If we changed it back to the original
       // engine, go back to the original or default icon.
       let favicon = item.querySelector(".urlbarView-favicon");
       if (engine && result.payload.icon) {
         favicon.src =
           (engine.iconURI && engine.iconURI.spec) ||
--- a/browser/components/urlbar/tests/browser/browser_search_favicon.js
+++ b/browser/components/urlbar/tests/browser/browser_search_favicon.js
@@ -46,13 +46,13 @@ add_task(async function() {
   await promiseAutocompleteResultPopup("foo");
   let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
 
   Assert.equal(result.type, UrlbarUtils.RESULT_TYPE.SEARCH);
   Assert.equal(result.displayed.title, "foobar");
 
   let bundle = Services.strings.createBundle("chrome://global/locale/autocomplete.properties");
   Assert.equal(result.displayed.action,
-    bundle.formatStringFromName("searchWithEngine", ["SearchEngine"], 1),
+    bundle.formatStringFromName("searchWithEngine", ["SearchEngine"]),
     "Should have the correct action text");
 
   gBrowser.removeCurrentTab();
 });
--- a/browser/components/urlbar/tests/browser/browser_urlbarPlaceholder.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarPlaceholder.js
@@ -10,17 +10,17 @@
 
 const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
 
 var originalEngine, extraEngine, expectedString;
 var tabs = [];
 
 add_task(async function setup() {
   originalEngine = await Services.search.getDefault();
-  expectedString = gBrowserBundle.formatStringFromName("urlbar.placeholder", [originalEngine.name], 1);
+  expectedString = gBrowserBundle.formatStringFromName("urlbar.placeholder", [originalEngine.name]);
 
   let rootDir = getRootDirectory(gTestPath);
   extraEngine = await SearchTestUtils.promiseNewSearchEngine(rootDir + TEST_ENGINE_BASENAME);
 
   // Force display of a tab with a URL bar, to clear out any possible placeholder
   // initialization listeners that happen on startup.
   let urlTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
   BrowserTestUtils.removeTab(urlTab);
--- a/browser/extensions/formautofill/FormAutofillDoorhanger.jsm
+++ b/browser/extensions/formautofill/FormAutofillDoorhanger.jsm
@@ -29,17 +29,17 @@ if (AppConstants.platform == "macosx") {
   changeAutofillOptsKey += "OSX";
   autofillOptsKey += "OSX";
   autofillSecurityOptionsKey += "OSX";
 }
 
 const CONTENT = {
   firstTimeUse: {
     notificationId: "autofill-address",
-    message: formatStringFromName("saveAddressesMessage", [brandShortName], 1),
+    message: formatStringFromName("saveAddressesMessage", [brandShortName]),
     anchor: {
       id: "autofill-address-notification-icon",
       URL: "chrome://formautofill/content/formfill-anchor.svg",
       tooltiptext: GetStringFromName("openAutofillMessagePanel"),
     },
     mainAction: {
       label: GetStringFromName(changeAutofillOptsKey),
       accessKey: GetStringFromName("changeAutofillOptionsAccessKey"),
@@ -92,17 +92,17 @@ const CONTENT = {
     options: {
       persistWhileVisible: true,
       popupIconURL: "chrome://formautofill/content/icon-address-update.svg",
       hideClose: true,
     },
   },
   addCreditCard: {
     notificationId: "autofill-credit-card",
-    message: formatStringFromName("saveCreditCardMessage", [brandShortName], 1),
+    message: formatStringFromName("saveCreditCardMessage", [brandShortName]),
     descriptionLabel: GetStringFromName("saveCreditCardDescriptionLabel"),
     descriptionIcon: true,
     linkMessage: GetStringFromName(autofillSecurityOptionsKey),
     spotlightURL: "about:preferences#privacy-credit-card-autofill",
     anchor: {
       id: "autofill-credit-card-notification-icon",
       URL: "chrome://formautofill/content/formfill-anchor.svg",
       tooltiptext: GetStringFromName("openAutofillMessagePanel"),
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -20,17 +20,17 @@ ChromeUtils.defineModuleGetter(this, "Fo
 ChromeUtils.defineModuleGetter(this, "FormAutofillHeuristics",
                                "resource://formautofill/FormAutofillHeuristics.jsm");
 ChromeUtils.defineModuleGetter(this, "FormLikeFactory",
                                "resource://gre/modules/FormLikeFactory.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "reauthPasswordPromptMessage", () => {
   const brandShortName = FormAutofillUtils.brandBundle.GetStringFromName("brandShortName");
   return FormAutofillUtils.stringBundle.formatStringFromName(
-    `useCreditCardPasswordPrompt.${AppConstants.platform}`, [brandShortName], 1);
+    `useCreditCardPasswordPrompt.${AppConstants.platform}`, [brandShortName]);
 });
 
 this.log = null;
 FormAutofill.defineLazyLogGetter(this, EXPORTED_SYMBOLS[0]);
 
 const {FIELD_STATES} = FormAutofillUtils;
 
 class FormAutofillSection {
--- a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
+++ b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
@@ -353,17 +353,17 @@ class CreditCardResult extends ProfileAu
 
   _generateLabels(focusedFieldName, allFieldNames, profiles) {
     if (!this._isSecure) {
       if (!insecureWarningEnabled) {
         return [];
       }
       let brandName = FormAutofillUtils.brandBundle.GetStringFromName("brandShortName");
 
-      return [FormAutofillUtils.stringBundle.formatStringFromName("insecureFieldWarningDescription", [brandName], 1)];
+      return [FormAutofillUtils.stringBundle.formatStringFromName("insecureFieldWarningDescription", [brandName])];
     }
 
     if (this._isInputAutofilled) {
       return [
         {primary: "", secondary: ""}, // Clear button
         {primary: "", secondary: ""}, // Footer
       ];
     }
--- a/browser/extensions/formautofill/content/customElements.js
+++ b/browser/extensions/formautofill/content/customElements.js
@@ -201,17 +201,17 @@
         let showCategories = hasExtraCategories ?
             orderedCategoryList.filter(category => categories.includes(category.id) && category.id != this._focusedCategory) :
             [orderedCategoryList.find(category => category.id == this._focusedCategory)];
 
         let separator = this._stringBundle.GetStringFromName("fieldNameSeparator");
         let warningTextTmplKey = hasExtraCategories ? "phishingWarningMessage" : "phishingWarningMessage2";
         let categoriesText = showCategories.map(category => this._stringBundle.GetStringFromName(category.l10nId)).join(separator);
 
-        this._warningTextBox.textContent = this._stringBundle.formatStringFromName(warningTextTmplKey, [categoriesText], 1);
+        this._warningTextBox.textContent = this._stringBundle.formatStringFromName(warningTextTmplKey, [categoriesText]);
         this.parentNode.parentNode.adjustHeight();
       };
 
       this._adjustAcItem();
     }
 
     _onCollapse() {
       /* global messageManager */
--- a/browser/extensions/formautofill/content/manageDialog.js
+++ b/browser/extensions/formautofill/content/manageDialog.js
@@ -21,17 +21,17 @@ ChromeUtils.defineModuleGetter(this, "fo
 ChromeUtils.defineModuleGetter(this, "FormAutofillUtils",
                                "resource://formautofill/FormAutofillUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "OSKeyStore",
                                "resource://formautofill/OSKeyStore.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "reauthPasswordPromptMessage", () => {
   const brandShortName = FormAutofillUtils.brandBundle.GetStringFromName("brandShortName");
   return FormAutofillUtils.stringBundle.formatStringFromName(
-    `editCreditCardPasswordPrompt.${AppConstants.platform}`, [brandShortName], 1);
+    `editCreditCardPasswordPrompt.${AppConstants.platform}`, [brandShortName]);
 });
 
 this.log = null;
 FormAutofill.defineLazyLogGetter(this, "manageAddresses");
 
 class ManageRecords {
   constructor(subStorageName, elements) {
     this._storageInitPromise = formAutofillStorage.initialize();
--- a/browser/extensions/fxmonitor/privileged/api.js
+++ b/browser/extensions/fxmonitor/privileged/api.js
@@ -87,17 +87,17 @@ this.FirefoxMonitor = {
     return this.extension.getURL(aPath);
   },
 
   getString(aKey) {
     return this.strings.GetStringFromName(aKey);
   },
 
   getFormattedString(aKey, args) {
-    return this.strings.formatStringFromName(aKey, args, args.length);
+    return this.strings.formatStringFromName(aKey, args);
   },
 
   // We used to persist the list of hosts we've already warned the
   // user for in this pref. Now, we check the pref at init and
   // if it has a value, migrate the remembered hosts to content prefs
   // and clear this one.
   kWarnedHostsPref: "extensions.fxmonitor.warnedHosts",
   migrateWarnedHostsIfNeeded() {
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -502,17 +502,17 @@ var ContentSearch = {
       data,
     }];
   },
 
   async _currentEngineObj() {
     let engine = Services.search.defaultEngine;
     let favicon = engine.getIconURLBySize(16, 16);
     let placeholder = this._stringBundle.formatStringFromName(
-      "searchWithEngine", [engine.name], 1);
+      "searchWithEngine", [engine.name]);
     let obj = {
       name: engine.name,
       placeholder,
       iconData: await this._maybeConvertURIToArrayBuffer(favicon),
     };
     return obj;
   },
 
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -286,17 +286,17 @@ var ExtensionsUI = {
 
       let strings = {};
       strings.acceptText = bundle.GetStringFromName("webext.defaultSearchYes.label");
       strings.acceptKey = bundle.GetStringFromName("webext.defaultSearchYes.accessKey");
       strings.cancelText = bundle.GetStringFromName("webext.defaultSearchNo.label");
       strings.cancelKey = bundle.GetStringFromName("webext.defaultSearchNo.accessKey");
       strings.addonName = name;
       strings.text = bundle.formatStringFromName("webext.defaultSearch.description",
-                                                 ["<>", currentEngine, newEngine], 3);
+                                                 ["<>", currentEngine, newEngine]);
 
       this.showDefaultSearchPrompt(browser, strings, icon).then(respond);
     }
   },
 
   // Create a set of formatted strings for a permission prompt
   _buildStrings(info) {
     let bundle = Services.strings.createBundle(BROWSER_PROPERTIES);
--- a/browser/modules/LiveBookmarkMigrator.jsm
+++ b/browser/modules/LiveBookmarkMigrator.jsm
@@ -107,17 +107,17 @@ var LiveBookmarkMigrator = {
       // Create head:
       let doc = hiddenDOMDoc.implementation.createDocument("", "opml", null);
       let root = doc.documentElement;
       root.setAttribute("version", "1.0");
       let head = doc.createElement("head");
       root.appendChild(head);
       let title = doc.createElement("title");
       title.textContent =
-        gBrowserBundle.formatStringFromName("livebookmarkMigration.title", [appName], 1);
+        gBrowserBundle.formatStringFromName("livebookmarkMigration.title", [appName]);
       head.appendChild(title);
 
       let body = doc.createElement("body");
       root.appendChild(body);
       // Make things vaguely readable:
       body.textContent = "\n";
 
       for (let lb of liveBookmarks) {
--- a/browser/modules/OpenInTabsUtils.jsm
+++ b/browser/modules/OpenInTabsUtils.jsm
@@ -18,17 +18,17 @@ XPCOMUtils.defineLazyGetter(this, "bundl
  * called without any tabbrowser instance.
  */
 this.OpenInTabsUtils = {
   getString(key) {
     return bundle.GetStringFromName(key);
   },
 
   getFormattedString(key, params) {
-    return bundle.formatStringFromName(key, params, params.length);
+    return bundle.formatStringFromName(key, params);
   },
 
   /**
    * Gives the user a chance to cancel loading lots of tabs at once.
    */
   confirmOpenInTabs(numTabsToOpen, aWindow) {
     const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen";
     const MAX_OPNE_PREF = "browser.tabs.maxOpenBeforeWarn";
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -722,17 +722,17 @@ GeolocationPermissionPrompt.prototype = 
   },
 
   get message() {
     if (this.principal.URI.schemeIs("file")) {
       return gBrowserBundle.GetStringFromName("geolocation.shareWithFile3");
     }
 
     return gBrowserBundle.formatStringFromName("geolocation.shareWithSite3",
-                                               ["<>"], 1);
+                                               ["<>"]);
   },
 
   get promptActions() {
     return [{
       label: gBrowserBundle.GetStringFromName("geolocation.allowLocation"),
       accessKey:
         gBrowserBundle.GetStringFromName("geolocation.allowLocation.accesskey"),
       action: SitePermissions.ALLOW,
@@ -792,17 +792,17 @@ DesktopNotificationPermissionPrompt.prot
   },
 
   get anchorID() {
     return "web-notifications-notification-icon";
   },
 
   get message() {
     return gBrowserBundle.formatStringFromName("webNotifications.receiveFromSite2",
-                                                    ["<>"], 1);
+                                                    ["<>"]);
   },
 
   get promptActions() {
     let actions = [
       {
         label: gBrowserBundle.GetStringFromName("webNotifications.allow"),
         accessKey:
           gBrowserBundle.GetStringFromName("webNotifications.allow.accesskey"),
@@ -882,17 +882,17 @@ PersistentStoragePermissionPrompt.protot
   },
 
   get anchorID() {
     return "persistent-storage-notification-icon";
   },
 
   get message() {
     return gBrowserBundle.formatStringFromName("persistentStorage.allowWithSite",
-                                                    ["<>"], 1);
+                                                    ["<>"]);
   },
 
   get promptActions() {
     return [
       {
         label: gBrowserBundle.GetStringFromName("persistentStorage.allow"),
         accessKey:
           gBrowserBundle.GetStringFromName("persistentStorage.allow.accesskey"),
@@ -980,20 +980,20 @@ MIDIPermissionPrompt.prototype = {
     if (this.principal.URI.schemeIs("file")) {
       if (this.isSysexPerm) {
         message = gBrowserBundle.formatStringFromName("midi.shareSysexWithFile.message");
       } else {
         message = gBrowserBundle.formatStringFromName("midi.shareWithFile.message");
       }
     } else if (this.isSysexPerm) {
       message = gBrowserBundle.formatStringFromName("midi.shareSysexWithSite.message",
-                                                    ["<>"], 1);
+                                                    ["<>"]);
     } else {
       message = gBrowserBundle.formatStringFromName("midi.shareWithSite.message",
-                                                    ["<>"], 1);
+                                                    ["<>"]);
     }
     return message;
   },
 
   get promptActions() {
     return [{
         label: gBrowserBundle.GetStringFromName("midi.Allow.label"),
         accessKey: gBrowserBundle.GetStringFromName("midi.Allow.accesskey"),
@@ -1051,17 +1051,17 @@ StorageAccessPermissionPrompt.prototype 
       escAction: "buttoncommand",
     };
   },
 
   onShown() {
     let document = this.browser.ownerDocument;
     let label =
       gBrowserBundle.formatStringFromName("storageAccess.description.label",
-                                          [this.prettifyHostPort(this.request.principal.URI), "<>"], 2);
+                                          [this.prettifyHostPort(this.request.principal.URI), "<>"]);
     let parts = label.split("<>");
     if (parts.length == 1) {
       parts.push("");
     }
     let map = {
       "storage-access-perm-label": parts[0],
       "storage-access-perm-learnmore":
         gBrowserBundle.GetStringFromName("storageAccess.description.learnmore"),
@@ -1081,17 +1081,17 @@ StorageAccessPermissionPrompt.prototype 
     return "storage-access";
   },
 
   get anchorID() {
     return "storage-access-notification-icon";
   },
 
   get message() {
-    return gBrowserBundle.formatStringFromName("storageAccess.message", ["<>", "<>"], 2);
+    return gBrowserBundle.formatStringFromName("storageAccess.message", ["<>", "<>"]);
   },
 
   get promptActions() {
     let self = this;
 
     let storageAccessHistogram = Services.telemetry.getHistogramById("STORAGE_ACCESS_API_UI");
 
     return [{
--- a/browser/modules/SiteDataManager.jsm
+++ b/browser/modules/SiteDataManager.jsm
@@ -391,17 +391,17 @@ var SiteDataManager = {
     }
 
     let brandName = gBrandBundle.GetStringFromName("brandShortName");
     let flags =
       Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
       Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1 +
       Services.prompt.BUTTON_POS_0_DEFAULT;
     let title = gStringBundle.GetStringFromName("clearSiteDataPromptTitle");
-    let text = gStringBundle.formatStringFromName("clearSiteDataPromptText", [brandName], 1);
+    let text = gStringBundle.formatStringFromName("clearSiteDataPromptText", [brandName]);
     let btn0Label = gStringBundle.GetStringFromName("clearSiteDataNow");
 
     let result = Services.prompt.confirmEx(
       win, title, text, flags, btn0Label, null, null, null, {});
     return result == 0;
   },
 
   /**
--- a/browser/modules/test/browser/browser_ContentSearch.js
+++ b/browser/modules/test/browser/browser_ContentSearch.js
@@ -379,17 +379,17 @@ var currentStateObj = async function() {
 };
 
 var defaultEngineObj = async function() {
   let engine = await Services.search.getDefault();
   let uriFavicon = engine.getIconURLBySize(16, 16);
   let bundle = Services.strings.createBundle("chrome://global/locale/autocomplete.properties");
   return {
     name: engine.name,
-    placeholder: bundle.formatStringFromName("searchWithEngine", [engine.name], 1),
+    placeholder: bundle.formatStringFromName("searchWithEngine", [engine.name]),
     iconData: await iconDataFromURI(uriFavicon),
   };
 };
 
 function iconDataFromURI(uri) {
   if (!uri) {
     return Promise.resolve(null);
   }
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -997,17 +997,17 @@ function getGlobalIndicator() {
         Services.strings.createBundle("chrome://browser/locale/webrtcIndicator.properties");
 
       if (activeStreams.length == 1) {
         let stream = activeStreams[0];
 
         let menuitem = this.ownerDocument.createXULElement("menuitem");
         let labelId = "webrtcIndicator.sharing" + type + "With.menuitem";
         let label = stream.browser.contentTitle || stream.uri;
-        menuitem.setAttribute("label", bundle.formatStringFromName(labelId, [label], 1));
+        menuitem.setAttribute("label", bundle.formatStringFromName(labelId, [label]));
         menuitem.setAttribute("disabled", "true");
         this.appendChild(menuitem);
 
         menuitem = this.ownerDocument.createXULElement("menuitem");
         menuitem.setAttribute("label",
                               bundle.GetStringFromName("webrtcIndicator.controlSharing.menuitem"));
         menuitem.stream = stream;
         menuitem.addEventListener("command", indicator._command);
@@ -1024,17 +1024,17 @@ function getGlobalIndicator() {
       menuitem.setAttribute("label", label);
       menuitem.setAttribute("disabled", "true");
       this.appendChild(menuitem);
 
       for (let stream of activeStreams) {
         let item = this.ownerDocument.createXULElement("menuitem");
         labelId = "webrtcIndicator.controlSharingOn.menuitem";
         label = stream.browser.contentTitle || stream.uri;
-        item.setAttribute("label", bundle.formatStringFromName(labelId, [label], 1));
+        item.setAttribute("label", bundle.formatStringFromName(labelId, [label]));
         item.stream = stream;
         item.addEventListener("command", indicator._command);
         this.appendChild(item);
       }
 
       return true;
     },
 
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -979,20 +979,20 @@ nsresult nsScriptSecurityManager::CheckL
   rv = NS_URIChainHasFlags(aTargetBaseURI,
                            nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
                            &hasSubsumersFlag);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!hasFlags && !hasSubsumersFlag) {
     nsCOMPtr<nsIStringBundle> bundle = BundleHelper::GetOrCreate();
     if (bundle) {
       nsAutoString message;
-      NS_ConvertASCIItoUTF16 ucsTargetScheme(targetScheme);
-      const char16_t* formatStrings[] = {ucsTargetScheme.get()};
+      AutoTArray<nsString, 1> formatStrings;
+      CopyASCIItoUTF16(targetScheme, *formatStrings.AppendElement());
       rv = bundle->FormatStringFromName("ProtocolFlagError", formatStrings,
-                                        ArrayLength(formatStrings), message);
+                                        message);
       if (NS_SUCCEEDED(rv)) {
         nsCOMPtr<nsIConsoleService> console(
             do_GetService("@mozilla.org/consoleservice;1"));
         NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
 
         console->LogStringMessage(message.get());
       }
     }
@@ -1019,21 +1019,20 @@ nsresult nsScriptSecurityManager::Report
 
   nsCOMPtr<nsIStringBundle> bundle = BundleHelper::GetOrCreate();
   if (NS_WARN_IF(!bundle)) {
     return NS_OK;
   }
 
   // Localize the error message
   nsAutoString message;
-  NS_ConvertASCIItoUTF16 ucsSourceSpec(sourceSpec);
-  NS_ConvertASCIItoUTF16 ucsTargetSpec(targetSpec);
-  const char16_t* formatStrings[] = {ucsSourceSpec.get(), ucsTargetSpec.get()};
-  rv = bundle->FormatStringFromName(aMessageTag, formatStrings,
-                                    ArrayLength(formatStrings), message);
+  AutoTArray<nsString, 2> formatStrings;
+  CopyASCIItoUTF16(sourceSpec, *formatStrings.AppendElement());
+  CopyASCIItoUTF16(targetSpec, *formatStrings.AppendElement());
+  rv = bundle->FormatStringFromName(aMessageTag, formatStrings, message);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIConsoleService> console(
       do_GetService(NS_CONSOLESERVICE_CONTRACTID));
   NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
   nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
   NS_ENSURE_TRUE(error, NS_ERROR_FAILURE);
 
@@ -1284,23 +1283,23 @@ nsScriptSecurityManager::CanCreateWrappe
   if (NS_WARN_IF(!bundle)) {
     return NS_OK;
   }
 
   NS_ConvertUTF8toUTF16 classInfoUTF16(classInfoNameUTF8);
   nsresult rv;
   nsAutoString errorMsg;
   if (originUTF16.IsEmpty()) {
-    const char16_t* formatStrings[] = {classInfoUTF16.get()};
-    rv = bundle->FormatStringFromName("CreateWrapperDenied", formatStrings, 1,
+    AutoTArray<nsString, 1> formatStrings = {classInfoUTF16};
+    rv = bundle->FormatStringFromName("CreateWrapperDenied", formatStrings,
                                       errorMsg);
   } else {
-    const char16_t* formatStrings[] = {classInfoUTF16.get(), originUTF16.get()};
+    AutoTArray<nsString, 2> formatStrings = {classInfoUTF16, originUTF16};
     rv = bundle->FormatStringFromName("CreateWrapperDeniedForOrigin",
-                                      formatStrings, 2, errorMsg);
+                                      formatStrings, errorMsg);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   SetPendingException(cx, errorMsg.get());
   return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
 }
 
 NS_IMETHODIMP
--- a/devtools/client/aboutdebugging/components/Aboutdebugging.js
+++ b/devtools/client/aboutdebugging/components/Aboutdebugging.js
@@ -104,17 +104,17 @@ class AboutDebuggingApp extends Componen
       panel = selectedPanel.component({ client, connect, id: selectedPanel.id });
     } else {
       panel = (
         dom.div({ className: "error-page" },
           dom.h1({ className: "header-name" },
             Strings.GetStringFromName("pageNotFound")
           ),
           dom.h4({ className: "error-page-details" },
-            Strings.formatStringFromName("doesNotExist", [selectedPanelId], 1))
+            Strings.formatStringFromName("doesNotExist", [selectedPanelId]))
         )
       );
     }
 
     return dom.div({ className: "app" },
       PanelMenu({ panels, selectedPanelId, selectPanel }),
       dom.div({ className: "main-content" }, panel)
     );
--- a/devtools/client/aboutdebugging/components/addons/InstallError.js
+++ b/devtools/client/aboutdebugging/components/addons/InstallError.js
@@ -22,17 +22,17 @@ class AddonsInstallError extends Compone
     };
   }
 
   render() {
     const { error } = this.props;
     if (!this.props.error) {
       return null;
     }
-    const text = Strings.formatStringFromName("addonInstallError", [error.message], 1);
+    const text = Strings.formatStringFromName("addonInstallError", [error.message]);
 
     const additionalErrors = Array.isArray(error.additionalErrors) ?
       dom.div(
         { className: "addons-install-error__additional-errors" },
         error.additionalErrors.join(" ")
       ) : null;
 
     return dom.div(
--- a/devtools/client/inspector/test/browser_inspector_navigate_to_errors.js
+++ b/devtools/client/inspector/test/browser_inspector_navigate_to_errors.js
@@ -23,27 +23,25 @@ add_task(async function() {
   const hasPage = await getNodeFront("#test-doc-1", inspector);
   ok(!hasPage, "Inspector actor is no longer able to reach previous page DOM node");
 
   const hasNetErrorNode = await getNodeFront("#errorShortDesc", inspector);
   ok(hasNetErrorNode, "Inspector actor is able to reach error page DOM node");
 
   const bundle = Services.strings.createBundle("chrome://global/locale/appstrings.properties");
   let domain = TEST_URL_2.match(/^http:\/\/(.*)\/$/)[1];
-  let errorMsg = bundle.formatStringFromName("connectionFailure",
-                                             [domain], 1);
+  let errorMsg = bundle.formatStringFromName("connectionFailure", [domain]);
   is(await getDisplayedNodeTextContent("#errorShortDescText", inspector), errorMsg,
      "Inpector really inspects the error page");
 
   info("Navigate to unknown domain");
   await navigateTo(inspector, TEST_URL_3);
 
   domain = TEST_URL_3.match(/^http:\/\/(.*)\/$/)[1];
-  errorMsg = bundle.formatStringFromName("dnsNotFound2",
-                                         [domain], 1);
+  errorMsg = bundle.formatStringFromName("dnsNotFound2", [domain]);
   is(await getDisplayedNodeTextContent("#errorShortDescText", inspector), errorMsg,
      "Inspector really inspects the new error page");
 
   info("Navigate to a valid url");
   await navigateTo(inspector, TEST_URL_4);
 
   is(await getDisplayedNodeTextContent("body", inspector), "test-doc-4",
      "Inspector really inspects the valid url");
--- a/devtools/client/scratchpad/scratchpad.js
+++ b/devtools/client/scratchpad/scratchpad.js
@@ -989,17 +989,17 @@ var Scratchpad = {
     aCharsetArray.some(charset => {
       try {
         converter.charset = charset;
         content = converter.ConvertToUnicode(aContent);
         return true;
       } catch (e) {
         this.notificationBox.appendNotification(
             this.strings.formatStringFromName("importFromFile.convert.failed",
-                                              [ charset ], 1),
+                                              [ charset ]),
             "file-import-convert-failed",
             null,
             this.notificationBox.PRIORITY_WARNING_HIGH,
             null);
       }
     });
     return content;
   },
@@ -1038,18 +1038,17 @@ var Scratchpad = {
         if (Components.isSuccessCode(aStatus)) {
           const charsets = this._getApplicableCharsets();
           content = NetUtil.readInputStreamToString(aInputStream,
             aInputStream.available());
           content = this._getUnicodeContent(content, charsets);
           if (!content) {
             const message = this.strings.formatStringFromName(
               "importFromFile.convert.failed",
-              [charsets.join(", ")],
-              1);
+              [charsets.join(", ")]);
             this.notificationBox.appendNotification(
               message,
               "file-import-convert-failed",
               null,
               this.notificationBox.PRIORITY_CRITICAL_MEDIUM,
               null);
             if (aCallback) {
               aCallback.call(this, aStatus, content);
@@ -1513,17 +1512,17 @@ var Scratchpad = {
   getInnerWindowId: function SP_getInnerWindowId(aWindow) {
     return aWindow.windowUtils.currentInnerWindowID;
   },
 
   updateStatusBar: function SP_updateStatusBar(aEventType) {
     var statusBarField = document.getElementById("statusbar-line-col");
     const { line, ch } = this.editor.getCursor();
     statusBarField.textContent = this.strings.formatStringFromName(
-      "scratchpad.statusBarLineCol", [ line + 1, ch + 1], 2);
+      "scratchpad.statusBarLineCol", [ line + 1, ch + 1]);
   },
 
   /**
    * The Scratchpad window load event handler. This method
    * initializes the Scratchpad window and source editor.
    *
    * @param Event aEvent
    */
@@ -1545,18 +1544,17 @@ var Scratchpad = {
       chromeContextCommand.removeAttribute("disabled");
       errorConsoleCommand.removeAttribute("disabled");
     }
 
     let initialText = this.strings.formatStringFromName(
       "scratchpadIntro1",
       [ShortcutUtils.prettifyShortcut(document.getElementById("sp-key-run"), true),
        ShortcutUtils.prettifyShortcut(document.getElementById("sp-key-inspect"), true),
-       ShortcutUtils.prettifyShortcut(document.getElementById("sp-key-display"), true)],
-      3);
+       ShortcutUtils.prettifyShortcut(document.getElementById("sp-key-display"), true)]);
 
     let args = window.arguments;
     let state = null;
 
     if (args && args[0] instanceof Ci.nsIDialogParamBlock) {
       args = args[0];
       this._instanceId = args.GetString(0);
 
@@ -1589,17 +1587,17 @@ var Scratchpad = {
 
       this.editor.setFontSize(Services.prefs.getIntPref(EDITOR_FONT_SIZE));
 
       this.editor.on("change", this._onChanged);
       // Keep a reference to the bound version for use in onUnload.
       this.updateStatusBar = Scratchpad.updateStatusBar.bind(this);
       this.editor.on("cursorActivity", this.updateStatusBar);
       const okstring = this.strings.GetStringFromName("selfxss.okstring");
-      const msg = this.strings.formatStringFromName("selfxss.msg", [okstring], 1);
+      const msg = this.strings.formatStringFromName("selfxss.msg", [okstring]);
       this._onPaste = pasteHandlerGen(this.editor.container.contentDocument.body,
         this.notificationBox, msg, okstring);
 
       editorElement.addEventListener("paste", this._onPaste, true);
       editorElement.addEventListener("drop", this._onPaste);
       this.editor.on("saveRequested", () => this.saveFile());
       this.editor.focus();
       this.editor.setCursor({ line: lines.length, ch: lines.pop().length });
--- a/devtools/client/scratchpad/test/browser_scratchpad_run_error_goto_line.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_run_error_goto_line.js
@@ -43,15 +43,15 @@ function runTests(sw) {
     is(cursor.ch + 1, 1, "jumpToLine goto error location (column)");
   }, error => {
     ok(false, error);
     finish();
   }).then(() => {
     var statusBarField = sp.editor.container.ownerDocument.querySelector("#statusbar-line-col");
     const { line, ch } = sp.editor.getCursor();
     is(statusBarField.textContent, sp.strings.formatStringFromName(
-      "scratchpad.statusBarLineCol", [ line + 1, ch + 1], 2), "statusbar text is correct (" + statusBarField.textContent + ")");
+      "scratchpad.statusBarLineCol", [ line + 1, ch + 1]), "statusbar text is correct (" + statusBarField.textContent + ")");
     finish();
   }, error => {
     ok(false, error);
     finish();
   });
 }
--- a/devtools/client/styleeditor/StyleEditorUtil.jsm
+++ b/devtools/client/styleeditor/StyleEditorUtil.jsm
@@ -39,17 +39,17 @@ const PREF_ORIG_SOURCES = "devtools.sour
  * @return string
  */
 function getString(name) {
   try {
     if (arguments.length == 1) {
       return gStringBundle.GetStringFromName(name);
     }
     const rest = Array.prototype.slice.call(arguments, 1);
-    return gStringBundle.formatStringFromName(name, rest, rest.length);
+    return gStringBundle.formatStringFromName(name, rest);
   } catch (ex) {
     console.error(ex);
     throw new Error("L10N error. '" + name + "' is missing from " +
                     PROPERTIES_URL);
   }
 }
 
 /**
--- a/devtools/client/webide/content/webide.js
+++ b/devtools/client/webide/content/webide.js
@@ -206,17 +206,17 @@ var UI = {
 
   openInBrowser: function(url) {
     openContentLink(url);
   },
 
   updateTitle: function() {
     const project = AppManager.selectedProject;
     if (project) {
-      window.document.title = Strings.formatStringFromName("title_app", [project.name], 1);
+      window.document.title = Strings.formatStringFromName("title_app", [project.name]);
     } else {
       window.document.title = Strings.GetStringFromName("title_noApp");
     }
   },
 
   /** ******** BUSY UI **********/
 
   _busyTimeout: null,
@@ -292,17 +292,17 @@ var UI = {
     });
     return promise;
   },
 
   reportError: function(l10nProperty, ...l10nArgs) {
     let text;
 
     if (l10nArgs.length > 0) {
-      text = Strings.formatStringFromName(l10nProperty, l10nArgs, l10nArgs.length);
+      text = Strings.formatStringFromName(l10nProperty, l10nArgs);
     } else {
       text = Strings.GetStringFromName(l10nProperty);
     }
 
     console.error(text);
 
     const buttons = [{
       label: Strings.GetStringFromName("notification_showTroubleShooting_label"),
--- a/devtools/client/webide/modules/app-manager.js
+++ b/devtools/client/webide/modules/app-manager.js
@@ -125,17 +125,17 @@ var AppManager = exports.AppManager = {
 
   reportError: function(l10nProperty, ...l10nArgs) {
     const win = Services.wm.getMostRecentWindow("devtools:webide");
     if (win) {
       win.UI.reportError(l10nProperty, ...l10nArgs);
     } else {
       let text;
       if (l10nArgs.length > 0) {
-        text = Strings.formatStringFromName(l10nProperty, l10nArgs, l10nArgs.length);
+        text = Strings.formatStringFromName(l10nProperty, l10nArgs);
       } else {
         text = Strings.GetStringFromName(l10nProperty);
       }
       console.error(text);
     }
   },
 
   onConnectionChanged: async function() {
--- a/devtools/client/webide/modules/app-validator.js
+++ b/devtools/client/webide/modules/app-validator.js
@@ -66,41 +66,41 @@ AppValidator.checkManifest = function(ma
 
     const req = new XMLHttpRequest();
     req.overrideMimeType("text/plain");
 
     try {
       req.open("GET", manifestURL, true);
       req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
     } catch (e) {
-      error = strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1);
+      error = strings.formatStringFromName("validator.invalidManifestURL", [manifestURL]);
       return reject(error);
     }
 
     req.onload = function() {
       let manifest = null;
       try {
         manifest = JSON.parse(req.responseText);
       } catch (e) {
-        error = strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2);
+        error = strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL]);
         reject(error);
       }
 
       resolve({manifest, manifestURL});
     };
 
     req.onerror = function() {
-      error = strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2);
+      error = strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL]);
       reject(error);
     };
 
     try {
       req.send(null);
     } catch (e) {
-      error = strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL], 2);
+      error = strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL]);
       reject(error);
     }
   });
 };
 
 AppValidator.findManifestAtOrigin = function(manifestURL) {
   const fixedManifest = Services.io.newURI(manifestURL).prePath + "/manifest.webapp";
   return AppValidator.checkManifest(fixedManifest);
@@ -157,21 +157,21 @@ AppValidator.prototype._getManifest = fu
     if (!manifestURL) {
       return Promise.resolve(null);
     }
   } else if (this.type == "hosted") {
     manifestURL = this.location;
     try {
       Services.io.newURI(manifestURL);
     } catch (e) {
-      this.error(strings.formatStringFromName("validator.invalidHostedManifestURL", [manifestURL, e.message], 2));
+      this.error(strings.formatStringFromName("validator.invalidHostedManifestURL", [manifestURL, e.message]));
       return Promise.resolve(null);
     }
   } else {
-    this.error(strings.formatStringFromName("validator.invalidProjectType", [this.type], 1));
+    this.error(strings.formatStringFromName("validator.invalidProjectType", [this.type]));
     return Promise.resolve(null);
   }
   return this._fetchManifest(manifestURL);
 };
 
 AppValidator.prototype.validateManifest = function(manifest) {
   if (!manifest.name) {
     this.error(strings.GetStringFromName("validator.missNameManifestProperty"));
@@ -192,70 +192,70 @@ AppValidator.prototype._getOriginURL = f
     return Services.io.newURI(this.location).prePath;
   }
 };
 
 AppValidator.prototype.validateLaunchPath = function(manifest) {
   return new Promise(resolve => {
     // The launch_path field has to start with a `/`
     if (manifest.launch_path && manifest.launch_path[0] !== "/") {
-      this.error(strings.formatStringFromName("validator.nonAbsoluteLaunchPath", [manifest.launch_path], 1));
+      this.error(strings.formatStringFromName("validator.nonAbsoluteLaunchPath", [manifest.launch_path]));
       resolve();
     }
     const origin = this._getOriginURL();
     let path;
     if (this.type == "packaged") {
       path = "." + (manifest.launch_path || "/index.html");
     } else if (this.type == "hosted") {
       path = manifest.launch_path || "/";
     }
     let indexURL;
     try {
       indexURL = Services.io.newURI(path, null, Services.io.newURI(origin)).spec;
     } catch (e) {
-      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [origin + path], 1));
+      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [origin + path]));
       return resolve();
     }
 
     const req = new XMLHttpRequest();
     req.overrideMimeType("text/plain");
     try {
       req.open("HEAD", indexURL, true);
       req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
     } catch (e) {
-      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
+      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL]));
       return resolve();
     }
     req.onload = () => {
       if (req.status >= 400) {
-        this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2));
+        this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status]));
       }
       resolve();
     };
     req.onerror = () => {
-      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
+      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL]));
       resolve();
     };
 
     try {
       req.send(null);
     } catch (e) {
-      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
+      this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL]));
       resolve();
     }
   });
 };
 
 AppValidator.prototype.validateType = function(manifest) {
   const appType = manifest.type || "web";
   if (!["web", "privileged", "certified"].includes(appType)) {
-    this.error(strings.formatStringFromName("validator.invalidAppType", [appType], 1));
+    this.error(strings.formatStringFromName("validator.invalidAppType", [appType]));
   } else if (this.type == "hosted" &&
              ["certified", "privileged"].includes(appType)) {
-    this.error(strings.formatStringFromName("validator.invalidHostedPriviledges", [appType], 1));
+    this.error(strings.formatStringFromName("validator.invalidHostedPriviledges", [appType]));
   }
 
   // certified app are not fully supported on the simulator
   if (appType === "certified") {
     this.warning(strings.GetStringFromName("validator.noCertifiedSupport"));
   }
 };
 
--- a/devtools/client/webide/modules/runtime-list.js
+++ b/devtools/client/webide/modules/runtime-list.js
@@ -135,17 +135,17 @@ RuntimeList.prototype = {
     }
 
     const usbListNode = doc.querySelector("#runtime-panel-usb");
     const wifiListNode = doc.querySelector("#runtime-panel-wifi");
     const otherListNode = doc.querySelector("#runtime-panel-other");
     const noADBExtensionNode = doc.querySelector("#runtime-panel-noadbextension");
     const noUSBNode = doc.querySelector("#runtime-panel-nousbdevice");
     noADBExtensionNode.textContent =
-      Strings.formatStringFromName("runtimePanel_noadbextension", ["ADB Extension"], 1);
+      Strings.formatStringFromName("runtimePanel_noadbextension", ["ADB Extension"]);
 
     if (adbAddon.status === ADB_ADDON_STATES.INSTALLED) {
       noADBExtensionNode.setAttribute("hidden", "true");
     } else {
       noADBExtensionNode.removeAttribute("hidden");
     }
 
     const runtimeList = AppManager.runtimeList;
--- a/devtools/client/webide/test/test_app_validator.html
+++ b/devtools/client/webide/test/test_app_validator.html
@@ -87,53 +87,53 @@
         });
       },
 
       // Test a launch path that returns a 404
       function() {
         const validator = createHosted("wrong-launch-path");
         validator.validate().then(() => {
           is(validator.errors.length, 1, "app with non-existant launch path got an error");
-          is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [fakeOrigin + "wrong-path.html", 404], 2),
+          is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [fakeOrigin + "wrong-path.html", 404]),
                "with the right error message");
           is(validator.warnings.length, 0, "but no warning");
           next();
         });
       },
       function() {
         const validator = createPackaged("wrong-launch-path");
         validator.validate().then(() => {
           is(validator.errors.length, 1, "app with wrong path got an error");
           const file = nsFile(validator.location);
           file.append("wrong-path.html");
           const url = Services.io.newFileURI(file);
-          is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPath", [url.spec], 1),
+          is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPath", [url.spec]),
                "with the expected message");
           is(validator.warnings.length, 0, "but no warning");
 
           next();
         });
       },
 
       // Test when using a non-absolute path for launch_path
       function() {
         const validator = createHosted("non-absolute-path");
         validator.validate().then(() => {
           is(validator.errors.length, 1, "app with non absolute path got an error");
-          is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1),
+          is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"]),
                "with expected message");
           is(validator.warnings.length, 0, "but no warning");
           next();
         });
       },
       function() {
         const validator = createPackaged("non-absolute-path");
         validator.validate().then(() => {
           is(validator.errors.length, 1, "app with non absolute path got an error");
-          is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1),
+          is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"]),
                "with expected message");
           is(validator.warnings.length, 0, "but no warning");
           next();
         });
       },
 
       // Test multiple failures (missing name [error] and icon [warning])
       function() {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4062,63 +4062,59 @@ nsDocShell::DisplayLoadError(nsresult aE
 
   NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
   NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
 
   const char* error = nullptr;
   // The key used to select the appropriate error message from the properties
   // file.
   const char* errorDescriptionID = nullptr;
-  const uint32_t kMaxFormatStrArgs = 3;
-  nsAutoString formatStrs[kMaxFormatStrArgs];
-  uint32_t formatStrCount = 0;
+  AutoTArray<nsString, 3> formatStrs;
   bool addHostPort = false;
   nsresult rv = NS_OK;
   nsAutoString messageStr;
   nsAutoCString cssClass;
   nsAutoCString errorPage;
 
   errorPage.AssignLiteral("neterror");
 
   // Turn the error code into a human readable error message.
   if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
     NS_ENSURE_ARG_POINTER(aURI);
 
     // Extract the schemes into a comma delimited list.
     nsAutoCString scheme;
     aURI->GetScheme(scheme);
-    CopyASCIItoUTF16(scheme, formatStrs[0]);
+    CopyASCIItoUTF16(scheme, *formatStrs.AppendElement());
     nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
     while (nestedURI) {
       nsCOMPtr<nsIURI> tempURI;
       nsresult rv2;
       rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
       if (NS_SUCCEEDED(rv2) && tempURI) {
         tempURI->GetScheme(scheme);
         formatStrs[0].AppendLiteral(", ");
         AppendASCIItoUTF16(scheme, formatStrs[0]);
       }
       nestedURI = do_QueryInterface(tempURI);
     }
-    formatStrCount = 1;
     error = "unknownProtocolFound";
   } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
     NS_ENSURE_ARG_POINTER(aURI);
     error = "fileNotFound";
   } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
     NS_ENSURE_ARG_POINTER(aURI);
     error = "fileAccessDenied";
   } else if (NS_ERROR_UNKNOWN_HOST == aError) {
     NS_ENSURE_ARG_POINTER(aURI);
     // Get the host
     nsAutoCString host;
     nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
     innermostURI->GetHost(host);
-    CopyUTF8toUTF16(host, formatStrs[0]);
-    formatStrCount = 1;
+    CopyUTF8toUTF16(host, *formatStrs.AppendElement());
     errorDescriptionID = "dnsNotFound2";
     error = "dnsNotFound";
   } else if (NS_ERROR_CONNECTION_REFUSED == aError ||
              NS_ERROR_PROXY_BAD_GATEWAY == aError) {
     NS_ENSURE_ARG_POINTER(aURI);
     addHostPort = true;
     error = "connectionFailure";
   } else if (NS_ERROR_NET_INTERRUPT == aError) {
@@ -4126,18 +4122,17 @@ nsDocShell::DisplayLoadError(nsresult aE
     addHostPort = true;
     error = "netInterrupt";
   } else if (NS_ERROR_NET_TIMEOUT == aError ||
              NS_ERROR_PROXY_GATEWAY_TIMEOUT == aError) {
     NS_ENSURE_ARG_POINTER(aURI);
     // Get the host
     nsAutoCString host;
     aURI->GetHost(host);
-    CopyUTF8toUTF16(host, formatStrs[0]);
-    formatStrCount = 1;
+    CopyUTF8toUTF16(host, *formatStrs.AppendElement());
     error = "netTimeout";
   } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
              NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) {
     // CSP error
     cssClass.AssignLiteral("neterror");
     error = "cspBlocked";
   } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
     nsCOMPtr<nsINSSErrorsService> nsserr =
@@ -4231,18 +4226,17 @@ nsDocShell::DisplayLoadError(nsresult aE
       error = "nssFailure2";
     }
   } else if (NS_ERROR_PHISHING_URI == aError ||
              NS_ERROR_MALWARE_URI == aError ||
              NS_ERROR_UNWANTED_URI == aError ||
              NS_ERROR_HARMFUL_URI == aError) {
     nsAutoCString host;
     aURI->GetHost(host);
-    CopyUTF8toUTF16(host, formatStrs[0]);
-    formatStrCount = 1;
+    CopyUTF8toUTF16(host, *formatStrs.AppendElement());
 
     // Malware and phishing detectors may want to use an alternate error
     // page, but if the pref's not set, we'll fall back on the standard page
     nsAutoCString alternateErrorPage;
     nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page",
                                           alternateErrorPage);
     if (NS_SUCCEEDED(rv)) {
       errorPage.Assign(alternateErrorPage);
@@ -4429,54 +4423,50 @@ nsDocShell::DisplayLoadError(nsresult aE
     if (addHostPort) {
       // Build up the host:port string.
       nsAutoCString hostport;
       if (aURI) {
         aURI->GetHostPort(hostport);
       } else {
         hostport.Assign('?');
       }
-      CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]);
+      CopyUTF8toUTF16(hostport, *formatStrs.AppendElement());
     }
 
     nsAutoCString spec;
     rv = NS_ERROR_NOT_AVAILABLE;
+    auto& nextFormatStr = *formatStrs.AppendElement();
     if (aURI) {
       // displaying "file://" is aesthetically unpleasing and could even be
       // confusing to the user
       if (SchemeIsFile(aURI)) {
         aURI->GetPathQueryRef(spec);
       } else {
         aURI->GetSpec(spec);
       }
 
       nsCOMPtr<nsITextToSubURI> textToSubURI(
           do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
       if (NS_SUCCEEDED(rv)) {
         rv = textToSubURI->UnEscapeURIForUI(NS_LITERAL_CSTRING("UTF-8"), spec,
-                                            formatStrs[formatStrCount]);
+                                            nextFormatStr);
       }
     } else {
       spec.Assign('?');
     }
     if (NS_FAILED(rv)) {
-      CopyUTF8toUTF16(spec, formatStrs[formatStrCount]);
+      CopyUTF8toUTF16(spec, nextFormatStr);
     }
     rv = NS_OK;
-    ++formatStrCount;
-
-    const char16_t* strs[kMaxFormatStrArgs];
-    for (uint32_t i = 0; i < formatStrCount; i++) {
-      strs[i] = formatStrs[i].get();
-    }
+
     nsAutoString str;
-    rv = stringBundle->FormatStringFromName(errorDescriptionID, strs,
-                                            formatStrCount, str);
+    rv =
+        stringBundle->FormatStringFromName(errorDescriptionID, formatStrs, str);
     NS_ENSURE_SUCCESS(rv, rv);
-    messageStr.Assign(str.get());
+    messageStr.Assign(str);
   }
 
   // Display the error as a page or an alert prompt
   NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
 
   if ((NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) &&
       SchemeIsHTTPS(aURI)) {
     // Maybe TLS intolerant. Treat this as an SSL error.
@@ -12055,29 +12045,29 @@ nsresult nsDocShell::ConfirmRepost(bool*
   nsCOMPtr<nsIStringBundle> brandBundle;
   rv = stringBundleService->CreateBundle(kBrandBundleURL,
                                          getter_AddRefs(brandBundle));
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ASSERTION(prompter && brandBundle && appBundle,
                "Unable to set up repost prompter.");
 
-  nsAutoString brandName;
-  rv = brandBundle->GetStringFromName("brandShortName", brandName);
+  AutoTArray<nsString, 1> formatStrings;
+  rv = brandBundle->GetStringFromName("brandShortName",
+                                      *formatStrings.AppendElement());
 
   nsAutoString msgString, button0Title;
   if (NS_FAILED(rv)) {  // No brand, use the generic version.
     rv = appBundle->GetStringFromName("confirmRepostPrompt", msgString);
   } else {
     // Brand available - if the app has an override file with formatting, the
     // app name will be included. Without an override, the prompt will look
     // like the generic version.
-    const char16_t* formatStrings[] = {brandName.get()};
     rv = appBundle->FormatStringFromName("confirmRepostPrompt", formatStrings,
-                                         ArrayLength(formatStrings), msgString);
+                                         msgString);
   }
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   rv = appBundle->GetStringFromName("resendButton.label", button0Title);
   if (NS_FAILED(rv)) {
     return rv;
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -109,18 +109,17 @@ class EventSourceImpl final : public nsI
   void FailConnection();
 
   nsresult Thaw();
   nsresult Freeze();
 
   static void TimerCallback(nsITimer* aTimer, void* aClosure);
 
   nsresult PrintErrorOnConsole(const char* aBundleURI, const char* aError,
-                               const char16_t** aFormatStrings,
-                               uint32_t aFormatStringsLen);
+                               const nsTArray<nsString>& aFormatStrings);
   nsresult ConsoleError();
 
   static nsresult StreamReaderFunc(nsIInputStream* aInputStream, void* aClosure,
                                    const char* aFromRawSegment,
                                    uint32_t aToOffset, uint32_t aCount,
                                    uint32_t* aWriteCount);
   void ParseSegment(const char* aBuffer, uint32_t aLength);
   nsresult SetFieldAndClear();
@@ -1151,20 +1150,19 @@ nsresult EventSourceImpl::SetReconnectio
   nsresult rv = mTimer->InitWithNamedFuncCallback(
       TimerCallback, this, mReconnectionTime, nsITimer::TYPE_ONE_SHOT,
       "dom::EventSourceImpl::SetReconnectionTimeout");
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
-nsresult EventSourceImpl::PrintErrorOnConsole(const char* aBundleURI,
-                                              const char* aError,
-                                              const char16_t** aFormatStrings,
-                                              uint32_t aFormatStringsLen) {
+nsresult EventSourceImpl::PrintErrorOnConsole(
+    const char* aBundleURI, const char* aError,
+    const nsTArray<nsString>& aFormatStrings) {
   AssertIsOnMainThread();
   MOZ_ASSERT(!IsShutDown());
   nsCOMPtr<nsIStringBundleService> bundleService =
       mozilla::services::GetStringBundleService();
   NS_ENSURE_STATE(bundleService);
 
   nsCOMPtr<nsIStringBundle> strBundle;
   nsresult rv =
@@ -1176,19 +1174,18 @@ nsresult EventSourceImpl::PrintErrorOnCo
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIScriptError> errObj(
       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Localize the error message
   nsAutoString message;
-  if (aFormatStrings) {
-    rv = strBundle->FormatStringFromName(aError, aFormatStrings,
-                                         aFormatStringsLen, message);
+  if (!aFormatStrings.IsEmpty()) {
+    rv = strBundle->FormatStringFromName(aError, aFormatStrings, message);
   } else {
     rv = strBundle->GetStringFromName(aError, message);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = errObj->InitWithWindowID(
       message, mScriptFile, EmptyString(), mScriptLine, mScriptColumn,
       nsIScriptError::errorFlag, "Event Source", mInnerWindowID);
@@ -1203,27 +1200,25 @@ nsresult EventSourceImpl::PrintErrorOnCo
 
 nsresult EventSourceImpl::ConsoleError() {
   AssertIsOnMainThread();
   MOZ_ASSERT(!IsShutDown());
   nsAutoCString targetSpec;
   nsresult rv = mSrc->GetSpec(targetSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
-  const char16_t* formatStrings[] = {specUTF16.get()};
+  AutoTArray<nsString, 1> formatStrings;
+  CopyUTF8toUTF16(targetSpec, *formatStrings.AppendElement());
 
   if (ReadyState() == CONNECTING) {
     rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                             "connectionFailure", formatStrings,
-                             ArrayLength(formatStrings));
+                             "connectionFailure", formatStrings);
   } else {
     rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                             "netInterrupt", formatStrings,
-                             ArrayLength(formatStrings));
+                             "netInterrupt", formatStrings);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 void EventSourceImpl::DispatchFailConnection() {
   AssertIsOnMainThread();
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3599,17 +3599,24 @@ nsresult nsContentUtils::FormatLocalized
   nsresult rv = EnsureStringBundle(aFile);
   NS_ENSURE_SUCCESS(rv, rv);
   nsIStringBundle* bundle = sStringBundles[aFile];
 
   if (!aParams || !aParamsLength) {
     return bundle->GetStringFromName(aKey, aResult);
   }
 
-  return bundle->FormatStringFromName(aKey, aParams, aParamsLength, aResult);
+  // XXXbz Temporary.  Will change the signature of FormatLocalizedString in the
+  // next changeset, but it has lots of callsites and this changeset was already
+  // getting big.
+  nsTArray<nsString> params(aParamsLength);
+  for (uint32_t i = 0; i < aParamsLength; ++i) {
+    params.AppendElement(aParams[i]);
+  }
+  return bundle->FormatStringFromName(aKey, params, aResult);
 }
 
 /* static */
 nsresult nsContentUtils::FormatLocalizedString(
     PropertiesFile aFile, const char* aKey,
     const nsTArray<nsString>& aParamArray, nsAString& aResult) {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/browser-element/mochitest/browserElement_ReloadPostRequest.js
+++ b/dom/browser-element/mochitest/browserElement_ReloadPostRequest.js
@@ -20,17 +20,17 @@ var isPostRequestSubmitted;
 
 function getExpectedStrings() {
   let result = {};
   let appBundle = Services.strings.createBundle("chrome://global/locale/appstrings.properties");
   let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
   try {
     let brandName = brandBundle.GetStringFromName("brandShortName");
     result.message = appBundle.formatStringFromName("confirmRepostPrompt",
-                                                    [brandName], 1);
+                                                    [brandName]);
   } catch (e) {
     // for the case that we don't have brandShortName
     result.message = appBundle.GetStringFromName("confirmRepostPrompt");
   }
   result.resend = appBundle.GetStringFromName("resendButton.label");
 
   return result;
 }
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -1514,36 +1514,36 @@ nsresult HTMLFormElement::GetActionURL(n
   // Potentially the page uses the CSP directive 'upgrade-insecure-requests'. In
   // such a case we have to upgrade the action url from http:// to https://.
   // If the actionURL is not http, then there is nothing to do.
   bool isHttpScheme = false;
   rv = actionURL->SchemeIs("http", &isHttpScheme);
   NS_ENSURE_SUCCESS(rv, rv);
   if (isHttpScheme && document->GetUpgradeInsecureRequests(false)) {
     // let's use the old specification before the upgrade for logging
+    AutoTArray<nsString, 2> params;
     nsAutoCString spec;
     rv = actionURL->GetSpec(spec);
     NS_ENSURE_SUCCESS(rv, rv);
-    NS_ConvertUTF8toUTF16 reportSpec(spec);
+    CopyUTF8toUTF16(spec, *params.AppendElement());
 
     // upgrade the actionURL from http:// to use https://
     nsCOMPtr<nsIURI> upgradedActionURL;
     rv = NS_GetSecureUpgradedURI(actionURL, getter_AddRefs(upgradedActionURL));
     NS_ENSURE_SUCCESS(rv, rv);
     actionURL = upgradedActionURL.forget();
 
     // let's log a message to the console that we are upgrading a request
     nsAutoCString scheme;
     rv = actionURL->GetScheme(scheme);
     NS_ENSURE_SUCCESS(rv, rv);
-    NS_ConvertUTF8toUTF16 reportScheme(scheme);
+    CopyUTF8toUTF16(scheme, *params.AppendElement());
 
-    const char16_t* params[] = {reportSpec.get(), reportScheme.get()};
     CSP_LogLocalizedStr(
-        "upgradeInsecureRequest", params, ArrayLength(params),
+        "upgradeInsecureRequest", params,
         EmptyString(),  // aSourceFile
         EmptyString(),  // aScriptSample
         0,              // aLineNumber
         0,              // aColumnNumber
         nsIScriptError::warningFlag,
         NS_LITERAL_CSTRING("upgradeInsecureRequest"), document->InnerWindowID(),
         !!document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId);
   }
--- a/dom/html/ImageDocument.cpp
+++ b/dom/html/ImageDocument.cpp
@@ -575,21 +575,20 @@ nsresult ImageDocument::OnSizeAvailable(
 nsresult ImageDocument::OnLoadComplete(imgIRequest* aRequest,
                                        nsresult aStatus) {
   UpdateTitleAndCharset();
 
   // mImageContent can be null if the document is already destroyed
   if (NS_FAILED(aStatus) && mStringBundle && mImageContent) {
     nsAutoCString src;
     mDocumentURI->GetSpec(src);
-    NS_ConvertUTF8toUTF16 srcString(src);
-    const char16_t* formatString[] = {srcString.get()};
+    AutoTArray<nsString, 1> formatString;
+    CopyUTF8toUTF16(src, *formatString.AppendElement());
     nsAutoString errorMsg;
-    mStringBundle->FormatStringFromName("InvalidImage", formatString, 1,
-                                        errorMsg);
+    mStringBundle->FormatStringFromName("InvalidImage", formatString, errorMsg);
 
     mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, errorMsg, false);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -779,21 +778,20 @@ void ImageDocument::UpdateTitleAndCharse
       typeStr = Substring(iter, end);
     } else {
       typeStr = mimeType;
     }
   }
 
   nsAutoString status;
   if (mImageIsResized) {
-    nsAutoString ratioStr;
-    ratioStr.AppendInt(NSToCoordFloor(GetRatio() * 100));
+    AutoTArray<nsString, 1> formatString;
+    formatString.AppendElement()->AppendInt(NSToCoordFloor(GetRatio() * 100));
 
-    const char16_t* formatString[1] = {ratioStr.get()};
-    mStringBundle->FormatStringFromName("ScaledImage", formatString, 1, status);
+    mStringBundle->FormatStringFromName("ScaledImage", formatString, status);
   }
 
   static const char* const formatNames[4] = {
       "ImageTitleWithNeitherDimensionsNorFile",
       "ImageTitleWithoutDimensions",
       "ImageTitleWithDimensions2",
       "ImageTitleWithDimensions2AndFile",
   };
--- a/dom/html/MediaDocument.cpp
+++ b/dom/html/MediaDocument.cpp
@@ -342,49 +342,48 @@ void MediaDocument::UpdateTitleAndCharse
     // if we got a valid size (not all media have a size)
     if (aWidth != 0 && aHeight != 0) {
       nsAutoString widthStr;
       nsAutoString heightStr;
       widthStr.AppendInt(aWidth);
       heightStr.AppendInt(aHeight);
       // If we got a filename, display it
       if (!fileStr.IsEmpty()) {
-        const char16_t* formatStrings[4] = {fileStr.get(), typeStr.get(),
-                                            widthStr.get(), heightStr.get()};
+        AutoTArray<nsString, 4> formatStrings = {fileStr, typeStr, widthStr,
+                                                 heightStr};
         mStringBundle->FormatStringFromName(aFormatNames[eWithDimAndFile],
-                                            formatStrings, 4, title);
+                                            formatStrings, title);
       } else {
-        const char16_t* formatStrings[3] = {typeStr.get(), widthStr.get(),
-                                            heightStr.get()};
+        AutoTArray<nsString, 3> formatStrings = {typeStr, widthStr, heightStr};
         mStringBundle->FormatStringFromName(aFormatNames[eWithDim],
-                                            formatStrings, 3, title);
+                                            formatStrings, title);
       }
     } else {
       // If we got a filename, display it
       if (!fileStr.IsEmpty()) {
-        const char16_t* formatStrings[2] = {fileStr.get(), typeStr.get()};
+        AutoTArray<nsString, 2> formatStrings = {fileStr, typeStr};
         mStringBundle->FormatStringFromName(aFormatNames[eWithFile],
-                                            formatStrings, 2, title);
+                                            formatStrings, title);
       } else {
-        const char16_t* formatStrings[1] = {typeStr.get()};
+        AutoTArray<nsString, 1> formatStrings = {typeStr};
         mStringBundle->FormatStringFromName(aFormatNames[eWithNoInfo],
-                                            formatStrings, 1, title);
+                                            formatStrings, title);
       }
     }
   }
 
   // set it on the document
   if (aStatus.IsEmpty()) {
     IgnoredErrorResult ignored;
     SetTitle(title, ignored);
   } else {
     nsAutoString titleWithStatus;
-    const nsPromiseFlatString& status = PromiseFlatString(aStatus);
-    const char16_t* formatStrings[2] = {title.get(), status.get()};
-    mStringBundle->FormatStringFromName("TitleWithStatus", formatStrings, 2,
+    AutoTArray<nsString, 2> formatStrings;
+    formatStrings.AppendElement(title);
+    formatStrings.AppendElement(aStatus);
+    mStringBundle->FormatStringFromName("TitleWithStatus", formatStrings,
                                         titleWithStatus);
-    IgnoredErrorResult ignored;
-    SetTitle(titleWithStatus, ignored);
+    SetTitle(titleWithStatus, IgnoreErrors());
   }
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/manifest/ValueExtractor.jsm
+++ b/dom/manifest/ValueExtractor.jsm
@@ -29,18 +29,17 @@ ValueExtractor.prototype = {
   extractValue({expectedType, object, objectName, property, trim}) {
     const value = object[property];
     const isArray = Array.isArray(value);
     // We need to special-case "array", as it's not a JS primitive.
     const type = (isArray) ? "array" : typeof value;
     if (type !== expectedType) {
       if (type !== "undefined") {
         this.console.warn(this.domBundle.formatStringFromName("ManifestInvalidType",
-                                                              [objectName, property, expectedType],
-                                                              3));
+                                                              [objectName, property, expectedType]));
       }
       return undefined;
     }
     // Trim string and returned undefined if the empty string.
     const shouldTrim = expectedType === "string" && value && trim;
     if (shouldTrim) {
       return value.trim() || undefined;
     }
@@ -51,29 +50,27 @@ ValueExtractor.prototype = {
     let color;
     if (InspectorUtils.isValidCSSColor(value)) {
       const rgba = InspectorUtils.colorToRGBA(value);
       color = "#" + ((rgba.r << 16) |
         (rgba.g << 8) |
         rgba.b).toString(16);
     } else if (value) {
       this.console.warn(this.domBundle.formatStringFromName("ManifestInvalidCSSColor",
-                                                            [spec.property, value],
-                                                            2));
+                                                            [spec.property, value]));
     }
     return color;
   },
   extractLanguageValue(spec) {
     let langTag;
     const value = this.extractValue(spec);
     if (value !== undefined) {
       try {
         langTag = Intl.getCanonicalLocales(value)[0];
       } catch (err) {
         console.warn(this.domBundle.formatStringFromName("ManifestLangIsInvalid",
-                                                        [spec.property, value],
-                                                        2));
+                                                        [spec.property, value]));
       }
     }
     return langTag;
   },
 };
 var EXPORTED_SYMBOLS = ["ValueExtractor"]; // jshint ignore:line
--- a/dom/push/PushCrypto.jsm
+++ b/dom/push/PushCrypto.jsm
@@ -67,18 +67,17 @@ class CryptoError extends Error {
    * Console.
    *
    * @param {String} scope The scope of the service worker receiving the
    *  message, prepended to any other substitutions in the string.
    * @returns {String} The localized string.
    */
   format(scope) {
     let params = [scope, ...this.params].map(String);
-    return gDOMBundle.formatStringFromName(this.property, params,
-                                           params.length);
+    return gDOMBundle.formatStringFromName(this.property, params);
   }
 }
 
 function getEncryptionKeyParams(encryptKeyField) {
   if (!encryptKeyField) {
     return null;
   }
   var params = encryptKeyField.split(",");
--- a/dom/security/FramingChecker.cpp
+++ b/dom/security/FramingChecker.cpp
@@ -173,19 +173,19 @@ static bool ShouldIgnoreFrameOptions(nsI
     // is nothing to do here.
     return false;
   }
 
   // log warning to console that xfo is ignored because of CSP
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   uint64_t innerWindowID = loadInfo->GetInnerWindowID();
   bool privateWindow = !!loadInfo->GetOriginAttributes().mPrivateBrowsingId;
-  const char16_t* params[] = {u"x-frame-options", u"frame-ancestors"};
+  AutoTArray<nsString, 2> params = {NS_LITERAL_STRING("x-frame-options"),
+                                    NS_LITERAL_STRING("frame-ancestors")};
   CSP_LogLocalizedStr("IgnoringSrcBecauseOfDirective", params,
-                      ArrayLength(params),
                       EmptyString(),  // no sourcefile
                       EmptyString(),  // no scriptsample
                       0,              // no linenumber
                       0,              // no columnnumber
                       nsIScriptError::warningFlag,
                       NS_LITERAL_CSTRING("IgnoringSrcBecauseOfDirective"),
                       innerWindowID, privateWindow);
 
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -799,30 +799,30 @@ void nsCSPContext::flushConsoleMessages(
     ConsoleMsgQueueElem& elem = mConsoleMsgQueue[i];
     CSP_LogMessage(elem.mMsg, elem.mSourceName, elem.mSourceLine,
                    elem.mLineNumber, elem.mColumnNumber, elem.mSeverityFlag,
                    elem.mCategory, mInnerWindowID, privateWindow);
   }
   mConsoleMsgQueue.Clear();
 }
 
-void nsCSPContext::logToConsole(const char* aName, const char16_t** aParams,
-                                uint32_t aParamsLength,
+void nsCSPContext::logToConsole(const char* aName,
+                                const nsTArray<nsString>& aParams,
                                 const nsAString& aSourceName,
                                 const nsAString& aSourceLine,
                                 uint32_t aLineNumber, uint32_t aColumnNumber,
                                 uint32_t aSeverityFlag) {
   // we are passing aName as the category so we can link to the
   // appropriate MDN docs depending on the specific error.
   nsDependentCString category(aName);
 
   // let's check if we have to queue up console messages
   if (mQueueUpMessages) {
     nsAutoString msg;
-    CSP_GetLocalizedStr(aName, aParams, aParamsLength, msg);
+    CSP_GetLocalizedStr(aName, aParams, msg);
     ConsoleMsgQueueElem& elem = *mConsoleMsgQueue.AppendElement();
     elem.mMsg = msg;
     elem.mSourceName = PromiseFlatString(aSourceName);
     elem.mSourceLine = PromiseFlatString(aSourceLine);
     elem.mLineNumber = aLineNumber;
     elem.mColumnNumber = aColumnNumber;
     elem.mSeverityFlag = aSeverityFlag;
     elem.mCategory = category;
@@ -831,19 +831,19 @@ void nsCSPContext::logToConsole(const ch
 
   bool privateWindow = false;
   nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
   if (doc) {
     privateWindow =
         !!doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
   }
 
-  CSP_LogLocalizedStr(aName, aParams, aParamsLength, aSourceName, aSourceLine,
-                      aLineNumber, aColumnNumber, aSeverityFlag, category,
-                      mInnerWindowID, privateWindow);
+  CSP_LogLocalizedStr(aName, aParams, aSourceName, aSourceLine, aLineNumber,
+                      aColumnNumber, aSeverityFlag, category, mInnerWindowID,
+                      privateWindow);
 }
 
 /**
  * Strip URI for reporting according to:
  * http://www.w3.org/TR/CSP/#violation-reports
  *
  * @param aURI
  *        The uri to be stripped for reporting
@@ -1049,22 +1049,21 @@ nsresult nsCSPContext::SendReports(
   nsCOMPtr<nsIChannel> reportChannel;
 
   nsresult rv;
   for (uint32_t r = 0; r < reportURIs.Length(); r++) {
     nsAutoCString reportURICstring = NS_ConvertUTF16toUTF8(reportURIs[r]);
     // try to create a new uri from every report-uri string
     rv = NS_NewURI(getter_AddRefs(reportURI), reportURIs[r]);
     if (NS_FAILED(rv)) {
-      const char16_t* params[] = {reportURIs[r].get()};
+      AutoTArray<nsString, 1> params = {reportURIs[r]};
       CSPCONTEXTLOG(("Could not create nsIURI for report URI %s",
                      reportURICstring.get()));
-      logToConsole("triedToSendReport", params, ArrayLength(params),
-                   aViolationEventInit.mSourceFile, aViolationEventInit.mSample,
-                   aViolationEventInit.mLineNumber,
+      logToConsole("triedToSendReport", params, aViolationEventInit.mSourceFile,
+                   aViolationEventInit.mSample, aViolationEventInit.mLineNumber,
                    aViolationEventInit.mColumnNumber,
                    nsIScriptError::errorFlag);
       continue;  // don't return yet, there may be more URIs
     }
 
     // try to create a new channel for every report-uri
     if (doc) {
       rv = NS_NewChannel(getter_AddRefs(reportChannel), reportURI, doc,
@@ -1086,22 +1085,21 @@ nsresult nsCSPContext::SendReports(
     // log a warning to console if scheme is not http or https
     bool isHttpScheme =
         (NS_SUCCEEDED(reportURI->SchemeIs("http", &isHttpScheme)) &&
          isHttpScheme) ||
         (NS_SUCCEEDED(reportURI->SchemeIs("https", &isHttpScheme)) &&
          isHttpScheme);
 
     if (!isHttpScheme) {
-      const char16_t* params[] = {reportURIs[r].get()};
-      logToConsole("reportURInotHttpsOrHttp2", params, ArrayLength(params),
-                   aViolationEventInit.mSourceFile, aViolationEventInit.mSample,
-                   aViolationEventInit.mLineNumber,
-                   aViolationEventInit.mColumnNumber,
-                   nsIScriptError::errorFlag);
+      AutoTArray<nsString, 1> params = {reportURIs[r]};
+      logToConsole(
+          "reportURInotHttpsOrHttp2", params, aViolationEventInit.mSourceFile,
+          aViolationEventInit.mSample, aViolationEventInit.mLineNumber,
+          aViolationEventInit.mColumnNumber, nsIScriptError::errorFlag);
       continue;
     }
 
     // make sure this is an anonymous request (no cookies) so in case the
     // policy URI is injected, it can't be abused for CSRF.
     nsLoadFlags flags;
     rv = reportChannel->GetLoadFlags(&flags);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1159,22 +1157,21 @@ nsresult nsCSPContext::SendReports(
     rv = reportChannel->AsyncOpen(listener);
 
     // AsyncOpen should not fail, but could if there's no load group (like if
     // SetRequestContextWith{Document,Principal} is not given a channel). This
     // should fail quietly and not return an error since it's really ok if
     // reports don't go out, but it's good to log the error locally.
 
     if (NS_FAILED(rv)) {
-      const char16_t* params[] = {reportURIs[r].get()};
+      AutoTArray<nsString, 1> params = {reportURIs[r]};
       CSPCONTEXTLOG(("AsyncOpen failed for report URI %s",
                      NS_ConvertUTF16toUTF8(params[0]).get()));
-      logToConsole("triedToSendReport", params, ArrayLength(params),
-                   aViolationEventInit.mSourceFile, aViolationEventInit.mSample,
-                   aViolationEventInit.mLineNumber,
+      logToConsole("triedToSendReport", params, aViolationEventInit.mSourceFile,
+                   aViolationEventInit.mSample, aViolationEventInit.mLineNumber,
                    aViolationEventInit.mColumnNumber,
                    nsIScriptError::errorFlag);
     } else {
       CSPCONTEXTLOG(
           ("Sent violation report to URI %s", reportURICstring.get()));
     }
   }
   return NS_OK;
@@ -1320,22 +1317,22 @@ class CSPReportSenderRunnable final : pu
               NS_ConvertUTF16toUTF8(nsContentUtils::GetLocalizedEllipsis()));
         }
       }
     }
 
     if (blockedContentSource.Length() > 0) {
       nsString blockedContentSource16 =
           NS_ConvertUTF8toUTF16(blockedContentSource);
-      const char16_t* params[] = {mViolatedDirective.get(),
-                                  blockedContentSource16.get()};
+      AutoTArray<nsString, 2> params = {mViolatedDirective,
+                                        blockedContentSource16};
       mCSPContext->logToConsole(
           mReportOnlyFlag ? "CSPROViolationWithURI" : "CSPViolationWithURI",
-          params, ArrayLength(params), mSourceFile, mScriptSample, mLineNum,
-          mColumnNum, nsIScriptError::errorFlag);
+          params, mSourceFile, mScriptSample, mLineNum, mColumnNum,
+          nsIScriptError::errorFlag);
     }
 
     // 4) fire violation event
     mCSPContext->FireViolationEvent(mTriggeringElement, mCSPEventListener,
                                     init);
 
     return NS_OK;
   }
@@ -1599,20 +1596,19 @@ nsCSPContext::GetCSPSandboxFlags(uint32_
       nsAutoString policy;
       mPolicies[i]->toString(policy);
 
       CSPCONTEXTLOG(
           ("nsCSPContext::GetCSPSandboxFlags, report only policy, ignoring "
            "sandbox in: %s",
            NS_ConvertUTF16toUTF8(policy).get()));
 
-      const char16_t* params[] = {policy.get()};
-      logToConsole("ignoringReportOnlyDirective", params, ArrayLength(params),
-                   EmptyString(), EmptyString(), 0, 0,
-                   nsIScriptError::warningFlag);
+      AutoTArray<nsString, 1> params = {policy};
+      logToConsole("ignoringReportOnlyDirective", params, EmptyString(),
+                   EmptyString(), 0, 0, nsIScriptError::warningFlag);
     }
   }
 
   return NS_OK;
 }
 
 /* ========== CSPViolationReportListener implementation ========== */
 
--- a/dom/security/nsCSPContext.h
+++ b/dom/security/nsCSPContext.h
@@ -65,20 +65,20 @@ class nsCSPContext : public nsIContentSe
    * innerWindowID is initialized on the document. Use this function
    * to call back to flush queued up console messages and initialize
    * the innerWindowID. Node, If SetRequestContextWithPrincipal() was
    * called then we do not have a innerWindowID anyway and hence
    * we can not flush messages to the correct console.
    */
   void flushConsoleMessages();
 
-  void logToConsole(const char* aName, const char16_t** aParams,
-                    uint32_t aParamsLength, const nsAString& aSourceName,
-                    const nsAString& aSourceLine, uint32_t aLineNumber,
-                    uint32_t aColumnNumber, uint32_t aSeverityFlag);
+  void logToConsole(const char* aName, const nsTArray<nsString>& aParams,
+                    const nsAString& aSourceName, const nsAString& aSourceLine,
+                    uint32_t aLineNumber, uint32_t aColumnNumber,
+                    uint32_t aSeverityFlag);
 
   /**
    * Construct SecurityPolicyViolationEventInit structure.
    *
    * @param aBlockedURI
    *        A nsIURI: the source of the violation.
    * @param aOriginalUri
    *        The original URI if the blocked content is a redirect, else null
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -175,22 +175,21 @@ bool nsCSPParser::atValidPctEncodedChar(
 // http://tools.ietf.org/html/rfc3986#section-3.3
 bool nsCSPParser::atValidPathChar() {
   return (atValidUnreservedChar() || atValidSubDelimChar() ||
           atValidPctEncodedChar() || peek(COLON) || peek(ATSYMBOL));
 }
 
 void nsCSPParser::logWarningErrorToConsole(uint32_t aSeverityFlag,
                                            const char* aProperty,
-                                           const char16_t* aParams[],
-                                           uint32_t aParamsLength) {
+                                           const nsTArray<nsString>& aParams) {
   CSPPARSERLOG(("nsCSPParser::logWarningErrorToConsole: %s", aProperty));
   // send console messages off to the context and let the context
   // deal with it (potentially messages need to be queued up)
-  mCSPContext->logToConsole(aProperty, aParams, aParamsLength,
+  mCSPContext->logToConsole(aProperty, aParams,
                             EmptyString(),   // aSourceName
                             EmptyString(),   // aSourceLine
                             0,               // aLineNumber
                             0,               // aColumnNumber
                             aSeverityFlag);  // aFlags
 }
 
 bool nsCSPParser::hostChar() {
@@ -225,19 +224,19 @@ bool nsCSPParser::port() {
 
   // Port might be "*"
   if (accept(WILDCARD)) {
     return true;
   }
 
   // Port must start with a number
   if (!accept(isNumberToken)) {
-    const char16_t* params[] = {mCurToken.get()};
+    AutoTArray<nsString, 1> params = {mCurToken};
     logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParsePort",
-                             params, ArrayLength(params));
+                             params);
     return false;
   }
   // Consume more numbers and set parsed port to the nsCSPHost
   while (accept(isNumberToken)) { /* consume */
   }
   return true;
 }
 
@@ -260,20 +259,19 @@ bool nsCSPParser::subPath(nsCSPHostSrc* 
       // the decoded-sub path.
       CSP_PercentDecodeStr(mCurValue, pctDecodedSubPath);
       aCspHost->appendPath(pctDecodedSubPath);
       // Resetting current value since we are appending parts of the path
       // to aCspHost, e.g; "http://www.example.com/path1/path2" then the
       // first part is "/path1", second part "/path2"
       resetCurValue();
     } else if (!atValidPathChar()) {
-      const char16_t* params[] = {mCurToken.get()};
+      AutoTArray<nsString, 1> params = {mCurToken};
       logWarningErrorToConsole(nsIScriptError::warningFlag,
-                               "couldntParseInvalidSource", params,
-                               ArrayLength(params));
+                               "couldntParseInvalidSource", params);
       return false;
     }
     // potentially we have encountred a valid pct-encoded character in
     // atValidPathChar(); if so, we have to account for "% HEXDIG HEXDIG" and
     // advance the pointer past the pct-encoded char.
     if (peek(PERCENT_SIGN)) {
       advance();
       advance();
@@ -299,37 +297,35 @@ bool nsCSPParser::path(nsCSPHostSrc* aCs
 
   // Resetting current value and forgetting everything we have parsed so far
   // e.g. parsing "http://www.example.com/path1/path2", then
   // "http://www.example.com" has already been parsed so far
   // forget about it.
   resetCurValue();
 
   if (!accept(SLASH)) {
-    const char16_t* params[] = {mCurToken.get()};
+    AutoTArray<nsString, 1> params = {mCurToken};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "couldntParseInvalidSource", params,
-                             ArrayLength(params));
+                             "couldntParseInvalidSource", params);
     return false;
   }
   if (atEndOfPath()) {
     // one slash right after host [port] is also considered a path, e.g.
     // www.example.com/ should result in www.example.com/
     // please note that we do not have to perform any pct-decoding here
     // because we are just appending a '/' and not any actual chars.
     aCspHost->appendPath(NS_LITERAL_STRING("/"));
     return true;
   }
   // path can begin with "/" but not "//"
   // see http://tools.ietf.org/html/rfc3986#section-3.3
   if (peek(SLASH)) {
-    const char16_t* params[] = {mCurToken.get()};
+    AutoTArray<nsString, 1> params = {mCurToken};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "couldntParseInvalidSource", params,
-                             ArrayLength(params));
+                             "couldntParseInvalidSource", params);
     return false;
   }
   return subPath(aCspHost);
 }
 
 bool nsCSPParser::subHost() {
   CSPPARSERLOG(("nsCSPParser::subHost, mCurToken: %s, mCurValue: %s",
                 NS_ConvertUTF16toUTF8(mCurToken).get(),
@@ -368,50 +364,46 @@ nsCSPHostSrc* nsCSPParser::host() {
   // "https://*", "*.example.com", "*:*", etc.
   if (accept(WILDCARD)) {
     // Might solely be the wildcard
     if (atEnd() || peek(COLON)) {
       return new nsCSPHostSrc(mCurValue);
     }
     // If the token is not only the "*", a "." must follow right after
     if (!accept(DOT)) {
-      const char16_t* params[] = {mCurToken.get()};
+      AutoTArray<nsString, 1> params = {mCurToken};
       logWarningErrorToConsole(nsIScriptError::warningFlag,
-                               "couldntParseInvalidHost", params,
-                               ArrayLength(params));
+                               "couldntParseInvalidHost", params);
       return nullptr;
     }
   }
 
   // Expecting at least one host-char
   if (!hostChar()) {
-    const char16_t* params[] = {mCurToken.get()};
+    AutoTArray<nsString, 1> params = {mCurToken};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "couldntParseInvalidHost", params,
-                             ArrayLength(params));
+                             "couldntParseInvalidHost", params);
     return nullptr;
   }
 
   // There might be several sub hosts defined.
   if (!subHost()) {
-    const char16_t* params[] = {mCurToken.get()};
+    AutoTArray<nsString, 1> params = {mCurToken};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "couldntParseInvalidHost", params,
-                             ArrayLength(params));
+                             "couldntParseInvalidHost", params);
     return nullptr;
   }
 
   // HostName might match a keyword, log to the console.
   if (CSP_IsQuotelessKeyword(mCurValue)) {
     nsString keyword = mCurValue;
     ToLowerCase(keyword);
-    const char16_t* params[] = {mCurToken.get(), keyword.get()};
+    AutoTArray<nsString, 2> params = {mCurToken, keyword};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "hostNameMightBeKeyword", params,
-                             ArrayLength(params));
+                             "hostNameMightBeKeyword", params);
   }
 
   // Create a new nsCSPHostSrc with the parsed host.
   return new nsCSPHostSrc(mCurValue);
 }
 
 // keyword-source = "'self'" / "'unsafe-inline'" / "'unsafe-eval'"
 nsCSPBaseSrc* nsCSPParser::keywordSource() {
@@ -432,39 +424,37 @@ nsCSPBaseSrc* nsCSPParser::keywordSource
   if (CSP_IsKeyword(mCurToken, CSP_STRICT_DYNAMIC)) {
     // make sure strict dynamic is enabled
     if (!StaticPrefs::security_csp_enableStrictDynamic()) {
       return nullptr;
     }
     if (!CSP_IsDirective(mCurDir[0],
                          nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE)) {
       // Todo: Enforce 'strict-dynamic' within default-src; see Bug 1313937
-      const char16_t* params[] = {u"strict-dynamic"};
+      AutoTArray<nsString, 1> params = {NS_LITERAL_STRING("strict-dynamic")};
       logWarningErrorToConsole(nsIScriptError::warningFlag,
-                               "ignoringStrictDynamic", params,
-                               ArrayLength(params));
+                               "ignoringStrictDynamic", params);
       return nullptr;
     }
     mStrictDynamic = true;
     return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
   }
 
   if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE)) {
     nsWeakPtr ctx = mCSPContext->GetLoadingContext();
     nsCOMPtr<Document> doc = do_QueryReferent(ctx);
     if (doc) {
       doc->SetHasUnsafeInlineCSP(true);
     }
     // make sure script-src only contains 'unsafe-inline' once;
     // ignore duplicates and log warning
     if (mUnsafeInlineKeywordSrc) {
-      const char16_t* params[] = {mCurToken.get()};
+      AutoTArray<nsString, 1> params = {mCurToken};
       logWarningErrorToConsole(nsIScriptError::warningFlag,
-                               "ignoringDuplicateSrc", params,
-                               ArrayLength(params));
+                               "ignoringDuplicateSrc", params);
       return nullptr;
     }
     // cache if we encounter 'unsafe-inline' so we can invalidate (ignore) it in
     // case that script-src directive also contains hash- or nonce-.
     mUnsafeInlineKeywordSrc =
         new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
     return mUnsafeInlineKeywordSrc;
   }
@@ -666,20 +656,19 @@ nsCSPBaseSrc* nsCSPParser::sourceExpress
     // scheme and delete cspScheme;
     cspScheme->toString(parsedScheme);
     parsedScheme.Trim(":", false, true);
     delete cspScheme;
 
     // If mCurToken provides not only a scheme, but also a host, we have to
     // check if two slashes follow the scheme.
     if (!accept(SLASH) || !accept(SLASH)) {
-      const char16_t* params[] = {mCurToken.get()};
+      AutoTArray<nsString, 1> params = {mCurToken};
       logWarningErrorToConsole(nsIScriptError::warningFlag,
-                               "failedToParseUnrecognizedSource", params,
-                               ArrayLength(params));
+                               "failedToParseUnrecognizedSource", params);
       return nullptr;
     }
   }
 
   // Calling resetCurValue allows us to keep pointers for mCurChar and mEndChar
   // alive, but resets mCurValue; e.g. mCurToken = "http://www.example.com",
   // then mCurChar = 'w' mEndChar = 'm' mCurValue = ""
   resetCurValue();
@@ -742,20 +731,20 @@ void nsCSPParser::sourceList(nsTArray<ns
     // If the directive contains no other srcs, then we set the 'none'
     if (outSrcs.IsEmpty() ||
         (outSrcs.Length() == 1 && outSrcs[0]->isReportSample())) {
       nsCSPKeywordSrc* keyword = new nsCSPKeywordSrc(CSP_NONE);
       outSrcs.InsertElementAt(0, keyword);
     }
     // Otherwise, we ignore 'none' and report a warning
     else {
-      const char16_t* params[] = {CSP_EnumToUTF16Keyword(CSP_NONE)};
+      AutoTArray<nsString, 1> params;
+      params.AppendElement(CSP_EnumToUTF16Keyword(CSP_NONE));
       logWarningErrorToConsole(nsIScriptError::warningFlag,
-                               "ignoringUnknownOption", params,
-                               ArrayLength(params));
+                               "ignoringUnknownOption", params);
     }
   }
 }
 
 void nsCSPParser::reportURIList(nsCSPDirective* aDir) {
   CSPPARSERLOG(("nsCSPParser::reportURIList"));
 
   nsTArray<nsCSPBaseSrc*> srcs;
@@ -769,33 +758,31 @@ void nsCSPParser::reportURIList(nsCSPDir
     CSPPARSERLOG(("nsCSPParser::reportURIList, mCurToken: %s, mCurValue: %s",
                   NS_ConvertUTF16toUTF8(mCurToken).get(),
                   NS_ConvertUTF16toUTF8(mCurValue).get()));
 
     rv = NS_NewURI(getter_AddRefs(uri), mCurToken, "", mSelfURI);
 
     // If creating the URI casued an error, skip this URI
     if (NS_FAILED(rv)) {
-      const char16_t* params[] = {mCurToken.get()};
+      AutoTArray<nsString, 1> params = {mCurToken};
       logWarningErrorToConsole(nsIScriptError::warningFlag,
-                               "couldNotParseReportURI", params,
-                               ArrayLength(params));
+                               "couldNotParseReportURI", params);
       continue;
     }
 
     // Create new nsCSPReportURI and append to the list.
     nsCSPReportURI* reportURI = new nsCSPReportURI(uri);
     srcs.AppendElement(reportURI);
   }
 
   if (srcs.Length() == 0) {
-    const char16_t* directiveName[] = {mCurToken.get()};
+    AutoTArray<nsString, 1> directiveName = {mCurToken};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "ignoringDirectiveWithNoValues", directiveName,
-                             ArrayLength(directiveName));
+                             "ignoringDirectiveWithNoValues", directiveName);
     delete aDir;
     return;
   }
 
   aDir->addSrcs(srcs);
   mPolicy->addDirective(aDir);
 }
 
@@ -812,20 +799,19 @@ void nsCSPParser::sandboxFlagList(nsCSPD
   for (uint32_t i = 1; i < mCurDir.Length(); i++) {
     mCurToken = mCurDir[i];
 
     CSPPARSERLOG(("nsCSPParser::sandboxFlagList, mCurToken: %s, mCurValue: %s",
                   NS_ConvertUTF16toUTF8(mCurToken).get(),
                   NS_ConvertUTF16toUTF8(mCurValue).get()));
 
     if (!nsContentUtils::IsValidSandboxFlag(mCurToken)) {
-      const char16_t* params[] = {mCurToken.get()};
+      AutoTArray<nsString, 1> params = {mCurToken};
       logWarningErrorToConsole(nsIScriptError::warningFlag,
-                               "couldntParseInvalidSandboxFlag", params,
-                               ArrayLength(params));
+                               "couldntParseInvalidSandboxFlag", params);
       continue;
     }
 
     flags.Append(mCurToken);
     if (i != mCurDir.Length() - 1) {
       flags.AppendLiteral(" ");
     }
   }
@@ -849,60 +835,57 @@ void nsCSPParser::directiveValue(nsTArra
 // directive-name = 1*( ALPHA / DIGIT / "-" )
 nsCSPDirective* nsCSPParser::directiveName() {
   CSPPARSERLOG(("nsCSPParser::directiveName, mCurToken: %s, mCurValue: %s",
                 NS_ConvertUTF16toUTF8(mCurToken).get(),
                 NS_ConvertUTF16toUTF8(mCurValue).get()));
 
   // Check if it is a valid directive
   if (!CSP_IsValidDirective(mCurToken)) {
-    const char16_t* params[] = {mCurToken.get()};
+    AutoTArray<nsString, 1> params = {mCurToken};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "couldNotProcessUnknownDirective", params,
-                             ArrayLength(params));
+                             "couldNotProcessUnknownDirective", params);
     return nullptr;
   }
 
   // The directive 'reflected-xss' is part of CSP 1.1, see:
   // http://www.w3.org/TR/2014/WD-CSP11-20140211/#reflected-xss
   // Currently we are not supporting that directive, hence we log a
   // warning to the console and ignore the directive including its values.
   if (CSP_IsDirective(mCurToken,
                       nsIContentSecurityPolicy::REFLECTED_XSS_DIRECTIVE)) {
-    const char16_t* params[] = {mCurToken.get()};
+    AutoTArray<nsString, 1> params = {mCurToken};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "notSupportingDirective", params,
-                             ArrayLength(params));
+                             "notSupportingDirective", params);
     return nullptr;
   }
 
   // Make sure the directive does not already exist
   // (see http://www.w3.org/TR/CSP11/#parsing)
   if (mPolicy->hasDirective(CSP_StringToCSPDirective(mCurToken))) {
-    const char16_t* params[] = {mCurToken.get()};
+    AutoTArray<nsString, 1> params = {mCurToken};
     logWarningErrorToConsole(nsIScriptError::warningFlag, "duplicateDirective",
-                             params, ArrayLength(params));
+                             params);
     return nullptr;
   }
 
   // CSP delivered via meta tag should ignore the following directives:
   // report-uri, frame-ancestors, and sandbox, see:
   // http://www.w3.org/TR/CSP11/#delivery-html-meta-element
   if (mDeliveredViaMetaTag &&
       ((CSP_IsDirective(mCurToken,
                         nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) ||
        (CSP_IsDirective(mCurToken,
                         nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE)) ||
        (CSP_IsDirective(mCurToken,
                         nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)))) {
     // log to the console to indicate that meta CSP is ignoring the directive
-    const char16_t* params[] = {mCurToken.get()};
+    AutoTArray<nsString, 1> params = {mCurToken};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "ignoringSrcFromMetaCSP", params,
-                             ArrayLength(params));
+                             "ignoringSrcFromMetaCSP", params);
     return nullptr;
   }
 
   // special case handling for block-all-mixed-content
   if (CSP_IsDirective(mCurToken,
                       nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
     return new nsBlockAllMixedContentDirective(
         CSP_StringToCSPDirective(mCurToken));
@@ -914,20 +897,19 @@ nsCSPDirective* nsCSPParser::directiveNa
     return new nsUpgradeInsecureDirective(CSP_StringToCSPDirective(mCurToken));
   }
 
   // child-src by itself is deprecatd but will be enforced
   //   * for workers (if worker-src is not explicitly specified)
   //   * for frames  (if frame-src is not explicitly specified)
   if (CSP_IsDirective(mCurToken,
                       nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE)) {
-    const char16_t* params[] = {mCurToken.get()};
+    AutoTArray<nsString, 1> params = {mCurToken};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "deprecatedChildSrcDirective", params,
-                             ArrayLength(params));
+                             "deprecatedChildSrcDirective", params);
     mChildSrc = new nsCSPChildSrcDirective(CSP_StringToCSPDirective(mCurToken));
     return mChildSrc;
   }
 
   // if we have a frame-src, cache it so we can discard child-src for frames
   if (CSP_IsDirective(mCurToken,
                       nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE)) {
     mFrameSrc = new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
@@ -961,20 +943,19 @@ void nsCSPParser::directive() {
 
   CSPPARSERLOG(("nsCSPParser::directive, mCurToken: %s, mCurValue: %s",
                 NS_ConvertUTF16toUTF8(mCurToken).get(),
                 NS_ConvertUTF16toUTF8(mCurValue).get()));
 
   // Make sure that the directive-srcs-array contains at least
   // one directive and one src.
   if (mCurDir.Length() < 1) {
-    const char16_t* params[] = {u"directive missing"};
+    AutoTArray<nsString, 1> params = {NS_LITERAL_STRING("directive missing")};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "failedToParseUnrecognizedSource", params,
-                             ArrayLength(params));
+                             "failedToParseUnrecognizedSource", params);
     return;
   }
 
   if (CSP_IsEmptyDirective(mCurValue, mCurToken)) {
     return;
   }
 
   // Try to create a new CSPDirective
@@ -984,34 +965,34 @@ void nsCSPParser::directive() {
     // that array
     return;
   }
 
   // special case handling for block-all-mixed-content, which is only specified
   // by a directive name but does not include any srcs.
   if (cspDir->equals(nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
     if (mCurDir.Length() > 1) {
-      const char16_t* params[] = {u"block-all-mixed-content"};
+      AutoTArray<nsString, 1> params = {
+          NS_LITERAL_STRING("block-all-mixed-content")};
       logWarningErrorToConsole(nsIScriptError::warningFlag,
-                               "ignoreSrcForDirective", params,
-                               ArrayLength(params));
+                               "ignoreSrcForDirective", params);
     }
     // add the directive and return
     mPolicy->addDirective(cspDir);
     return;
   }
 
   // special case handling for upgrade-insecure-requests, which is only
   // specified by a directive name but does not include any srcs.
   if (cspDir->equals(nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
     if (mCurDir.Length() > 1) {
-      const char16_t* params[] = {u"upgrade-insecure-requests"};
+      AutoTArray<nsString, 1> params = {
+          NS_LITERAL_STRING("upgrade-insecure-requests")};
       logWarningErrorToConsole(nsIScriptError::warningFlag,
-                               "ignoreSrcForDirective", params,
-                               ArrayLength(params));
+                               "ignoreSrcForDirective", params);
     }
     // add the directive and return
     mPolicy->addUpgradeInsecDir(
         static_cast<nsUpgradeInsecureDirective*>(cspDir));
     return;
   }
 
   // special case handling for report-uri directive (since it doesn't contain
@@ -1067,39 +1048,36 @@ void nsCSPParser::directive() {
       // Even though we invalidate all of the srcs internally, we don't want to
       // log messages for the srcs: (1) strict-dynamic, (2) unsafe-inline, (3)
       // nonces, and (4) hashes
       if (!srcStr.EqualsASCII(CSP_EnumToUTF8Keyword(CSP_STRICT_DYNAMIC)) &&
           !srcStr.EqualsASCII(CSP_EnumToUTF8Keyword(CSP_UNSAFE_EVAL)) &&
           !StringBeginsWith(
               srcStr, nsDependentString(CSP_EnumToUTF16Keyword(CSP_NONCE))) &&
           !StringBeginsWith(srcStr, NS_LITERAL_STRING("'sha"))) {
-        const char16_t* params[] = {srcStr.get()};
+        AutoTArray<nsString, 1> params = {srcStr};
         logWarningErrorToConsole(nsIScriptError::warningFlag,
-                                 "ignoringSrcForStrictDynamic", params,
-                                 ArrayLength(params));
+                                 "ignoringSrcForStrictDynamic", params);
       }
     }
     // Log a warning that all scripts might be blocked because the policy
     // contains 'strict-dynamic' but no valid nonce or hash.
     if (!mHasHashOrNonce) {
-      const char16_t* params[] = {mCurDir[0].get()};
+      AutoTArray<nsString, 1> params = {mCurDir[0]};
       logWarningErrorToConsole(nsIScriptError::warningFlag,
-                               "strictDynamicButNoHashOrNonce", params,
-                               ArrayLength(params));
+                               "strictDynamicButNoHashOrNonce", params);
     }
   } else if (mHasHashOrNonce && mUnsafeInlineKeywordSrc &&
              (cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) ||
               cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE))) {
     mUnsafeInlineKeywordSrc->invalidate();
     // log to the console that unsafe-inline will be ignored
-    const char16_t* params[] = {u"'unsafe-inline'"};
+    AutoTArray<nsString, 1> params = {NS_LITERAL_STRING("'unsafe-inline'")};
     logWarningErrorToConsole(nsIScriptError::warningFlag,
-                             "ignoringSrcWithinScriptStyleSrc", params,
-                             ArrayLength(params));
+                             "ignoringSrcWithinScriptStyleSrc", params);
   }
 
   // Add the newly created srcs to the directive and add the directive to the
   // policy
   cspDir->addSrcs(srcs);
   mPolicy->addDirective(cspDir);
 }
 
@@ -1171,21 +1149,20 @@ nsCSPPolicy* nsCSPParser::parseContentSe
 
   // Check that report-only policies define a report-uri, otherwise log warning.
   if (aReportOnly) {
     policy->setReportOnlyFlag(true);
     if (!policy->hasDirective(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
       nsAutoCString prePath;
       nsresult rv = aSelfURI->GetPrePath(prePath);
       NS_ENSURE_SUCCESS(rv, policy);
-      NS_ConvertUTF8toUTF16 unicodePrePath(prePath);
-      const char16_t* params[] = {unicodePrePath.get()};
+      AutoTArray<nsString, 1> params;
+      CopyUTF8toUTF16(prePath, *params.AppendElement());
       parser.logWarningErrorToConsole(nsIScriptError::warningFlag,
-                                      "reportURInotInReportOnlyHeader", params,
-                                      ArrayLength(params));
+                                      "reportURInotInReportOnlyHeader", params);
     }
   }
 
   policy->setDeliveredViaMetaTagFlag(aDeliveredViaMetaTag);
 
   if (policy->getNumDirectives() == 0) {
     // Individual errors were already reported in the parser, but if
     // we do not have an enforcable directive at all, we return null.
--- a/dom/security/nsCSPParser.h
+++ b/dom/security/nsCSPParser.h
@@ -103,18 +103,17 @@ class nsCSPParser {
   inline void resetCurValue() { mCurValue.Truncate(); }
 
   bool atEndOfPath();
   bool atValidPathChar();
 
   void resetCurChar(const nsAString& aToken);
 
   void logWarningErrorToConsole(uint32_t aSeverityFlag, const char* aProperty,
-                                const char16_t* aParams[],
-                                uint32_t aParamsLength);
+                                const nsTArray<nsString>& aParams);
 
   /**
    * When parsing the policy, the parser internally uses the following helper
    * variables/members which are used/reset during parsing. The following
    * example explains how they are used.
    * The tokenizer separats all input into arrays of arrays of strings, which
    * are stored in mTokens, for example:
    *   mTokens = [ [ script-src, http://www.example.com, 'self' ], ... ]
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -112,33 +112,33 @@ bool CSP_ShouldResponseInheritCSP(nsICha
   bool isBlob = (NS_SUCCEEDED(uri->SchemeIs("blob", &isBlob)) && isBlob);
   bool isData = (NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData);
   bool isFS = (NS_SUCCEEDED(uri->SchemeIs("filesystem", &isFS)) && isFS);
   bool isJS = (NS_SUCCEEDED(uri->SchemeIs("javascript", &isJS)) && isJS);
 
   return isBlob || isData || isFS || isJS;
 }
 
-void CSP_GetLocalizedStr(const char* aName, const char16_t** aParams,
-                         uint32_t aLength, nsAString& outResult) {
+void CSP_GetLocalizedStr(const char* aName, const nsTArray<nsString>& aParams,
+                         nsAString& outResult) {
   nsCOMPtr<nsIStringBundle> keyStringBundle;
   nsCOMPtr<nsIStringBundleService> stringBundleService =
       mozilla::services::GetStringBundleService();
 
   NS_ASSERTION(stringBundleService, "String bundle service must be present!");
   stringBundleService->CreateBundle(
       "chrome://global/locale/security/csp.properties",
       getter_AddRefs(keyStringBundle));
 
   NS_ASSERTION(keyStringBundle, "Key string bundle must be available!");
 
   if (!keyStringBundle) {
     return;
   }
-  keyStringBundle->FormatStringFromName(aName, aParams, aLength, outResult);
+  keyStringBundle->FormatStringFromName(aName, aParams, outResult);
 }
 
 void CSP_LogStrMessage(const nsAString& aMsg) {
   nsCOMPtr<nsIConsoleService> console(
       do_GetService("@mozilla.org/consoleservice;1"));
 
   if (!console) {
     return;
@@ -199,24 +199,24 @@ void CSP_LogMessage(const nsAString& aMe
     return;
   }
   console->LogMessage(error);
 }
 
 /**
  * Combines CSP_LogMessage and CSP_GetLocalizedStr into one call.
  */
-void CSP_LogLocalizedStr(const char* aName, const char16_t** aParams,
-                         uint32_t aLength, const nsAString& aSourceName,
+void CSP_LogLocalizedStr(const char* aName, const nsTArray<nsString>& aParams,
+                         const nsAString& aSourceName,
                          const nsAString& aSourceLine, uint32_t aLineNumber,
                          uint32_t aColumnNumber, uint32_t aFlags,
                          const nsACString& aCategory, uint64_t aInnerWindowID,
                          bool aFromPrivateWindow) {
   nsAutoString logMsg;
-  CSP_GetLocalizedStr(aName, aParams, aLength, logMsg);
+  CSP_GetLocalizedStr(aName, aParams, logMsg);
   CSP_LogMessage(logMsg, aSourceName, aSourceLine, aLineNumber, aColumnNumber,
                  aFlags, aCategory, aInnerWindowID, aFromPrivateWindow);
 }
 
 /* ===== Helpers ============================ */
 CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType) {
   switch (aType) {
     case nsIContentPolicy::TYPE_IMAGE:
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -22,25 +22,25 @@ class nsIChannel;
 namespace mozilla {
 namespace dom {
 struct CSP;
 }  // namespace dom
 }  // namespace mozilla
 
 /* =============== Logging =================== */
 
-void CSP_LogLocalizedStr(const char* aName, const char16_t** aParams,
-                         uint32_t aLength, const nsAString& aSourceName,
+void CSP_LogLocalizedStr(const char* aName, const nsTArray<nsString>& aParams,
+                         const nsAString& aSourceName,
                          const nsAString& aSourceLine, uint32_t aLineNumber,
                          uint32_t aColumnNumber, uint32_t aFlags,
                          const nsACString& aCategory, uint64_t aInnerWindowID,
                          bool aFromPrivateWindow);
 
-void CSP_GetLocalizedStr(const char* aName, const char16_t** aParams,
-                         uint32_t aLength, nsAString& outResult);
+void CSP_GetLocalizedStr(const char* aName, const nsTArray<nsString>& aParams,
+                         nsAString& outResult);
 
 void CSP_LogStrMessage(const nsAString& aMsg);
 
 void CSP_LogMessage(const nsAString& aMessage, const nsAString& aSourceName,
                     const nsAString& aSourceLine, uint32_t aLineNumber,
                     uint32_t aColumnNumber, uint32_t aFlags,
                     const nsACString& aCategory, uint64_t aInnerWindowID,
                     bool aFromPrivateWindow);
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -816,21 +816,22 @@ nsresult nsMixedContentBlocker::ShouldLo
   // Block all non secure loads in case the CSP directive is present. Please
   // note that at this point we already know, based on |schemeSecure| that the
   // load is not secure, so we can bail out early at this point.
   if (document->GetBlockAllMixedContent(isPreload)) {
     // log a message to the console before returning.
     nsAutoCString spec;
     rv = aContentLocation->GetSpec(spec);
     NS_ENSURE_SUCCESS(rv, rv);
-    NS_ConvertUTF8toUTF16 reportSpec(spec);
 
-    const char16_t* params[] = {reportSpec.get()};
+    AutoTArray<nsString, 1> params;
+    CopyUTF8toUTF16(spec, *params.AppendElement());
+
     CSP_LogLocalizedStr(
-        "blockAllMixedContent", params, ArrayLength(params),
+        "blockAllMixedContent", params,
         EmptyString(),  // aSourceFile
         EmptyString(),  // aScriptSample
         0,              // aLineNumber
         0,              // aColumnNumber
         nsIScriptError::errorFlag, NS_LITERAL_CSTRING("blockAllMixedContent"),
         document->InnerWindowID(),
         !!document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId);
     *aDecision = REJECT_REQUEST;
--- a/dom/security/test/csp/test_report_uri_missing_in_report_only_header.html
+++ b/dom/security/test/csp/test_report_uri_missing_in_report_only_header.html
@@ -15,17 +15,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none"></div>
 <iframe id="cspframe"></iframe>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 var stringBundleService = SpecialPowers.Cc["@mozilla.org/intl/stringbundle;1"]
                           .getService(SpecialPowers.Ci.nsIStringBundleService);
 var localizer = stringBundleService.createBundle("chrome://global/locale/security/csp.properties");
-var warningMsg = localizer.formatStringFromName("reportURInotInReportOnlyHeader", [window.location.origin], 1);
+var warningMsg = localizer.formatStringFromName("reportURInotInReportOnlyHeader", [window.location.origin]);
 function cleanup() {
   SpecialPowers.postConsoleSentinel();
   SimpleTest.finish();
 }
 
 SpecialPowers.registerConsoleListener(function ConsoleMsgListener(aMsg) {
   if (aMsg.message.indexOf(warningMsg) > -1) {
     ok(true, "report-uri not specified in Report-Only should throw a CSP warning.");
--- a/dom/security/test/csp/test_self_none_as_hostname_confusion.html
+++ b/dom/security/test/csp/test_self_none_as_hostname_confusion.html
@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 <pre id="test">
 
 <script class="testbody" type="text/javascript">
 // Load locale string during mochitest
 var stringBundleService = SpecialPowers.Cc["@mozilla.org/intl/stringbundle;1"]
                           .getService(SpecialPowers.Ci.nsIStringBundleService);
 var localizer = stringBundleService.createBundle("chrome://global/locale/security/csp.properties");
-var confusionMsg = localizer.formatStringFromName("hostNameMightBeKeyword", ["SELF", "self"], 2);
+var confusionMsg = localizer.formatStringFromName("hostNameMightBeKeyword", ["SELF", "self"]);
 
 function cleanup() {
   SpecialPowers.postConsoleSentinel();
   SimpleTest.finish();
 };
 
 // To prevent the test from asserting twice and calling SimpleTest.finish() twice,
 // startTest will be marked false as soon as the confusionMsg is detected.
--- a/dom/serviceworkers/test/error_reporting_helpers.js
+++ b/dom/serviceworkers/test/error_reporting_helpers.js
@@ -36,17 +36,17 @@ function expect_console_message(/* msgId
   // process repeated paired arguments of: msgId, args
   for (let i = 0; i < arguments.length; i += 2) {
     let msgId = arguments[i];
     let args = arguments[i + 1];
     if (args.length === 0) {
       expectations.push({errorMessage: localizer.GetStringFromName(msgId)});
     } else {
       expectations.push({
-        errorMessage: localizer.formatStringFromName(msgId, args, args.length)
+        errorMessage: localizer.formatStringFromName(msgId, args)
       });
     }
   }
   return new Promise(resolve => {
     SimpleTest.monitorConsole(resolve, expectations);
   });
 }
 let expect_console_messages = expect_console_message;
--- a/dom/serviceworkers/test/test_fetch_integrity.html
+++ b/dom/serviceworkers/test/test_fetch_integrity.html
@@ -54,17 +54,17 @@ function registerConsoleMonitorCallback(
 
 function waitForMessages() {
   let messages = [];
 
   // process repeated paired arguments of: msgId, args
   for (let i = 0; i < arguments.length; i += 3) {
     let msgId = arguments[i];
     let args = arguments[i + 1];
-    messages.push(security_localizer.formatStringFromName(msgId, args, args.length));
+    messages.push(security_localizer.formatStringFromName(msgId, args));
   }
 
   return new Promise(resolve => {
     registerConsoleMonitorCallback(msg => {
       for (let i = 0; i < messages.length; ++i) {
         if (messages[i] == msg.errorMessage) {
           messages.splice(i, 1);
           break;
@@ -84,17 +84,17 @@ function waitForMessages() {
 function expect_security_console_message(/* msgId, args, ... */) {
   let expectations = [];
   // process repeated paired arguments of: msgId, args
   for (let i = 0; i < arguments.length; i += 3) {
     let msgId = arguments[i];
     let args = arguments[i + 1];
     let filename = arguments[i + 2];
     expectations.push({
-      errorMessage: security_localizer.formatStringFromName(msgId, args, args.length),
+      errorMessage: security_localizer.formatStringFromName(msgId, args),
       sourceName: filename,
     });
   }
   return new Promise(resolve => {
     SimpleTest.monitorConsole(resolve, expectations);
   });
 }
 
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -1086,25 +1086,25 @@ nsresult nsWebBrowserPersist::SendErrorS
   if (!mProgressListener) {
     // Do nothing
     return NS_OK;
   }
 
   // Get the file path or spec from the supplied URI
   nsCOMPtr<nsIFile> file;
   GetLocalFileFromURI(aURI, getter_AddRefs(file));
-  nsAutoString path;
+  AutoTArray<nsString, 1> strings;
   nsresult rv;
   if (file) {
-    file->GetPath(path);
+    file->GetPath(*strings.AppendElement());
   } else {
     nsAutoCString fileurl;
     rv = aURI->GetSpec(fileurl);
     NS_ENSURE_SUCCESS(rv, rv);
-    AppendUTF8toUTF16(fileurl, path);
+    CopyUTF8toUTF16(fileurl, *strings.AppendElement());
   }
 
   const char* msgId;
   switch (aResult) {
     case NS_ERROR_FILE_NAME_TOO_LONG:
       // File name too long.
       msgId = "fileNameTooLongError";
       break;
@@ -1141,19 +1141,17 @@ nsresult nsWebBrowserPersist::SendErrorS
       do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && s, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIStringBundle> bundle;
   rv = s->CreateBundle(kWebBrowserPersistStringBundle, getter_AddRefs(bundle));
   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && bundle, NS_ERROR_FAILURE);
 
   nsAutoString msgText;
-  const char16_t* strings[1];
-  strings[0] = path.get();
-  rv = bundle->FormatStringFromName(msgId, strings, 1, msgText);
+  rv = bundle->FormatStringFromName(msgId, strings, msgText);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
   mProgressListener->OnStatusChange(nullptr, aRequest, aResult, msgText.get());
 
   return NS_OK;
 }
 
 nsresult nsWebBrowserPersist::GetValidURIFromObject(nsISupports* aObject,
--- a/dom/websocket/WebSocket.cpp
+++ b/dom/websocket/WebSocket.cpp
@@ -137,18 +137,17 @@ class WebSocketImpl final : public nsIIn
                       const nsACString& aReasonString = EmptyCString());
   nsresult CloseConnection(uint16_t reasonCode,
                            const nsACString& aReasonString = EmptyCString());
   void Disconnect();
   void DisconnectInternal();
 
   nsresult ConsoleError();
   void PrintErrorOnConsole(const char* aBundleURI, const char* aError,
-                           const char16_t** aFormatStrings,
-                           uint32_t aFormatStringsLen);
+                           nsTArray<nsString>&& aFormatStrings);
 
   nsresult DoOnMessageAvailable(const nsACString& aMsg, bool isBinary);
 
   // ConnectionCloseEvents: 'error' event if needed, then 'close' event.
   nsresult ScheduleConnectionCloseEvents(nsISupports* aContext,
                                          nsresult aStatusCode);
   // 2nd half of ScheduleConnectionCloseEvents, run in its own event.
   void DispatchConnectionCloseEvents();
@@ -259,57 +258,52 @@ class CallDispatchConnectionCloseEvents 
 //-----------------------------------------------------------------------------
 
 namespace {
 
 class PrintErrorOnConsoleRunnable final : public WorkerMainThreadRunnable {
  public:
   PrintErrorOnConsoleRunnable(WebSocketImpl* aImpl, const char* aBundleURI,
                               const char* aError,
-                              const char16_t** aFormatStrings,
-                              uint32_t aFormatStringsLen)
+                              nsTArray<nsString>&& aFormatStrings)
       : WorkerMainThreadRunnable(
             aImpl->mWorkerRef->Private(),
             NS_LITERAL_CSTRING("WebSocket :: print error on console")),
         mImpl(aImpl),
         mBundleURI(aBundleURI),
         mError(aError),
-        mFormatStrings(aFormatStrings),
-        mFormatStringsLen(aFormatStringsLen) {}
+        mFormatStrings(std::move(aFormatStrings)) {}
 
   bool MainThreadRun() override {
-    mImpl->PrintErrorOnConsole(mBundleURI, mError, mFormatStrings,
-                               mFormatStringsLen);
+    mImpl->PrintErrorOnConsole(mBundleURI, mError, std::move(mFormatStrings));
     return true;
   }
 
  private:
   // Raw pointer because this runnable is sync.
   WebSocketImpl* mImpl;
 
   const char* mBundleURI;
   const char* mError;
-  const char16_t** mFormatStrings;
-  uint32_t mFormatStringsLen;
+  nsTArray<nsString> mFormatStrings;
 };
 
 }  // namespace
 
 void WebSocketImpl::PrintErrorOnConsole(const char* aBundleURI,
                                         const char* aError,
-                                        const char16_t** aFormatStrings,
-                                        uint32_t aFormatStringsLen) {
+                                        nsTArray<nsString>&& aFormatStrings) {
   // This method must run on the main thread.
 
   if (!NS_IsMainThread()) {
     MOZ_ASSERT(mWorkerRef);
 
     RefPtr<PrintErrorOnConsoleRunnable> runnable =
         new PrintErrorOnConsoleRunnable(this, aBundleURI, aError,
-                                        aFormatStrings, aFormatStringsLen);
+                                        std::move(aFormatStrings));
     ErrorResult rv;
     runnable->Dispatch(Killing, rv);
     // XXXbz this seems totally broken.  We should be propagating this out, but
     // none of our callers really propagate anything usefully.  Come to think of
     // it, why is this a syncrunnable anyway?  Can't this be a fire-and-forget
     // runnable??
     rv.SuppressException();
     return;
@@ -329,19 +323,18 @@ void WebSocketImpl::PrintErrorOnConsole(
   NS_ENSURE_SUCCESS_VOID(rv);
 
   nsCOMPtr<nsIScriptError> errorObject(
       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS_VOID(rv);
 
   // Localize the error message
   nsAutoString message;
-  if (aFormatStrings) {
-    rv = strBundle->FormatStringFromName(aError, aFormatStrings,
-                                         aFormatStringsLen, message);
+  if (!aFormatStrings.IsEmpty()) {
+    rv = strBundle->FormatStringFromName(aError, aFormatStrings, message);
   } else {
     rv = strBundle->GetStringFromName(aError, message);
   }
   NS_ENSURE_SUCCESS_VOID(rv);
 
   if (mInnerWindowID) {
     rv = errorObject->InitWithWindowID(
         message, NS_ConvertUTF8toUTF16(mScriptFile), EmptyString(), mScriptLine,
@@ -491,27 +484,25 @@ nsresult WebSocketImpl::ConsoleError() {
   {
     MutexAutoLock lock(mMutex);
     if (mWorkerShuttingDown) {
       // Too late to report anything, bail out.
       return NS_OK;
     }
   }
 
-  NS_ConvertUTF8toUTF16 specUTF16(mURI);
-  const char16_t* formatStrings[] = {specUTF16.get()};
+  nsTArray<nsString> formatStrings;
+  CopyUTF8toUTF16(mURI, *formatStrings.AppendElement());
 
   if (mWebSocket->ReadyState() < WebSocket::OPEN) {
     PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                        "connectionFailure", formatStrings,
-                        ArrayLength(formatStrings));
+                        "connectionFailure", std::move(formatStrings));
   } else {
     PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
-                        "netInterrupt", formatStrings,
-                        ArrayLength(formatStrings));
+                        "netInterrupt", std::move(formatStrings));
   }
   /// todo some specific errors - like for message too large
   return NS_OK;
 }
 
 void WebSocketImpl::FailConnection(uint16_t aReasonCode,
                                    const nsACString& aReasonString) {
   AssertIsOnTargetThread();
@@ -1585,27 +1576,28 @@ nsresult WebSocketImpl::Init(JSContext* 
   // Potentially the page uses the CSP directive 'upgrade-insecure-requests'.
   // In such a case we have to upgrade ws: to wss: and also update mSecure
   // to reflect that upgrade. Please note that we can not upgrade from ws:
   // to wss: before performing content policy checks because CSP needs to
   // send reports in case the scheme is about to be upgraded.
   if (!mIsServerSide && !mSecure && originDoc &&
       originDoc->GetUpgradeInsecureRequests(false)) {
     // let's use the old specification before the upgrade for logging
-    NS_ConvertUTF8toUTF16 reportSpec(mURI);
+    AutoTArray<nsString, 2> params;
+    CopyUTF8toUTF16(mURI, *params.AppendElement());
 
     // upgrade the request from ws:// to wss:// and mark as secure
     mURI.ReplaceSubstring("ws://", "wss://");
     if (NS_WARN_IF(mURI.Find("wss://") != 0)) {
       return NS_OK;
     }
     mSecure = true;
 
-    const char16_t* params[] = {reportSpec.get(), u"wss"};
-    CSP_LogLocalizedStr("upgradeInsecureRequest", params, ArrayLength(params),
+    params.AppendElement(NS_LITERAL_STRING("wss"));
+    CSP_LogLocalizedStr("upgradeInsecureRequest", params,
                         EmptyString(),  // aSourceFile
                         EmptyString(),  // aScriptSample
                         0,              // aLineNumber
                         0,              // aColumnNumber
                         nsIScriptError::warningFlag,
                         NS_LITERAL_CSTRING("upgradeInsecureRequest"),
                         mInnerWindowID, mPrivateBrowsing);
   }
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -933,30 +933,29 @@ void txMozillaXSLTProcessor::reportError
   mTransformResult = aResult;
 
   if (aErrorText) {
     mErrorText.Assign(aErrorText);
   } else {
     nsCOMPtr<nsIStringBundleService> sbs =
         mozilla::services::GetStringBundleService();
     if (sbs) {
-      nsAutoString errorText;
+      nsString errorText;
       sbs->FormatStatusMessage(aResult, EmptyString().get(), errorText);
 
       nsAutoString errorMessage;
       nsCOMPtr<nsIStringBundle> bundle;
       sbs->CreateBundle(XSLT_MSGS_URL, getter_AddRefs(bundle));
 
       if (bundle) {
-        const char16_t* error[] = {errorText.get()};
+        AutoTArray<nsString, 1> error = {errorText};
         if (mStylesheet) {
-          bundle->FormatStringFromName("TransformError", error, 1,
-                                       errorMessage);
+          bundle->FormatStringFromName("TransformError", error, errorMessage);
         } else {
-          bundle->FormatStringFromName("LoadingError", error, 1, errorMessage);
+          bundle->FormatStringFromName("LoadingError", error, errorMessage);
         }
       }
       mErrorText.Assign(errorMessage);
     }
   }
 
   if (aSourceText) {
     mSourceText.Assign(aSourceText);
--- a/extensions/pref/autoconfig/src/prefcalls.js
+++ b/extensions/pref/autoconfig/src/prefcalls.js
@@ -178,17 +178,17 @@ function displayError(funcname, message)
     try {
         var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"]
                               .getService(Ci.nsIPromptService);
         var bundle = Cc["@mozilla.org/intl/stringbundle;1"]
                        .getService(Ci.nsIStringBundleService)
                        .createBundle("chrome://autoconfig/locale/autoconfig.properties");
 
          var title = bundle.GetStringFromName("autoConfigTitle");
-         var msg = bundle.formatStringFromName("autoConfigMsg", [funcname], 1);
+         var msg = bundle.formatStringFromName("autoConfigMsg", [funcname]);
          promptService.alert(null, title, msg + " " + message);
     } catch (e) { }
 }
 
 function getenv(name) {
     try {
         var environment = Cc["@mozilla.org/process/environment;1"].
             getService(Ci.nsIEnvironment);
--- a/intl/strres/nsIStringBundle.idl
+++ b/intl/strres/nsIStringBundle.idl
@@ -43,33 +43,27 @@ interface nsIStringBundle : nsISupports
   [noscript, binaryname(GetStringFromName)]
   AString GetStringFromNameCpp(in string aName);
 
   // this is kind of like ssprintf - except that you can
   // only pass it unicode strings, using the %S formatting character.
   // the id or name should refer to a string in the bundle that
   // uses %S.. do NOT try to use any other types.
   // this uses nsTextFormatter::ssprintf to do the dirty work.
-  AString formatStringFromID(in long aID,
-                             [array, size_is(length)] in wstring params,
-                             in unsigned long length);
+  AString formatStringFromID(in long aID, in Array<AString> params);
 
   // This method is mostly used from JS, where AUTF8String is appropriate.
   [binaryname(FormatStringFromAUTF8Name)]
-  AString formatStringFromName(in AUTF8String aName,
-                               [array, size_is(length)] in wstring params,
-                               in unsigned long length);
+  AString formatStringFromName(in AUTF8String aName, in Array<AString> params);
 
   // This method is mostly used from C++, where |string| is appropriate because
   // the names are most often 8-bit string literals (normally ASCII, though
   // u8"foo" literals will also work).
   [noscript, binaryname(FormatStringFromName)]
-  AString formatStringFromNameCpp(in string aName,
-                                  [array, size_is(length)] in wstring params,
-                                  in unsigned long length);
+  AString formatStringFromNameCpp(in string aName, in Array<AString> params);
 
   /*
   Implements nsISimpleEnumerator, replaces nsIEnumerator
   */
   nsISimpleEnumerator getSimpleEnumeration();
   // Preloads string bundle data asynchronously
   void asyncPreload();
 
--- a/intl/strres/nsStringBundle.cpp
+++ b/intl/strres/nsStringBundle.cpp
@@ -567,47 +567,47 @@ nsresult SharedStringBundle::GetStringIm
 
   if (mStringMap->Get(PromiseFlatCString(aName), aResult)) {
     return NS_OK;
   }
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
-nsStringBundleBase::FormatStringFromID(int32_t aID, const char16_t** aParams,
-                                       uint32_t aLength, nsAString& aResult) {
+nsStringBundleBase::FormatStringFromID(int32_t aID,
+                                       const nsTArray<nsString>& aParams,
+                                       nsAString& aResult) {
   nsAutoCString idStr;
   idStr.AppendInt(aID, 10);
-  return FormatStringFromName(idStr.get(), aParams, aLength, aResult);
+  return FormatStringFromName(idStr.get(), aParams, aResult);
 }
 
 // this function supports at most 10 parameters.. see below for why
 NS_IMETHODIMP
 nsStringBundleBase::FormatStringFromAUTF8Name(const nsACString& aName,
-                                              const char16_t** aParams,
-                                              uint32_t aLength,
+                                              const nsTArray<nsString>& aParams,
                                               nsAString& aResult) {
-  return FormatStringFromName(PromiseFlatCString(aName).get(), aParams, aLength,
+  return FormatStringFromName(PromiseFlatCString(aName).get(), aParams,
                               aResult);
 }
 
 // this function supports at most 10 parameters.. see below for why
 NS_IMETHODIMP
 nsStringBundleBase::FormatStringFromName(const char* aName,
-                                         const char16_t** aParams,
-                                         uint32_t aLength, nsAString& aResult) {
-  NS_ASSERTION(aParams && aLength,
+                                         const nsTArray<nsString>& aParams,
+                                         nsAString& aResult) {
+  NS_ASSERTION(!aParams.IsEmpty(),
                "FormatStringFromName() without format parameters: use "
                "GetStringFromName() instead");
 
   nsAutoString formatStr;
   nsresult rv = GetStringFromName(aName, formatStr);
   if (NS_FAILED(rv)) return rv;
 
-  return FormatString(formatStr.get(), aParams, aLength, aResult);
+  return FormatString(formatStr.get(), aParams, aResult);
 }
 
 NS_IMETHODIMP
 nsStringBundleBase::GetSimpleEnumeration(nsISimpleEnumerator** aElements) {
   NS_ENSURE_ARG_POINTER(aElements);
 
   return GetSimpleEnumerationImpl(aElements);
 }
@@ -645,34 +645,38 @@ StringMapEnumerator::GetNext(nsISupports
 
   elem.forget(aNext);
 
   mIndex++;
   return NS_OK;
 }
 
 nsresult nsStringBundleBase::FormatString(const char16_t* aFormatStr,
-                                          const char16_t** aParams,
-                                          uint32_t aLength,
+                                          const nsTArray<nsString>& aParams,
                                           nsAString& aResult) {
-  NS_ENSURE_ARG(aLength <= 10);  // enforce 10-parameter limit
+  auto length = aParams.Length();
+  NS_ENSURE_ARG(length <= 10);  // enforce 10-parameter limit
 
   // implementation note: you would think you could use vsmprintf
   // to build up an arbitrary length array.. except that there
   // is no way to build up a va_list at runtime!
   // Don't believe me? See:
   //   http://www.eskimo.com/~scs/C-faq/q15.13.html
   // -alecf
-  nsTextFormatter::ssprintf(
-      aResult, aFormatStr, aLength >= 1 ? aParams[0] : nullptr,
-      aLength >= 2 ? aParams[1] : nullptr, aLength >= 3 ? aParams[2] : nullptr,
-      aLength >= 4 ? aParams[3] : nullptr, aLength >= 5 ? aParams[4] : nullptr,
-      aLength >= 6 ? aParams[5] : nullptr, aLength >= 7 ? aParams[6] : nullptr,
-      aLength >= 8 ? aParams[7] : nullptr, aLength >= 9 ? aParams[8] : nullptr,
-      aLength >= 10 ? aParams[9] : nullptr);
+  nsTextFormatter::ssprintf(aResult, aFormatStr,
+                            length >= 1 ? aParams[0].get() : nullptr,
+                            length >= 2 ? aParams[1].get() : nullptr,
+                            length >= 3 ? aParams[2].get() : nullptr,
+                            length >= 4 ? aParams[3].get() : nullptr,
+                            length >= 5 ? aParams[4].get() : nullptr,
+                            length >= 6 ? aParams[5].get() : nullptr,
+                            length >= 7 ? aParams[6].get() : nullptr,
+                            length >= 8 ? aParams[7].get() : nullptr,
+                            length >= 9 ? aParams[8].get() : nullptr,
+                            length >= 10 ? aParams[9].get() : nullptr);
 
   return NS_OK;
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////
 
 #define MAX_CACHED_BUNDLES 16
 
@@ -895,37 +899,33 @@ NS_IMETHODIMP
 nsStringBundleService::CreateBundle(const char* aURLSpec,
                                     nsIStringBundle** aResult) {
   getStringBundle(aURLSpec, aResult);
   return NS_OK;
 }
 
 #define GLOBAL_PROPERTIES "chrome://global/locale/global-strres.properties"
 
-nsresult nsStringBundleService::FormatWithBundle(nsIStringBundle* bundle,
-                                                 nsresult aStatus,
-                                                 uint32_t argCount,
-                                                 char16_t** argArray,
-                                                 nsAString& result) {
+nsresult nsStringBundleService::FormatWithBundle(
+    nsIStringBundle* bundle, nsresult aStatus,
+    const nsTArray<nsString>& argArray, nsAString& result) {
   nsresult rv;
 
   // try looking up the error message with the int key:
   uint16_t code = NS_ERROR_GET_CODE(aStatus);
-  rv = bundle->FormatStringFromID(code, (const char16_t**)argArray, argCount,
-                                  result);
+  rv = bundle->FormatStringFromID(code, argArray, result);
 
   // If the int key fails, try looking up the default error message. E.g. print:
   //   An unknown error has occurred (0x804B0003).
   if (NS_FAILED(rv)) {
-    nsAutoString statusStr;
-    statusStr.AppendInt(static_cast<uint32_t>(aStatus), 16);
-    const char16_t* otherArgArray[1];
-    otherArgArray[0] = statusStr.get();
+    AutoTArray<nsString, 1> otherArgArray;
+    otherArgArray.AppendElement()->AppendInt(static_cast<uint32_t>(aStatus),
+                                             16);
     uint16_t code = NS_ERROR_GET_CODE(NS_ERROR_FAILURE);
-    rv = bundle->FormatStringFromID(code, otherArgArray, 1, result);
+    rv = bundle->FormatStringFromID(code, otherArgArray, result);
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsStringBundleService::FormatStatusMessage(nsresult aStatus,
                                            const char16_t* aStatusArg,
@@ -944,49 +944,37 @@ nsStringBundleService::FormatStatusMessa
   if (aStatus == NS_OK) {
     return NS_ERROR_FAILURE;  // no message to format
   }
 
   // format the arguments:
   const nsDependentString args(aStatusArg);
   argCount = args.CountChar(char16_t('\n')) + 1;
   NS_ENSURE_ARG(argCount <= 10);  // enforce 10-parameter limit
-  char16_t* argArray[10];
+  AutoTArray<nsString, 10> argArray;
 
-  // convert the aStatusArg into a char16_t array
+  // convert the aStatusArg into an nsString array
   if (argCount == 1) {
-    // avoid construction for the simple case:
-    argArray[0] = (char16_t*)aStatusArg;
+    argArray.AppendElement(aStatusArg);
   } else if (argCount > 1) {
     int32_t offset = 0;
     for (i = 0; i < argCount; i++) {
       int32_t pos = args.FindChar('\n', offset);
       if (pos == -1) pos = args.Length();
-      argArray[i] = ToNewUnicode(Substring(args, offset, pos - offset));
-      if (argArray[i] == nullptr) {
-        rv = NS_ERROR_OUT_OF_MEMORY;
-        argCount = i - 1;  // don't try to free uninitialized memory
-        goto done;
-      }
+      argArray.AppendElement(Substring(args, offset, pos - offset));
       offset = pos + 1;
     }
   }
 
   // find the string bundle for the error's module:
   rv = mErrorService->GetErrorStringBundle(NS_ERROR_GET_MODULE(aStatus),
                                            getter_Copies(stringBundleURL));
   if (NS_SUCCEEDED(rv)) {
     getStringBundle(stringBundleURL.get(), getter_AddRefs(bundle));
-    rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result);
+    rv = FormatWithBundle(bundle, aStatus, argArray, result);
   }
   if (NS_FAILED(rv)) {
     getStringBundle(GLOBAL_PROPERTIES, getter_AddRefs(bundle));
-    rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result);
+    rv = FormatWithBundle(bundle, aStatus, argArray, result);
   }
 
-done:
-  if (argCount > 1) {
-    for (i = 0; i < argCount; i++) {
-      if (argArray[i]) free(argArray[i]);
-    }
-  }
   return rv;
 }
--- a/intl/strres/nsStringBundle.h
+++ b/intl/strres/nsStringBundle.h
@@ -57,17 +57,17 @@ class nsStringBundleBase : public nsIStr
   bool mAttemptedLoad;
   bool mLoaded;
 
   size_t SizeOfIncludingThisIfUnshared(
       mozilla::MallocSizeOf aMallocSizeOf) const override;
 
  public:
   static nsresult FormatString(const char16_t* formatStr,
-                               const char16_t** aParams, uint32_t aLength,
+                               const nsTArray<nsString>& aParams,
                                nsAString& aResult);
 };
 
 class nsStringBundle : public nsStringBundleBase {
  public:
   NS_DECL_ISUPPORTS_INHERITED
 
   nsCOMPtr<nsIPersistentProperties> mProps;
--- a/intl/strres/nsStringBundleService.h
+++ b/intl/strres/nsStringBundleService.h
@@ -56,17 +56,17 @@ class nsStringBundleService : public nsI
                              const mozilla::ipc::FileDescriptor& aMapFile,
                              size_t aMapSize) override;
 
  private:
   virtual ~nsStringBundleService();
 
   void getStringBundle(const char* aUrl, nsIStringBundle** aResult);
   nsresult FormatWithBundle(nsIStringBundle* bundle, nsresult aStatus,
-                            uint32_t argCount, char16_t** argArray,
+                            const nsTArray<nsString>& argArray,
                             nsAString& result);
 
   void flushBundleCache(bool ignoreShared = true);
 
   mozilla::UniquePtr<bundleCacheEntry_t> evictOneEntry();
 
   bundleCacheEntry_t* insertIntoCache(already_AddRefed<nsIStringBundle> aBundle,
                                       const nsACString& aHashKey);
--- a/intl/strres/tests/unit/test_bug378839.js
+++ b/intl/strres/tests/unit/test_bug378839.js
@@ -53,12 +53,12 @@ function run_test() {
     Assert.equal(bundle_view, value_view);
 
     var bundle_go = bundle.GetStringFromName(name_go);
     Assert.equal(bundle_go, value_go);
 
     var bundle_message = bundle.GetStringFromName(name_message);
     Assert.equal(bundle_message, value_message);
 
-    var bundle_hello = bundle.formatStringFromName(name_hello, [var_hello], 1);
+    var bundle_hello = bundle.formatStringFromName(name_hello, [var_hello]);
     Assert.equal(bundle_hello, value_hello);
 }
     
--- a/layout/mathml/tests/test_bug553917.html
+++ b/layout/mathml/tests/test_bug553917.html
@@ -73,17 +73,17 @@ https://bugzilla.mozilla.org/show_bug.cg
                           "AttributeParsingErrorNoTag","LengthParsingError", "MMultiscriptsErrors",
                           "UnitlessValuesAreDeprecated"];
       
       function getErrorMessage(name,idx)
       {
         if (name != "MMultiscriptsErrors") {
           var formatParams = g_errorInfo[name].args[idx];
           if (formatParams.length > 0) {
-            return g_bundl.formatStringFromName(name,formatParams,formatParams.length);
+            return g_bundl.formatStringFromName(name,formatParams);
           } else {
             return g_bundl.GetStringFromName(name);
           }
         } else {
           return g_bundl.GetStringFromName(g_errorInfo[name].args[idx]);
         }
       }
     
--- a/layout/mathml/tests/test_bug827713-2.html
+++ b/layout/mathml/tests/test_bug827713-2.html
@@ -30,18 +30,17 @@ https://bugzilla.mozilla.org/show_bug.cg
         }
     };
       
       var g_errorTypes = ["InvalidChild", "MMultiscriptsErrors"];
       
       function getErrorMessage(name,idx)
       {
         if (name != "MMultiscriptsErrors") {
-          return g_bundl.formatStringFromName(name,g_errorInfo[name].args[idx], 
-                                            g_errorInfo[name].args[idx].length);
+          return g_bundl.formatStringFromName(name,g_errorInfo[name].args[idx]);
         }
         else {
           return g_bundl.GetStringFromName(g_errorInfo[name].args[idx]);
         }
       }
     
     /** Checks the roll call to see if all expected error messages were present. */
     function processRollCall()
--- a/layout/style/ErrorReporter.cpp
+++ b/layout/style/ErrorReporter.cpp
@@ -297,22 +297,19 @@ void ErrorReporter::AddToError(const nsS
 void ErrorReporter::ReportUnexpected(const char* aMessage) {
   MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
 
   nsAutoString str;
   sStringBundle->GetStringFromName(aMessage, str);
   AddToError(str);
 }
 
-void ErrorReporter::ReportUnexpectedUnescaped(const char* aMessage,
-                                              const nsAutoString& aParam) {
+void ErrorReporter::ReportUnexpectedUnescaped(
+    const char* aMessage, const nsTArray<nsString>& aParam) {
   MOZ_ASSERT(ShouldReportErrors(mSheet, mLoader));
 
-  const char16_t* params[1] = {aParam.get()};
-
   nsAutoString str;
-  sStringBundle->FormatStringFromName(aMessage, params, ArrayLength(params),
-                                      str);
+  sStringBundle->FormatStringFromName(aMessage, aParam, str);
   AddToError(str);
 }
 
 }  // namespace css
 }  // namespace mozilla
--- a/layout/style/ErrorReporter.h
+++ b/layout/style/ErrorReporter.h
@@ -50,17 +50,17 @@ class MOZ_STACK_CLASS ErrorReporter fina
   // In all overloads of ReportUnexpected, aMessage is a stringbundle
   // name, which will be processed as a format string with the
   // indicated number of parameters.
 
   // no parameters
   void ReportUnexpected(const char* aMessage);
   // one parameter which has already been escaped appropriately
   void ReportUnexpectedUnescaped(const char* aMessage,
-                                 const nsAutoString& aParam);
+                                 const nsTArray<nsString>& aParam);
 
  private:
   void OutputError();
   void AddToError(const nsString& aErrorText);
   static void InitGlobals();
 
   static bool sInitialized;
   static bool sReportErrors;
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1956,26 +1956,28 @@ void Gecko_ReportUnexpectedCSSError(
     uint32_t colNumber) {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   ErrorReporter reporter(aSheet, aLoader, aURI);
 
   if (prefix) {
     if (prefixParam) {
       nsDependentCSubstring paramValue(prefixParam, prefixParamLen);
-      nsAutoString wideParam = NS_ConvertUTF8toUTF16(paramValue);
+      AutoTArray<nsString, 1> wideParam;
+      CopyUTF8toUTF16(paramValue, *wideParam.AppendElement());
       reporter.ReportUnexpectedUnescaped(prefix, wideParam);
     } else {
       reporter.ReportUnexpected(prefix);
     }
   }
 
   if (param) {
     nsDependentCSubstring paramValue(param, paramLen);
-    nsAutoString wideParam = NS_ConvertUTF8toUTF16(paramValue);
+    AutoTArray<nsString, 1> wideParam;
+    CopyUTF8toUTF16(paramValue, *wideParam.AppendElement());
     reporter.ReportUnexpectedUnescaped(message, wideParam);
   } else {
     reporter.ReportUnexpected(message);
   }
 
   if (suffix) {
     reporter.ReportUnexpected(suffix);
   }
--- a/mobile/android/chrome/content/ConsoleAPI.js
+++ b/mobile/android/chrome/content/ConsoleAPI.js
@@ -17,31 +17,31 @@ var ConsoleAPI = {
       Services.console.logMessage(consoleMsg);
     } else if (aMessage.level == "trace") {
       let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
       let args = aMessage.arguments;
       let filename = this.abbreviateSourceURL(args[0].filename);
       let functionName = args[0].functionName || bundle.GetStringFromName("stacktrace.anonymousFunction");
       let lineNumber = args[0].lineNumber;
 
-      let body = bundle.formatStringFromName("stacktrace.outputMessage", [filename, functionName, lineNumber], 3);
+      let body = bundle.formatStringFromName("stacktrace.outputMessage", [filename, functionName, lineNumber]);
       body += "\n";
       args.forEach(function(aFrame) {
         let functionName = aFrame.functionName || bundle.GetStringFromName("stacktrace.anonymousFunction");
         body += "  " + aFrame.filename + " :: " + functionName + " :: " + aFrame.lineNumber + "\n";
       });
 
       Services.console.logStringMessage(body);
     } else if (aMessage.level == "time" && aMessage.arguments) {
       let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
-      let body = bundle.formatStringFromName("timer.start", [aMessage.arguments.name], 1);
+      let body = bundle.formatStringFromName("timer.start", [aMessage.arguments.name]);
       Services.console.logStringMessage(body);
     } else if (aMessage.level == "timeEnd" && aMessage.arguments) {
       let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
-      let body = bundle.formatStringFromName("timer.end", [aMessage.arguments.name, aMessage.arguments.duration], 2);
+      let body = bundle.formatStringFromName("timer.end", [aMessage.arguments.name, aMessage.arguments.duration]);
       Services.console.logStringMessage(body);
     } else if (["group", "groupCollapsed", "groupEnd"].includes(aMessage.level)) {
       // Do nothing yet
     } else {
       Services.console.logStringMessage(joinedArguments);
     }
   },
 
--- a/mobile/android/chrome/content/OfflineApps.js
+++ b/mobile/android/chrome/content/OfflineApps.js
@@ -40,17 +40,17 @@ var OfflineApps = {
       label: strings.GetStringFromName("offlineApps.allow"),
       callback: function() {
         OfflineApps.allowSite(aContentWindow.document);
       },
       positive: true,
     }];
 
     let requestor = BrowserApp.manifest ? "'" + BrowserApp.manifest.name + "'" : host;
-    let message = strings.formatStringFromName("offlineApps.ask", [requestor], 1);
+    let message = strings.formatStringFromName("offlineApps.ask", [requestor]);
     let options = { checkbox: Strings.browser.GetStringFromName("offlineApps.dontAskAgain") };
     NativeWindow.doorhanger.show(message, notificationID, buttons, tab.id, options);
   },
 
   allowSite: function(aDocument) {
     Services.perms.addFromPrincipal(aDocument.nodePrincipal, "offline-app", Services.perms.ALLOW_ACTION);
 
     // When a site is enabled while loading, manifest resources will
--- a/mobile/android/chrome/content/RemoteDebugger.js
+++ b/mobile/android/chrome/content/RemoteDebugger.js
@@ -129,17 +129,17 @@ var RemoteDebugger = {
       return DebuggerServer.AuthenticationResult.DENY;
     }
 
     return new Promise(resolve => {
       let title = Strings.browser.GetStringFromName("remoteIncomingPromptTitle");
       let msg = Strings.browser.formatStringFromName("remoteIncomingPromptTCP", [
         session.client.host,
         session.client.port,
-      ], 2);
+      ]);
       let scan = Strings.browser.GetStringFromName("remoteIncomingPromptScan");
       let scanAndRemember = Strings.browser.GetStringFromName("remoteIncomingPromptScanAndRemember");
       let deny = Strings.browser.GetStringFromName("remoteIncomingPromptDeny");
 
       // Make prompt. Note: button order is in reverse.
       let prompt = new Prompt({
         window: null,
         hint: "remotedebug",
--- a/mobile/android/chrome/content/aboutAddons.js
+++ b/mobile/android/chrome/content/aboutAddons.js
@@ -387,17 +387,17 @@ var Addons = {
 
     let favicon = document.querySelector("#addons-details > .addon-item .icon");
     favicon.setAttribute("src", addon.iconURL || AMO_ICON);
 
     detailItem.querySelector(".title").textContent = addon.name;
     detailItem.querySelector(".version").textContent = addon.version;
     detailItem.querySelector(".description-full").textContent = addon.description;
     detailItem.querySelector(".status-uninstalled").textContent =
-      gStringBundle.formatStringFromName("addonStatus.uninstalled", [addon.name], 1);
+      gStringBundle.formatStringFromName("addonStatus.uninstalled", [addon.name]);
 
     let updateBtn = document.getElementById("update-btn");
     if (this._addonHasUpdate(addon)) {
       updateBtn.removeAttribute("hidden");
     } else {
       updateBtn.setAttribute("hidden", true);
     }
 
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1295,17 +1295,17 @@ var BrowserApp = {
       if (closedTabData) {
         let message;
         let title = closedTabData.entries[closedTabData.index - 1].title;
         let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(aTab.browser);
 
         if (isPrivate) {
           message = Strings.browser.GetStringFromName("privateClosedMessage.message");
         } else if (title) {
-          message = Strings.browser.formatStringFromName("undoCloseToast.message", [title], 1);
+          message = Strings.browser.formatStringFromName("undoCloseToast.message", [title]);
         } else {
           message = Strings.browser.GetStringFromName("undoCloseToast.messageDefault");
         }
 
         Snackbars.show(message, Snackbars.LENGTH_LONG, {
           action: {
             label: Strings.browser.GetStringFromName("undoCloseToast.action2"),
             callback: function() {
@@ -3263,17 +3263,17 @@ ChromeUtils.defineModuleGetter(this, "Pa
 // These alias to the old, deprecated NativeWindow interfaces
 [
   ["pageactions", "resource://gre/modules/PageActions.jsm", "PageActions"],
   ["toast", "resource://gre/modules/Snackbars.jsm", "Snackbars"],
 ].forEach(item => {
   let [name, script, exprt] = item;
 
   XPCOMUtils.defineLazyGetter(NativeWindow, name, () => {
-    var err = Strings.browser.formatStringFromName("nativeWindow.deprecated", ["NativeWindow." + name, script], 2);
+    var err = Strings.browser.formatStringFromName("nativeWindow.deprecated", ["NativeWindow." + name, script]);
     Cu.reportError(err);
 
     let sandbox = {};
     ChromeUtils.import(script, sandbox);
     return sandbox[exprt];
   });
 });
 
@@ -5086,17 +5086,17 @@ var XPInstallObserver = {
         let enabled = Services.prefs.getBoolPref("xpinstall.enabled", true);
 
         let buttons, message, callback;
         if (!enabled) {
           message = strings.GetStringFromName("xpinstallDisabledMessageLocked");
           buttons = [strings.GetStringFromName("unsignedAddonsDisabled.dismiss")];
           (data) => {};
         } else {
-          message = strings.formatStringFromName("xpinstallDisabledMessage2", [brandShortName, host], 2);
+          message = strings.formatStringFromName("xpinstallDisabledMessage2", [brandShortName, host]);
           buttons = [
               strings.GetStringFromName("xpinstallDisabledButton"),
               strings.GetStringFromName("unsignedAddonsDisabled.dismiss"),
           ];
           (data) => {
             if (data.button === 1) {
               Services.prefs.setBoolPref("xpinstall.enabled", true);
             }
@@ -5113,29 +5113,29 @@ var XPInstallObserver = {
       }
       case "addon-install-blocked": {
         if (!tab)
           return;
 
         let message;
         if (host) {
           // We have a host which asked for the install.
-          message = strings.formatStringFromName("xpinstallPromptWarning2", [brandShortName, host], 2);
+          message = strings.formatStringFromName("xpinstallPromptWarning2", [brandShortName, host]);
         } else {
           // Without a host we address the add-on as the initiator of the install.
           let addon = null;
           if (installInfo.installs.length > 0) {
             addon = installInfo.installs[0].name;
           }
           if (addon) {
             // We have an addon name, show the regular message.
-            message = strings.formatStringFromName("xpinstallPromptWarningLocal", [brandShortName, addon], 2);
+            message = strings.formatStringFromName("xpinstallPromptWarningLocal", [brandShortName, addon]);
           } else {
             // We don't have an addon name, show an alternative message.
-            message = strings.formatStringFromName("xpinstallPromptWarningDirect", [brandShortName], 1);
+            message = strings.formatStringFromName("xpinstallPromptWarningDirect", [brandShortName]);
           }
         }
 
         let buttons = [
             strings.GetStringFromName("xpinstallPromptAllowButton"),
             strings.GetStringFromName("unsignedAddonsDisabled.dismiss"),
         ];
         new Prompt({
@@ -5153,17 +5153,17 @@ var XPInstallObserver = {
       }
       case "addon-install-origin-blocked": {
         if (!tab)
           return;
 
         new Prompt({
           window: window,
           title: Strings.browser.GetStringFromName("addonError.titleBlocked"),
-          message: strings.formatStringFromName("xpinstallPromptWarningDirect", [brandShortName], 1),
+          message: strings.formatStringFromName("xpinstallPromptWarningDirect", [brandShortName]),
           buttons: [strings.GetStringFromName("unsignedAddonsDisabled.dismiss")],
         }).show((data) => {});
         break;
       }
       case "xpi-signature-changed": {
         if (JSON.parse(aData).disabled.length) {
           this._notifyUnsignedAddonsDisabled();
         }
@@ -5450,17 +5450,17 @@ var IndexedDB = {
       return;
 
     let host = browser.currentURI.asciiHost;
 
     let strings = Strings.browser;
 
     let message, responseTopic;
     if (topic == this._permissionsPrompt) {
-      message = strings.formatStringFromName("offlineApps.ask", [host], 1);
+      message = strings.formatStringFromName("offlineApps.ask", [host]);
       responseTopic = this._permissionsResponse;
     }
 
     const firstTimeoutDuration = 300000; // 5 minutes
 
     let timeoutId;
 
     let notificationID = responseTopic + host;
@@ -5788,29 +5788,29 @@ var IdentityHandler = {
         aState & Ci.nsIWebProgressListener.STATE_IS_BROKEN) {
       result.secure = false;
       return result;
     }
 
     result.secure = true;
 
     let iData = this.getIdentityData();
-    result.verifier = Strings.browser.formatStringFromName("identity.identified.verifier", [iData.caOrg], 1);
+    result.verifier = Strings.browser.formatStringFromName("identity.identified.verifier", [iData.caOrg]);
 
     // If the cert is identified, then we can populate the results with credentials
     if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
       result.owner = iData.subjectOrg;
 
       // Build an appropriate supplemental block out of whatever location data we have
       let supplemental = "";
       if (iData.city) {
         supplemental += iData.city + "\n";
       }
       if (iData.state && iData.country) {
-        supplemental += Strings.browser.formatStringFromName("identity.identified.state_and_country", [iData.state, iData.country], 2);
+        supplemental += Strings.browser.formatStringFromName("identity.identified.state_and_country", [iData.state, iData.country]);
         result.country = iData.country;
       } else if (iData.state) { // State only
         supplemental += iData.state;
       } else if (iData.country) { // Country only
         supplemental += iData.country;
         result.country = iData.country;
       }
       result.supplemental = supplemental;
@@ -6020,29 +6020,29 @@ var SearchEngines = {
       }
     });
   },
 
   addOpenSearchEngine: async function addOpenSearchEngine(engine) {
     try {
       await Services.search.addEngine(engine.url, engine.iconURL, false);
       // Display a toast confirming addition of new search engine.
-      Snackbars.show(Strings.browser.formatStringFromName("alertSearchEngineAddedToast", [engine.title], 1), Snackbars.LENGTH_LONG);
+      Snackbars.show(Strings.browser.formatStringFromName("alertSearchEngineAddedToast", [engine.title]), Snackbars.LENGTH_LONG);
     } catch (ex) {
       let code = ex.result;
       let errorMessage;
       if (code == 2) {
         // Engine is a duplicate.
         errorMessage = "alertSearchEngineDuplicateToast";
       } else {
         // Unknown failure. Display general error message.
         errorMessage = "alertSearchEngineErrorToast";
       }
 
-      Snackbars.show(Strings.browser.formatStringFromName(errorMessage, [engine.title], 1), Snackbars.LENGTH_LONG);
+      Snackbars.show(Strings.browser.formatStringFromName(errorMessage, [engine.title]), Snackbars.LENGTH_LONG);
     }
   },
 
   /**
    * Build and return an array of sorted form data / Query Parameters
    * for an element in a submission form.
    *
    * @param element
@@ -6165,17 +6165,17 @@ var SearchEngines = {
         // if there's already an engine with this name, add a number to
         // make the name unique (e.g., "Google" becomes "Google 2")
         let name = title.value;
         for (let i = 2; Services.search.getEngineByName(name); i++) {
             name = title.value + " " + i;
         }
 
         await Services.search.addEngineWithDetails(name, data, null, null, method, formURL);
-        Snackbars.show(Strings.browser.formatStringFromName("alertSearchEngineAddedToast", [name], 1), Snackbars.LENGTH_LONG);
+        Snackbars.show(Strings.browser.formatStringFromName("alertSearchEngineAddedToast", [name]), Snackbars.LENGTH_LONG);
 
         let engine = Services.search.getEngineByName(name);
         engine.wrappedJSObject._queryCharset = charset;
         formData.forEach(param => { engine.addParam(param.name, param.value, null); });
 
         if (resultCallback) {
             return resultCallback(true);
         }
@@ -6299,17 +6299,17 @@ var ExternalApps = {
       while (node && !uri) {
         uri = ExternalApps._getMediaLink(node);
         node = node.parentNode;
       }
       let apps = [];
       if (uri)
         apps = HelperApps.getAppsForUri(uri);
 
-      return apps.length == 1 ? Strings.browser.formatStringFromName("helperapps.openWithApp2", [apps[0].name], 1) :
+      return apps.length == 1 ? Strings.browser.formatStringFromName("helperapps.openWithApp2", [apps[0].name]) :
                                 Strings.browser.GetStringFromName("helperapps.openWithList2");
     }, this.filter, this.openExternal);
   },
 
   filter: {
     matches: function(aElement) {
       if (!Services.prefs.getBoolPref("network.protocol-handler.external-default")) {
         return false;
--- a/mobile/android/chrome/content/content.js
+++ b/mobile/android/chrome/content/content.js
@@ -136,21 +136,21 @@ var AboutNetErrorListener = {
     }
 
     if (!msg2) {
       // We couldn't get an error message. Use the error string.
       // Note that this is different from before where we used PR_ErrorToString.
       msg2 = nss_error_id_str;
     }
     let msg = gPipNSSBundle.formatStringFromName("SSLConnectionErrorPrefix2",
-                                                 [hostString, msg2], 2);
+                                                 [hostString, msg2]);
 
     if (nss_error_id_str) {
       msg += gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
-                                                [nss_error_id_str], 1);
+                                                [nss_error_id_str]);
     }
     return msg;
   },
 
   handleEvent(aEvent) {
     if (!this.isNetErrorSite) {
       return;
     }
@@ -178,17 +178,17 @@ var AboutCertErrorListener = {
   },
 
   get isCertErrorSite() {
     return content.document.documentURI.startsWith("about:certerror");
   },
 
   _setTechDetailsMsgPart1(hostString, securityInfo, technicalInfo, doc) {
     let msg = gPipNSSBundle.formatStringFromName("certErrorIntro",
-                                                 [hostString], 1);
+                                                 [hostString]);
     msg += "\n\n";
 
     if (securityInfo.isUntrusted && !securityInfo.serverCert.isSelfSigned) {
       switch (securityInfo.errorCode) {
         case SEC_ERROR_UNKNOWN_ISSUER:
           msg += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer") + "\n";
           msg += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer2") + "\n";
           msg += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer3") + "\n";
@@ -203,17 +203,17 @@ var AboutCertErrorListener = {
           msg += gPipNSSBundle.GetStringFromName("certErrorTrust_SignatureAlgorithmDisabled") + "\n";
           break;
         case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
           msg += gPipNSSBundle.GetStringFromName("certErrorTrust_ExpiredIssuer") + "\n";
           break;
         // This error code currently only exists for the Symantec distrust, we may need to adjust
         // it to fit other distrusts later.
         case MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED:
-          msg += gPipNSSBundle.formatStringFromName("certErrorTrust_Symantec", [hostString], 1) + "\n";
+          msg += gPipNSSBundle.formatStringFromName("certErrorTrust_Symantec", [hostString]) + "\n";
           break;
         case SEC_ERROR_UNTRUSTED_CERT:
         default:
           msg += gPipNSSBundle.GetStringFromName("certErrorTrust_Untrusted") + "\n";
       }
     }
     if (securityInfo.isUntrusted && securityInfo.serverCert.isSelfSigned) {
       msg += gPipNSSBundle.GetStringFromName("certErrorTrust_SelfSigned") + "\n";
@@ -245,17 +245,17 @@ var AboutCertErrorListener = {
     // in Firefox 63, so we add copy explaining that to the user.
     // In case of future distrusts of that scale we might need to add
     // additional parameters that allow us to identify the affected party
     // without replicating the complex logic from certverifier code.
     if (securityInfo.errorCode == MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED) {
       let introContent = doc.getElementById("introContent");
       let description = doc.createElement("p");
       description.textContent = gPipNSSBundle.formatStringFromName(
-        "certErrorSymantecDistrustDescription", [hostString], 1);
+        "certErrorSymantecDistrustDescription", [hostString]);
       introContent.append(description);
 
       // The regular "what should I do" message does not make sense in this case.
       doc.getElementById("whatShouldIDoContentText").textContent =
         gPipNSSBundle.GetStringFromName("certErrorSymantecDistrustAdministrator");
     }
 
     this._setTechDetailsMsgPart1(hostString, securityInfo, technicalInfo, doc);
@@ -333,46 +333,46 @@ var AboutCertErrorListener = {
             if (i != (numSubjectAltNames - 1)) {
               msg += ", ";
             }
           }
           technicalInfo.append(msg + "\n");
         }
       } else {
         let msg = gPipNSSBundle.formatStringFromName("certErrorMismatch",
-                                                    [hostString], 1);
+                                                    [hostString]);
         technicalInfo.append(msg + "\n");
       }
     }
 
     if (securityInfo.isNotValidAtThisTime) {
       let nowTime = new Date().getTime() * 1000;
       let dateOptions = { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" };
       let now = new Services.intl.DateTimeFormat(undefined, dateOptions).format(new Date());
       let msg = "";
       if (validity.notBefore) {
         if (nowTime > validity.notAfter) {
           msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
-                                                   [validity.notAfterLocalTime, now], 2) + "\n";
+                                                   [validity.notAfterLocalTime, now]) + "\n";
         } else {
           msg += gPipNSSBundle.formatStringFromName("certErrorNotYetValidNow",
-                                                   [validity.notBeforeLocalTime, now], 2) + "\n";
+                                                   [validity.notBeforeLocalTime, now]) + "\n";
         }
       } else {
         // If something goes wrong, we assume the cert expired.
         msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
-                                                 ["", now], 2) + "\n";
+                                                 ["", now]) + "\n";
       }
       technicalInfo.append(msg);
     }
     technicalInfo.append("\n");
 
     // Add link to certificate and error message.
     let errorCodeMsg = gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
-                                                          [securityInfo.errorCodeString], 1);
+                                                          [securityInfo.errorCodeString]);
     technicalInfo.append(errorCodeMsg);
   },
 
   handleEvent(aEvent) {
     if (!this.isCertErrorSite) {
       return;
     }
 
--- a/mobile/android/components/ContentPermissionPrompt.js
+++ b/mobile/android/components/ContentPermissionPrompt.js
@@ -128,17 +128,17 @@ ContentPermissionPrompt.prototype = {
         callback(/* allow */ true);
       },
       positive: true,
     }];
 
     let chromeWin = this.getChromeForRequest(request);
     let requestor = (chromeWin.BrowserApp && chromeWin.BrowserApp.manifest) ?
         "'" + chromeWin.BrowserApp.manifest.name + "'" : request.principal.URI.host;
-    let message = browserBundle.formatStringFromName(entityName + ".ask", [requestor], 1);
+    let message = browserBundle.formatStringFromName(entityName + ".ask", [requestor]);
     // desktopNotification doesn't have a checkbox
     let options;
     if (entityName == "desktopNotification2") {
       options = {
         link: {
           label: browserBundle.GetStringFromName("doorhanger.learnMore"),
           url: "https://www.mozilla.org/firefox/push/",
         },
--- a/mobile/android/components/LoginManagerPrompter.js
+++ b/mobile/android/components/LoginManagerPrompter.js
@@ -316,17 +316,17 @@ LoginManagerPrompter.prototype = {
    *
    * Returns the localized string for the specified key,
    * formatted if required.
    *
    */
   _getLocalizedString: function(key, formatArgs) {
     if (formatArgs)
       return this._strBundle.pwmgr.formatStringFromName(
-        key, formatArgs, formatArgs.length);
+        key, formatArgs);
     return this._strBundle.pwmgr.GetStringFromName(key);
   },
 
   /*
    * _sanitizeUsername
    *
    * Sanitizes the specified username, by stripping quotes and truncating if
    * it's too long. This helps prevent an evil site from messing with the
--- a/mobile/android/components/NSSDialogService.js
+++ b/mobile/android/components/NSSDialogService.js
@@ -43,18 +43,17 @@ NSSDialogs.prototype = {
   },
 
   formatString: function(aName, argList) {
     if (!this.bundle) {
       this.bundle =
         Services.strings.createBundle("chrome://browser/locale/pippki.properties");
     }
     let escapedArgList = Array.from(argList, x => this.escapeHTML(x));
-    return this.bundle.formatStringFromName(aName, escapedArgList,
-                                            escapedArgList.length);
+    return this.bundle.formatStringFromName(aName, escapedArgList);
   },
 
   getPrompt: function(aTitle, aText, aButtons, aCtx) {
     let win = null;
     try {
       win = aCtx.getInterface(Ci.nsIDOMWindow);
     } catch (e) {
     }
--- a/mobile/android/components/PromptService.js
+++ b/mobile/android/components/PromptService.js
@@ -679,25 +679,25 @@ var PromptUtils = {
     if (realm.length > 150) {
       realm = realm.substring(0, 150);
       // Append "..." (or localized equivalent).
       realm += this.ellipsis;
     }
 
     let text;
     if (isProxy) {
-      text = this.bundle.formatStringFromName("EnterLoginForProxy3", [realm, displayHost], 2);
+      text = this.bundle.formatStringFromName("EnterLoginForProxy3", [realm, displayHost]);
     } else if (isPassOnly) {
-      text = this.bundle.formatStringFromName("EnterPasswordFor", [username, displayHost], 2);
+      text = this.bundle.formatStringFromName("EnterPasswordFor", [username, displayHost]);
     } else if (isCrossOrig) {
-      text = this.bundle.formatStringFromName("EnterUserPasswordForCrossOrigin2", [displayHost], 1);
+      text = this.bundle.formatStringFromName("EnterUserPasswordForCrossOrigin2", [displayHost]);
     } else if (!realm) {
-      text = this.bundle.formatStringFromName("EnterUserPasswordFor2", [displayHost], 1);
+      text = this.bundle.formatStringFromName("EnterUserPasswordFor2", [displayHost]);
     } else {
-      text = this.bundle.formatStringFromName("EnterLoginForRealm3", [realm, displayHost], 2);
+      text = this.bundle.formatStringFromName("EnterLoginForRealm3", [realm, displayHost]);
     }
 
     return text;
   },
 
   // JS port of http://mxr.mozilla.org/mozilla-central/source/toolkit/components/windowwatcher/nsPromptUtils.h#89
   getAuthHostPort: function pu_getAuthHostPort(aChannel, aAuthInfo) {
     let uri = aChannel.URI;
--- a/mobile/android/components/geckoview/GeckoViewPrompt.js
+++ b/mobile/android/components/geckoview/GeckoViewPrompt.js
@@ -736,25 +736,25 @@ PromptDelegate.prototype = {
     if (realm.length > 50) {
       realm = realm.substring(0, 50) + "\u2026";
     }
 
     let bundle = Services.strings.createBundle(
         "chrome://global/locale/commonDialogs.properties");
     let text;
     if (isProxy) {
-      text = bundle.formatStringFromName("EnterLoginForProxy3", [realm, displayHost], 2);
+      text = bundle.formatStringFromName("EnterLoginForProxy3", [realm, displayHost]);
     } else if (isPassOnly) {
-      text = bundle.formatStringFromName("EnterPasswordFor", [username, displayHost], 2);
+      text = bundle.formatStringFromName("EnterPasswordFor", [username, displayHost]);
     } else if (isCrossOrig) {
-      text = bundle.formatStringFromName("EnterUserPasswordForCrossOrigin2", [displayHost], 1);
+      text = bundle.formatStringFromName("EnterUserPasswordForCrossOrigin2", [displayHost]);
     } else if (!realm) {
-      text = bundle.formatStringFromName("EnterUserPasswordFor2", [displayHost], 1);
+      text = bundle.formatStringFromName("EnterUserPasswordFor2", [displayHost]);
     } else {
-      text = bundle.formatStringFromName("EnterLoginForRealm3", [realm, displayHost], 2);
+      text = bundle.formatStringFromName("EnterLoginForRealm3", [realm, displayHost]);
     }
 
     return text;
   },
 
   _getAuthTarget: function(aChannel, aAuthInfo) {
     // If our proxy is demanding authentication, don't use the
     // channel's actual destination.
--- a/mobile/android/modules/ActionBarHandler.jsm
+++ b/mobile/android/modules/ActionBarHandler.jsm
@@ -548,17 +548,17 @@ var ActionBarHandler = {
         ActionBarHandler._uninit();
         UITelemetry.addEvent("action.1", "actionbar", null, "call");
       },
     },
 
     SEARCH: {
       id: "search_action",
       label: () => Strings.browser.formatStringFromName("contextmenu.search",
-        [Services.search.defaultEngine.name], 1),
+        [Services.search.defaultEngine.name]),
       icon: "drawable://ab_search",
       order: 1,
       floatingOrder: 6,
 
       selector: {
         matches: function(element, win) {
           // Allow if selected text exists.
           return (ActionBarHandler._getSelectedText().length > 0);
--- a/mobile/android/modules/DownloadNotifications.jsm
+++ b/mobile/android/modules/DownloadNotifications.jsm
@@ -83,17 +83,17 @@ var DownloadNotifications = {
   },
 
   onDownloadChanged: function(download) {
     let notification = notifications.get(download);
 
     if (download.succeeded) {
       let file = new FileUtils.File(download.target.path);
 
-      Snackbars.show(strings.formatStringFromName("alertDownloadSucceeded", [file.leafName], 1), Snackbars.LENGTH_LONG, {
+      Snackbars.show(strings.formatStringFromName("alertDownloadSucceeded", [file.leafName]), Snackbars.LENGTH_LONG, {
         action: {
           label: strings.GetStringFromName("helperapps.open"),
           callback: () => {
             UITelemetry.addEvent("launch.1", "toast", null, "downloads");
             try {
               file.launch();
             } catch (ex) {
               this.showInAboutDownloads(download);
--- a/mobile/android/modules/FxAccountsWebChannel.jsm
+++ b/mobile/android/modules/FxAccountsWebChannel.jsm
@@ -231,17 +231,17 @@ this.FxAccountsWebChannel.prototype = {
                 // If we /don't have/ an Android Account, we warn if we're
                 // connecting to a new Account.  This is to minimize surprise;
                 // we never did this when changing accounts via the native UI.
                 let prevAcctHash = this._helpers.getPreviousAccountNameHashPref();
                 let shouldShowWarning = prevAcctHash && (prevAcctHash != this._helpers.sha256(data.email));
 
                 if (shouldShowWarning) {
                   log.w("Warning about creating a new Android Account: previously linked to different email address!");
-                  let message = strings.formatStringFromName("relinkVerify.message", [data.email], 1);
+                  let message = strings.formatStringFromName("relinkVerify.message", [data.email]);
                   new Prompt({
                     window: sendingContext.browser && sendingContext.browser.ownerGlobal,
                     title: strings.GetStringFromName("relinkVerify.title"),
                     message: message,
                     buttons: [
                       // This puts Cancel on the right.
                       strings.GetStringFromName("relinkVerify.cancel"),
                       strings.GetStringFromName("relinkVerify.continue"),
--- a/mobile/android/modules/WebrtcUI.jsm
+++ b/mobile/android/modules/WebrtcUI.jsm
@@ -228,17 +228,17 @@ var WebrtcUI = {
         if (res)
           return Strings.browser.GetStringFromName("getUserMedia." + aType + "." + res[1] + "Camera");
 
         if (device.name.startsWith("&") && device.name.endsWith(";"))
           return Strings.browser.GetStringFromName(device.name.substring(1, device.name.length - 1));
 
         if (device.name.trim() == "") {
           defaultCount++;
-          return Strings.browser.formatStringFromName("getUserMedia." + aType + ".default", [defaultCount], 1);
+          return Strings.browser.formatStringFromName("getUserMedia." + aType + ".default", [defaultCount]);
         }
         return device.name;
       }, this);
   },
 
   _addDevicesToOptions: function(aDevices, aType, aOptions) {
     if (aDevices.length) {
       // Filter out empty items from the list
@@ -314,17 +314,17 @@ var WebrtcUI = {
     else
       return;
 
     let chromeWin = this.getChromeWindow(aContentWindow);
     let principal = aContentWindow.document.nodePrincipal;
     let host = principal.URI.host;
     let requestor = (chromeWin.BrowserApp && chromeWin.BrowserApp.manifest) ?
           "'" + chromeWin.BrowserApp.manifest.name + "'" : host;
-    let message = Strings.browser.formatStringFromName("getUserMedia.share" + requestType + ".message", [ requestor ], 1);
+    let message = Strings.browser.formatStringFromName("getUserMedia.share" + requestType + ".message", [ requestor ]);
 
     let options = { inputs: [] };
     if (videoDevices.length > 1 || audioDevices.length > 0) {
       // videoSource is both the string used for l10n lookup and the object that will be returned
       this._addDevicesToOptions(videoDevices, "videoSource", options);
     }
 
     if (audioDevices.length > 1 || videoDevices.length > 0) {
--- a/mobile/android/modules/geckoview/GeckoViewConsole.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewConsole.jsm
@@ -61,31 +61,31 @@ var GeckoViewConsole = {
       Services.console.logMessage(consoleMsg);
     } else if (aMessage.level == "trace") {
       const bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
       const args = aMessage.arguments;
       const filename = this.abbreviateSourceURL(args[0].filename);
       const functionName = args[0].functionName || bundle.GetStringFromName("stacktrace.anonymousFunction");
       const lineNumber = args[0].lineNumber;
 
-      let body = bundle.formatStringFromName("stacktrace.outputMessage", [filename, functionName, lineNumber], 3);
+      let body = bundle.formatStringFromName("stacktrace.outputMessage", [filename, functionName, lineNumber]);
       body += "\n";
       args.forEach(function(aFrame) {
         const functionName = aFrame.functionName || bundle.GetStringFromName("stacktrace.anonymousFunction");
         body += "  " + aFrame.filename + " :: " + functionName + " :: " + aFrame.lineNumber + "\n";
       });
 
       Services.console.logStringMessage(body);
     } else if (aMessage.level == "time" && aMessage.arguments) {
       const bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
-      const body = bundle.formatStringFromName("timer.start", [aMessage.arguments.name], 1);
+      const body = bundle.formatStringFromName("timer.start", [aMessage.arguments.name]);
       Services.console.logStringMessage(body);
     } else if (aMessage.level == "timeEnd" && aMessage.arguments) {
       const bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
-      const body = bundle.formatStringFromName("timer.end", [aMessage.arguments.name, aMessage.arguments.duration], 2);
+      const body = bundle.formatStringFromName("timer.end", [aMessage.arguments.name, aMessage.arguments.duration]);
       Services.console.logStringMessage(body);
     } else if (["group", "groupCollapsed", "groupEnd"].includes(aMessage.level)) {
       // Do nothing yet
     } else {
       Services.console.logStringMessage(joinedArguments);
     }
   },
 
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2783,20 +2783,20 @@ nsresult NS_ShouldSecureUpgrade(
         nsAutoCString scheme;
         aURI->GetScheme(scheme);
         // append the additional 's' for security to the scheme :-)
         scheme.AppendLiteral("s");
         NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault());
         NS_ConvertUTF8toUTF16 reportScheme(scheme);
 
         if (aLoadInfo->GetUpgradeInsecureRequests()) {
-          const char16_t* params[] = {reportSpec.get(), reportScheme.get()};
+          AutoTArray<nsString, 2> params = {reportSpec, reportScheme};
           uint32_t innerWindowId = aLoadInfo->GetInnerWindowID();
           CSP_LogLocalizedStr(
-              "upgradeInsecureRequest", params, ArrayLength(params),
+              "upgradeInsecureRequest", params,
               EmptyString(),  // aSourceFile
               EmptyString(),  // aScriptSample
               0,              // aLineNumber
               0,              // aColumnNumber
               nsIScriptError::warningFlag,
               NS_LITERAL_CSTRING("upgradeInsecureRequest"), innerWindowId,
               !!aLoadInfo->GetOriginAttributes().mPrivateBrowsingId);
           Telemetry::AccumulateCategorical(
--- a/netwerk/protocol/gio/nsGIOProtocolHandler.cpp
+++ b/netwerk/protocol/gio/nsGIOProtocolHandler.cpp
@@ -770,28 +770,27 @@ static void mount_operation_ask_password
     g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED);
     return;
   }
   nsAutoString nsmessage;
 
   if (flags & G_ASK_PASSWORD_NEED_PASSWORD) {
     if (flags & G_ASK_PASSWORD_NEED_USERNAME) {
       if (!realm.IsEmpty()) {
-        const char16_t* strings[] = {realm.get(), dispHost.get()};
-        bundle->FormatStringFromName("EnterLoginForRealm3", strings, 2,
-                                     nsmessage);
+        AutoTArray<nsString, 2> strings = {realm, dispHost};
+        bundle->FormatStringFromName("EnterLoginForRealm3", strings, nsmessage);
       } else {
-        const char16_t* strings[] = {dispHost.get()};
-        bundle->FormatStringFromName("EnterUserPasswordFor2", strings, 1,
+        AutoTArray<nsString, 1> strings = {dispHost};
+        bundle->FormatStringFromName("EnterUserPasswordFor2", strings,
                                      nsmessage);
       }
     } else {
       NS_ConvertUTF8toUTF16 userName(default_user);
-      const char16_t* strings[] = {userName.get(), dispHost.get()};
-      bundle->FormatStringFromName("EnterPasswordFor", strings, 2, nsmessage);
+      AutoTArray<nsString, 2> strings = {userName, dispHost};
+      bundle->FormatStringFromName("EnterPasswordFor", strings, nsmessage);
     }
   } else {
     g_warning("Unknown mount operation request (flags: %x)", flags);
   }
 
   if (nsmessage.IsEmpty()) {
     g_mount_operation_reply(mount_op, G_MOUNT_OPERATION_ABORTED);
     return;
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
@@ -1490,20 +1490,20 @@ bool nsHttpChannelAuthProvider::ConfirmA
     }
     // It's possible cutPoint was 1 and is now 0. Only insert the ellipsis
     // if we're actually removing anything.
     if (cutPoint > 0) {
       ucsHost.Replace(0, cutPoint, nsContentUtils::GetLocalizedEllipsis());
     }
   }
 
-  const char16_t* strs[2] = {ucsHost.get(), ucsUser.get()};
+  AutoTArray<nsString, 2> strs = {ucsHost, ucsUser};
 
   nsAutoString msg;
-  rv = bundle->FormatStringFromName(bundleKey, strs, 2, msg);
+  rv = bundle->FormatStringFromName(bundleKey, strs, msg);
   if (NS_FAILED(rv)) return true;
 
   nsCOMPtr<nsIInterfaceRequestor> callbacks;
   rv = mAuthChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
   if (NS_FAILED(rv)) return true;
 
   nsCOMPtr<nsILoadGroup> loadGroup;
   rv = mAuthChannel->GetLoadGroup(getter_AddRefs(loadGroup));
--- a/netwerk/streamconv/converters/nsIndexedToHTML.cpp
+++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp
@@ -520,23 +520,21 @@ nsresult nsIndexedToHTML::DoOnStartReque
     nsAutoCString charset;
     encoding->Name(charset);
     rv = mTextToSubURI->UnEscapeAndConvert(charset, titleUri, unEscapeSpec);
   }
   if (NS_FAILED(rv)) return rv;
 
   nsCString htmlEscSpecUtf8;
   nsAppendEscapedHTML(NS_ConvertUTF16toUTF8(unEscapeSpec), htmlEscSpecUtf8);
-  NS_ConvertUTF8toUTF16 htmlEscSpec(htmlEscSpecUtf8);
+  AutoTArray<nsString, 1> formatTitle;
+  CopyUTF8toUTF16(htmlEscSpecUtf8, *formatTitle.AppendElement());
 
   nsAutoString title;
-  const char16_t* formatTitle[] = {htmlEscSpec.get()};
-
-  rv = mBundle->FormatStringFromName(
-      "DirTitle", formatTitle, sizeof(formatTitle) / sizeof(char16_t*), title);
+  rv = mBundle->FormatStringFromName("DirTitle", formatTitle, title);
   if (NS_FAILED(rv)) return rv;
 
   // we want to convert string bundle to NCR
   // to ensure they're shown in any charsets
   AppendNonAsciiToNCR(title, buffer);
 
   buffer.AppendLiteral("</title>\n");
 
@@ -566,24 +564,16 @@ nsresult nsIndexedToHTML::DoOnStartReque
   nsCString direction(NS_LITERAL_CSTRING("ltr"));
   if (LocaleService::GetInstance()->IsAppLocaleRTL()) {
     direction.AssignLiteral("rtl");
   }
 
   buffer.AppendLiteral("</head>\n<body dir=\"");
   buffer.Append(direction);
   buffer.AppendLiteral("\">\n<h1>");
-
-  const char16_t* formatHeading[] = {htmlEscSpec.get()};
-
-  rv = mBundle->FormatStringFromName("DirTitle", formatHeading,
-                                     sizeof(formatHeading) / sizeof(char16_t*),
-                                     title);
-  if (NS_FAILED(rv)) return rv;
-
   AppendNonAsciiToNCR(title, buffer);
   buffer.AppendLiteral("</h1>\n");
 
   if (!parentStr.IsEmpty()) {
     nsAutoString parentText;
     rv = mBundle->GetStringFromName("DirGoUp", parentText);
     if (NS_FAILED(rv)) return rv;
 
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -558,23 +558,20 @@ void PK11PasswordPromptRunnable::RunOnTa
     mResult = ShowProtectedAuthPrompt(mSlot, mIR);
     return;
   }
 
   nsAutoString promptString;
   if (PK11_IsInternal(mSlot)) {
     rv = GetPIPNSSBundleString("CertPassPromptDefault", promptString);
   } else {
-    NS_ConvertUTF8toUTF16 tokenName(PK11_GetTokenName(mSlot));
-    const char16_t* formatStrings[] = {
-        tokenName.get(),
-    };
-    rv =
-        PIPBundleFormatStringFromName("CertPassPrompt", formatStrings,
-                                      ArrayLength(formatStrings), promptString);
+    AutoTArray<nsString, 1> formatStrings = {
+        NS_ConvertUTF8toUTF16(PK11_GetTokenName(mSlot))};
+    rv = PIPBundleFormatStringFromName("CertPassPrompt", formatStrings,
+                                       promptString);
   }
   if (NS_FAILED(rv)) {
     return;
   }
 
   nsString password;
   // |checkState| is unused because |checkMsg| (the argument just before it) is
   // null, but XPConnect requires it to point to a valid bool nonetheless.
--- a/security/manager/ssl/nsNSSCertHelper.cpp
+++ b/security/manager/ssl/nsNSSCertHelper.cpp
@@ -65,31 +65,30 @@ nsresult GetPIPNSSBundleString(const cha
   if (NS_FAILED(rv)) {
     return rv;
   }
   result.Assign(NS_ConvertUTF16toUTF8(tmp));
   return NS_OK;
 }
 
 nsresult PIPBundleFormatStringFromName(const char* stringName,
-                                       const char16_t** params,
-                                       uint32_t numParams, nsAString& result) {
+                                       const nsTArray<nsString>& params,
+                                       nsAString& result) {
   MOZ_ASSERT(stringName);
-  MOZ_ASSERT(params);
-  if (!stringName || !params) {
+  MOZ_ASSERT(!params.IsEmpty());
+  if (!stringName || params.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
   }
   nsCOMPtr<nsIStringBundle> pipnssBundle;
   nsresult rv = GetPIPNSSBundle(getter_AddRefs(pipnssBundle));
   if (NS_FAILED(rv)) {
     return rv;
   }
   result.Truncate();
-  return pipnssBundle->FormatStringFromName(stringName, params, numParams,
-                                            result);
+  return pipnssBundle->FormatStringFromName(stringName, params, result);
 }
 
 static nsresult ProcessVersion(SECItem* versionItem,
                                nsIASN1PrintableItem** retItem) {
   nsAutoString text;
   GetPIPNSSBundleString("CertDumpVersion", text);
   nsCOMPtr<nsIASN1PrintableItem> printableItem = new nsNSSASN1PrintableItem();
   nsresult rv = printableItem->SetDisplayName(text);
@@ -107,21 +106,19 @@ static nsresult ProcessVersion(SECItem* 
     version = *BitwiseCast<uint8_t*, unsigned char*>(versionItem->data);
   } else {
     // If there is no version present in the cert, then RFC 5280 says we
     // default to v1 (0).
     version = 0;
   }
 
   // A value of n actually corresponds to version n + 1
-  nsAutoString versionString;
-  versionString.AppendInt(version + 1);
-  const char16_t* params[1] = {versionString.get()};
-  rv = PIPBundleFormatStringFromName("CertDumpVersionValue", params,
-                                     MOZ_ARRAY_LENGTH(params), text);
+  AutoTArray<nsString, 1> params;
+  params.AppendElement()->AppendInt(version + 1);
+  rv = PIPBundleFormatStringFromName("CertDumpVersionValue", params, text);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   rv = printableItem->SetDisplayValue(text);
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -545,22 +542,21 @@ static nsresult GetOIDText(SECItem* oid,
       break;
     default:
       break;
   }
 
   if (bundlekey) {
     rv = GetPIPNSSBundleString(bundlekey, text);
   } else {
-    nsAutoString text2;
-    rv = GetDefaultOIDFormat(oid, text2, ' ');
+    AutoTArray<nsString, 1> params;
+    rv = GetDefaultOIDFormat(oid, *params.AppendElement(), ' ');
     if (NS_FAILED(rv)) return rv;
 
-    const char16_t* params[1] = {text2.get()};
-    rv = PIPBundleFormatStringFromName("CertDumpDefOID", params, 1, text);
+    rv = PIPBundleFormatStringFromName("CertDumpDefOID", params, text);
   }
   return rv;
 }
 
 #define SEPARATOR "\n"
 
 static nsresult ProcessRawBytes(SECItem* data, nsAString& text,
                                 bool wantHeader = true) {
@@ -575,23 +571,22 @@ static nsresult ProcessRawBytes(SECItem*
     text.Append(value);
     text.AppendLiteral(SEPARATOR);
     return NS_OK;
   }
 
   // Else produce a hex dump.
 
   if (wantHeader) {
-    nsAutoString bytelen, bitlen;
-    bytelen.AppendInt(data->len);
-    bitlen.AppendInt(data->len * 8);
+    AutoTArray<nsString, 2> params;
+    params.AppendElement()->AppendInt(data->len);      // bytelen
+    params.AppendElement()->AppendInt(data->len * 8);  // bitlen
 
-    const char16_t* params[2] = {bytelen.get(), bitlen.get()};
-    nsresult rv = PIPBundleFormatStringFromName("CertDumpRawBytesHeader",
-                                                params, 2, text);
+    nsresult rv =
+        PIPBundleFormatStringFromName("CertDumpRawBytesHeader", params, text);
     if (NS_FAILED(rv)) return rv;
 
     text.AppendLiteral(SEPARATOR);
   }
 
   // This prints the value of the byte out into a
   // string that can later be displayed as a byte
   // string.  We place a new line after 24 bytes
@@ -687,18 +682,18 @@ static nsresult ProcessBasicConstraints(
   if (NS_FAILED(rv2)) return rv2;
   text.Append(local.get());
   if (value.pathLenConstraint != -1) {
     nsAutoString depth;
     if (value.pathLenConstraint == CERT_UNLIMITED_PATH_CONSTRAINT)
       GetPIPNSSBundleString("CertDumpPathLenUnlimited", depth);
     else
       depth.AppendInt(value.pathLenConstraint);
-    const char16_t* params[1] = {depth.get()};
-    rv2 = PIPBundleFormatStringFromName("CertDumpPathLen", params, 1, local);
+    AutoTArray<nsString, 1> params = {depth};
+    rv2 = PIPBundleFormatStringFromName("CertDumpPathLen", params, local);
     if (NS_FAILED(rv2)) return rv2;
     text.AppendLiteral(SEPARATOR);
     text.Append(local.get());
   }
   return NS_OK;
 }
 
 static nsresult ProcessExtKeyUsage(SECItem* extData, nsAString& text) {
@@ -779,21 +774,19 @@ static nsresult ProcessRDN(CERTRDN* rdn,
                                     (char*)decodeItem->data, decodeItem->len);
     if (SECSuccess != status) {
       return NS_ERROR_FAILURE;
     }
 
     nsAutoString avaValue;
     LossyUTF8ToUTF16(escapedValue.get(), strlen(escapedValue.get()), avaValue);
 
-    const char16_t* params[2];
-    params[0] = type.get();
-    params[1] = avaValue.get();
+    AutoTArray<nsString, 2> params = {type, avaValue};
     nsAutoString temp;
-    PIPBundleFormatStringFromName("AVATemplate", params, 2, temp);
+    PIPBundleFormatStringFromName("AVATemplate", params, temp);
     finalString += temp + NS_LITERAL_STRING("\n");
   }
   return NS_OK;
 }
 
 static nsresult ProcessName(CERTName* name, char16_t** value) {
   CERTRDN** rdns;
   CERTRDN** rdn;
@@ -1511,19 +1504,18 @@ static nsresult ProcessSubjectPublicKeyI
     switch (key->keyType) {
       case rsaKey: {
         displayed = true;
         nsAutoString length1, length2, data1, data2;
         length1.AppendInt(key->u.rsa.modulus.len * 8);
         length2.AppendInt(key->u.rsa.publicExponent.len * 8);
         ProcessRawBytes(&key->u.rsa.modulus, data1, false);
         ProcessRawBytes(&key->u.rsa.publicExponent, data2, false);
-        const char16_t* params[4] = {length1.get(), data1.get(), length2.get(),
-                                     data2.get()};
-        PIPBundleFormatStringFromName("CertDumpRSATemplate", params, 4, text);
+        AutoTArray<nsString, 4> params = {length1, data1, length2, data2};
+        PIPBundleFormatStringFromName("CertDumpRSATemplate", params, text);
         break;
       }
       case ecKey: {
         displayed = true;
         SECKEYECPublicKey& ecpk = key->u.ec;
         int fieldSizeLenAsBits =
             SECKEY_ECParamsToKeySize(&ecpk.DEREncodedParams);
         int basePointOrderLenAsBits =
@@ -1533,18 +1525,18 @@ static nsresult ProcessSubjectPublicKeyI
         s_bpol.AppendInt(basePointOrderLenAsBits);
 
         if (ecpk.publicValue.len > 4) {
           ProcessRawBytes(&ecpk.publicValue, s_pv, false);
         } else {
           int i_pv = DER_GetInteger(&ecpk.publicValue);
           s_pv.AppendInt(i_pv);
         }
-        const char16_t* params[] = {s_fsl.get(), s_bpol.get(), s_pv.get()};
-        PIPBundleFormatStringFromName("CertDumpECTemplate", params, 3, text);
+        AutoTArray<nsString, 3> params = {s_fsl, s_bpol, s_pv};
+        PIPBundleFormatStringFromName("CertDumpECTemplate", params, text);
         break;
       }
       default:
         /* Algorithm unknown, or too rarely used to bother displaying it */
         break;
     }
   }
   if (!displayed) {
--- a/security/manager/ssl/nsNSSCertHelper.h
+++ b/security/manager/ssl/nsNSSCertHelper.h
@@ -21,12 +21,12 @@ nsresult GetCertFingerprintByOidTag(CERT
 // If input is valid UTF-8, converts from UTF-8 to UTF-16. Otherwise,
 // converts from Latin1 to UTF-16.
 void LossyUTF8ToUTF16(const char* str, uint32_t len, /*out*/ nsAString& result);
 
 // Must be used on the main thread only.
 nsresult GetPIPNSSBundleString(const char* stringName, nsAString& result);
 nsresult GetPIPNSSBundleString(const char* stringName, nsACString& result);
 nsresult PIPBundleFormatStringFromName(const char* stringName,
-                                       const char16_t** params,
-                                       uint32_t numParams, nsAString& result);
+                                       const nsTArray<nsString>& params,
+                                       nsAString& result);
 
 #endif  // nsNSSCertHelper_h
--- a/services/fxaccounts/FxAccountsWebChannel.jsm
+++ b/services/fxaccounts/FxAccountsWebChannel.jsm
@@ -547,17 +547,17 @@ this.FxAccountsWebChannelHelpers.prototy
    *
    * @private
    */
   _promptForRelink(acctName) {
     let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
     let continueLabel = sb.GetStringFromName("continue.label");
     let title = sb.GetStringFromName("relinkVerify.title");
     let description = sb.formatStringFromName("relinkVerify.description",
-                                              [acctName], 1);
+                                              [acctName]);
     let body = sb.GetStringFromName("relinkVerify.heading") +
                "\n\n" + description;
     let ps = Services.prompt;
     let buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) +
                       (ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL) +
                       ps.BUTTON_POS_1_DEFAULT;
 
     // If running in context of the browser chrome, window does not exist.
--- a/services/fxaccounts/tests/browser/browser_device_connected.js
+++ b/services/fxaccounts/tests/browser/browser_device_connected.js
@@ -36,16 +36,16 @@ async function testDeviceConnected(devic
   Assert.ok("Tab successfully opened");
 
   Assert.equal(tab.linkedBrowser.currentURI.spec, DEVICES_URL);
 
   BrowserTestUtils.removeTab(tab);
 }
 
 add_task(async function() {
-  expectedBody = accountsBundle.formatStringFromName("deviceConnectedBody", ["My phone"], 1);
+  expectedBody = accountsBundle.formatStringFromName("deviceConnectedBody", ["My phone"]);
   await testDeviceConnected("My phone");
 });
 
 add_task(async function() {
   expectedBody = accountsBundle.GetStringFromName("deviceConnectedBody.noDeviceName");
   await testDeviceConnected(null);
 });
--- a/services/sync/modules/util.js
+++ b/services/sync/modules/util.js
@@ -652,17 +652,17 @@ var Utils = {
     let system =
       // 'device' is defined on unix systems
       Services.sysinfo.get("device") ||
       hostname ||
       // fall back on ua info string
       Cc["@mozilla.org/network/protocol;1?name=http"].getService(Ci.nsIHttpProtocolHandler).oscpu;
 
     let syncStrings = Services.strings.createBundle("chrome://weave/locale/sync.properties");
-    return syncStrings.formatStringFromName("client.name2", [user, brandName, system], 3);
+    return syncStrings.formatStringFromName("client.name2", [user, brandName, system]);
   },
 
   getDeviceName() {
     let deviceName = localDeviceName;
 
     if (deviceName === "") {
       deviceName = this.getDefaultDeviceName();
       Svc.Prefs.set("client.name", deviceName);
--- a/toolkit/components/alerts/resources/content/alert.js
+++ b/toolkit/components/alerts/resources/content/alert.js
@@ -59,28 +59,25 @@ function prefillAlertInfo() {
         const ALERT_BUNDLE = Services.strings.createBundle(
           "chrome://alerts/locale/alert.properties");
         const BRAND_BUNDLE = Services.strings.createBundle(
           "chrome://branding/locale/brand.properties");
         const BRAND_NAME = BRAND_BUNDLE.GetStringFromName("brandShortName");
         let label = document.getElementById("alertSourceLabel");
         label.setAttribute("value",
           ALERT_BUNDLE.formatStringFromName("source.label",
-                                            [hostPort],
-                                            1));
+                                            [hostPort]));
         let doNotDisturbMenuItem = document.getElementById("doNotDisturbMenuItem");
         doNotDisturbMenuItem.setAttribute("label",
           ALERT_BUNDLE.formatStringFromName("pauseNotifications.label",
-                                            [BRAND_NAME],
-                                            1));
+                                            [BRAND_NAME]));
         let disableForOrigin = document.getElementById("disableForOriginMenuItem");
         disableForOrigin.setAttribute("label",
           ALERT_BUNDLE.formatStringFromName("webActions.disableForOrigin.label",
-                                            [hostPort],
-                                            1));
+                                            [hostPort]));
         let openSettings = document.getElementById("openSettingsMenuItem");
         openSettings.setAttribute("label",
           ALERT_BUNDLE.GetStringFromName("webActions.settings.label"));
       }
     }
     case 11:
       gAlertListener = window.arguments[10];
     case 10:
--- a/toolkit/components/alerts/test/test_principal.html
+++ b/toolkit/components/alerts/test/test_principal.html
@@ -83,17 +83,17 @@ async function testNullPrincipal() {
 async function testNodePrincipal() {
   var principal = SpecialPowers.wrap(document).nodePrincipal;
   var source = await notify("nodePrincipal", principal);
 
   var stringBundle = Services.strings.createBundle(
     "chrome://alerts/locale/alert.properties"
   );
   var localizedSource = stringBundle.formatStringFromName(
-    "source.label", [principal.URI.hostPort], 1);
+    "source.label", [principal.URI.hostPort]);
   is(source, localizedSource, "Should include source for node principal");
 }
 
 function runTest() {
   if (!("@mozilla.org/alerts-service;1" in Cc)) {
     todo(false, "Alerts service does not exist in this application");
     return;
   }
--- a/toolkit/components/downloads/DownloadUIHelper.jsm
+++ b/toolkit/components/downloads/DownloadUIHelper.jsm
@@ -64,18 +64,17 @@ XPCOMUtils.defineLazyGetter(DownloadUIHe
   let strings = {};
   let sb = Services.strings.createBundle(kStringBundleUrl);
   for (let string of sb.getSimpleEnumeration()) {
     let stringName = string.key;
     if (stringName in kStringsRequiringFormatting) {
       strings[stringName] = function() {
         // Convert "arguments" to a real array before calling into XPCOM.
         return sb.formatStringFromName(stringName,
-                                       Array.from(arguments),
-                                       arguments.length);
+                                       Array.from(arguments));
       };
     } else {
       strings[stringName] = string.value;
     }
   }
   return strings;
 });
 
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -1187,17 +1187,17 @@ class ExtensionData {
     if (allUrls) {
       result.msgs.push(bundle.GetStringFromName("webextPerms.hostDescription.allUrls"));
     } else {
       // Formats a list of host permissions.  If we have 4 or fewer, display
       // them all, otherwise display the first 3 followed by an item that
       // says "...plus N others"
       let format = (list, itemKey, moreKey) => {
         function formatItems(items) {
-          result.msgs.push(...items.map(item => bundle.formatStringFromName(itemKey, [item], 1)));
+          result.msgs.push(...items.map(item => bundle.formatStringFromName(itemKey, [item])));
         }
         if (list.length < 5) {
           formatItems(list);
         } else {
           formatItems(list.slice(0, 3));
 
           let remaining = list.length - 3;
           result.msgs.push(PluralForm.get(remaining, bundle.GetStringFromName(moreKey))
@@ -1211,17 +1211,17 @@ class ExtensionData {
              "webextPerms.hostDescription.tooManySites");
     }
 
     let permissionKey = perm => `webextPerms.description.${perm}`;
 
     // Next, show the native messaging permission if it is present.
     const NATIVE_MSG_PERM = "nativeMessaging";
     if (perms.permissions.includes(NATIVE_MSG_PERM)) {
-      result.msgs.push(bundle.formatStringFromName(permissionKey(NATIVE_MSG_PERM), [info.appName], 1));
+      result.msgs.push(bundle.formatStringFromName(permissionKey(NATIVE_MSG_PERM), [info.appName]));
     }
 
     // Finally, show remaining permissions, in the same order as AMO.
     // The permissions are sorted alphabetically by the permission
     // string to match AMO.
     let permissionsCopy = perms.permissions.slice(0);
     for (let permission of permissionsCopy.sort()) {
       // Handled above
@@ -1233,48 +1233,48 @@ class ExtensionData {
       } catch (err) {
         // We deliberately do not include all permissions in the prompt.
         // So if we don't find one then just skip it.
       }
     }
 
     const haveAccessKeys = (AppConstants.platform !== "android");
 
-    result.header = bundle.formatStringFromName("webextPerms.header", ["<>"], 1);
+    result.header = bundle.formatStringFromName("webextPerms.header", ["<>"]);
     result.text = info.unsigned ?
                   bundle.GetStringFromName("webextPerms.unsignedWarning") : "";
     result.listIntro = bundle.GetStringFromName("webextPerms.listIntro");
 
     result.acceptText = bundle.GetStringFromName("webextPerms.add.label");
     result.cancelText = bundle.GetStringFromName("webextPerms.cancel.label");
     if (haveAccessKeys) {
       result.acceptKey = bundle.GetStringFromName("webextPerms.add.accessKey");
       result.cancelKey = bundle.GetStringFromName("webextPerms.cancel.accessKey");
     }
 
     if (info.type == "sideload") {
-      result.header = bundle.formatStringFromName("webextPerms.sideloadHeader", ["<>"], 1);
+      result.header = bundle.formatStringFromName("webextPerms.sideloadHeader", ["<>"]);
       let key = result.msgs.length == 0 ?
                 "webextPerms.sideloadTextNoPerms" : "webextPerms.sideloadText2";
       result.text = bundle.GetStringFromName(key);
       result.acceptText = bundle.GetStringFromName("webextPerms.sideloadEnable.label");
       result.cancelText = bundle.GetStringFromName("webextPerms.sideloadCancel.label");
       if (haveAccessKeys) {
         result.acceptKey = bundle.GetStringFromName("webextPerms.sideloadEnable.accessKey");
         result.cancelKey = bundle.GetStringFromName("webextPerms.sideloadCancel.accessKey");
       }
     } else if (info.type == "update") {
-      result.header = bundle.formatStringFromName("webextPerms.updateText", ["<>"], 1);
+      result.header = bundle.formatStringFromName("webextPerms.updateText", ["<>"]);
       result.text = "";
       result.acceptText = bundle.GetStringFromName("webextPerms.updateAccept.label");
       if (haveAccessKeys) {
         result.acceptKey = bundle.GetStringFromName("webextPerms.updateAccept.accessKey");
       }
     } else if (info.type == "optional") {
-      result.header = bundle.formatStringFromName("webextPerms.optionalPermsHeader", ["<>"], 1);
+      result.header = bundle.formatStringFromName("webextPerms.optionalPermsHeader", ["<>"]);
       result.text = "";
       result.listIntro = bundle.GetStringFromName("webextPerms.optionalPermsListIntro");
       result.acceptText = bundle.GetStringFromName("webextPerms.optionalPermsAllow.label");
       result.cancelText = bundle.GetStringFromName("webextPerms.optionalPermsDeny.label");
       if (haveAccessKeys) {
         result.acceptKey = bundle.GetStringFromName("webextPerms.optionalPermsAllow.accessKey");
         result.cancelKey = bundle.GetStringFromName("webextPerms.optionalPermsDeny.accessKey");
       }
--- a/toolkit/components/extensions/parent/ext-management.js
+++ b/toolkit/components/extensions/parent/ext-management.js
@@ -17,17 +17,17 @@ XPCOMUtils.defineLazyGetter(this, "Globa
 });
 
 var {
   ExtensionError,
 } = ExtensionUtils;
 
 const _ = (key, ...args) => {
   if (args.length) {
-    return strBundle.formatStringFromName(key, args, args.length);
+    return strBundle.formatStringFromName(key, args);
   }
   return strBundle.GetStringFromName(key);
 };
 
 const installType = addon => {
   if (addon.temporarilyInstalled) {
     return "development";
   } else if (addon.foreignInstall) {
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js
@@ -127,37 +127,37 @@ add_task(async function host_permissions
   }, {
     description: "A few host permissions",
     manifest: {
       permissions: ["http://a/", "http://*.b/", "http://c/*"],
     },
     expectedOrigins: ["http://a/", "http://*.b/", "http://c/*"],
     expectedWarnings: [
       // Wildcard hosts take precedence in the permission list.
-      bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["b"], 1),
-      bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["a"], 1),
-      bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["c"], 1),
+      bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["b"]),
+      bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["a"]),
+      bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["c"]),
     ],
   }, {
     description: "many host permission",
     manifest: {
       permissions: ["http://a/", "http://b/", "http://c/", "http://d/", "http://e/*",
                     "http://*.1/", "http://*.2/", "http://*.3/", "http://*.4/"],
     },
     expectedOrigins: ["http://a/", "http://b/", "http://c/", "http://d/", "http://e/*",
                       "http://*.1/", "http://*.2/", "http://*.3/", "http://*.4/"],
     expectedWarnings: [
       // Wildcard hosts take precedence in the permission list.
-      bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["1"], 1),
-      bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["2"], 1),
-      bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["3"], 1),
-      bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["4"], 1),
-      bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["a"], 1),
-      bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["b"], 1),
-      bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["c"], 1),
+      bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["1"]),
+      bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["2"]),
+      bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["3"]),
+      bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["4"]),
+      bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["a"]),
+      bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["b"]),
+      bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["c"]),
       PluralForm.get(2, bundle.GetStringFromName("webextPerms.hostDescription.tooManySites")).replace("#1", "2"),
     ],
   }];
   for (let {description, manifest, expectedOrigins, expectedWarnings} of permissionTestCases) {
     let manifestPermissions = await getManifestPermissions({
       manifest,
     });
 
@@ -182,21 +182,21 @@ add_task(async function api_permissions(
   });
   deepEqual(manifestPermissions, {
     origins: ["http://x/", "http://*.x/", "http://*.tld/"],
     permissions: ["activeTab", "webNavigation", "tabs", "nativeMessaging"],
   }, "Expected origins and permissions");
 
   deepEqual(getPermissionWarnings(manifestPermissions), [
     // Host permissions first, with wildcards on top.
-    bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["x"], 1),
-    bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["tld"], 1),
-    bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["x"], 1),
+    bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["x"]),
+    bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["tld"]),
+    bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["x"]),
     // nativeMessaging permission warning first of all permissions.
-    bundle.formatStringFromName("webextPerms.description.nativeMessaging", [DUMMY_APP_NAME], 1),
+    bundle.formatStringFromName("webextPerms.description.nativeMessaging", [DUMMY_APP_NAME]),
     // Other permissions in alphabetical order.
     // Note: activeTab has no permission warning string.
     bundle.GetStringFromName("webextPerms.description.tabs"),
     bundle.GetStringFromName("webextPerms.description.webNavigation"),
   ], "Expected warnings");
 });
 
 // Tests that the expected permission warnings are generated for a mix of host
@@ -228,17 +228,17 @@ add_task(async function unprivileged_wit
     },
   });
   deepEqual(manifestPermissions, {
     origins: ["http://a/"],
     permissions: [],
   }, "Expected origins and permissions for unprivileged add-on with mozillaAddons");
 
   deepEqual(getPermissionWarnings(manifestPermissions), [
-    bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["a"], 1),
+    bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["a"]),
   ], "Expected warnings for unprivileged add-on with mozillaAddons permission.");
 });
 
 // Tests that an update with less permissions has no warning.
 add_task(async function update_drop_permission() {
   let warnings = await getPermissionWarningsForUpdate({
     manifest: {
       permissions: ["<all_urls>", "https://a/", "http://b/"],
@@ -287,18 +287,18 @@ add_task(async function update_change_pe
         // (no new warning) file:-scheme, but host "f" is same as "http://f/".
         "file://f/",
         // (expect warning) New permission was added.
         "proxy",
       ],
     },
   });
   deepEqual(warnings, [
-    bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["c"], 1),
-    bundle.formatStringFromName("webextPerms.description.proxy", [DUMMY_APP_NAME], 1),
+    bundle.formatStringFromName("webextPerms.hostDescription.wildcard", ["c"]),
+    bundle.formatStringFromName("webextPerms.description.proxy", [DUMMY_APP_NAME]),
   ], "Expected permission warnings for new permissions only");
 });
 
 // Tests that a privileged extension with the mozillaAddons permission can be
 // updated without errors.
 add_task(async function update_privileged_with_mozillaAddons() {
   let warnings = await getPermissionWarningsForUpdate({
     isPrivileged: true,
@@ -307,17 +307,17 @@ add_task(async function update_privilege
     },
   }, {
     isPrivileged: true,
     manifest: {
       permissions: ["mozillaAddons", "resource://a/", "resource://b/"],
     },
   });
   deepEqual(warnings, [
-    bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["b"], 1),
+    bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["b"]),
   ], "Expected permission warnings for new host only");
 });
 
 // Tests that an unprivileged extension cannot get privileged permissions
 // through an update.
 add_task(async function update_unprivileged_with_mozillaAddons() {
   // Unprivileged
   let warnings = await getPermissionWarningsForUpdate({
--- a/toolkit/components/narrate/NarrateControls.jsm
+++ b/toolkit/components/narrate/NarrateControls.jsm
@@ -264,23 +264,23 @@ NarrateControls.prototype = {
       case "WINNT":
         // On windows the language is included in the name, so just use the name
         return voice.name;
       case "Linux":
         // On Linux, the name is usually the unlocalized language name.
         // Use a localized language name, and have the language tag in
         // parenthisis. This is to avoid six languages called "English".
         return gStrings.formatStringFromName("voiceLabel",
-          [this._getLanguageName(voice.lang) || voice.name, voice.lang], 2);
+          [this._getLanguageName(voice.lang) || voice.name, voice.lang]);
       default:
         // On Mac the language is not included in the name, find a localized
         // language name or show the tag if none exists.
         // This is the ideal naming scheme so it is also the "default".
         return gStrings.formatStringFromName("voiceLabel",
-          [voice.name, this._getLanguageName(voice.lang) || voice.lang], 2);
+          [voice.name, this._getLanguageName(voice.lang) || voice.lang]);
     }
   },
 
   _getLanguageName(lang) {
     try {
       // This may throw if the lang doesn't match.
       // XXX: Replace with Intl.Locale once bug 1433303 lands.
       let langCode = lang.match(/^[a-z]{2,3}/)[0];
--- a/toolkit/components/normandy/content/ShieldFrameChild.jsm
+++ b/toolkit/components/normandy/content/ShieldFrameChild.jsm
@@ -74,17 +74,17 @@ class ShieldFrameChild extends ActorChil
         );
         break;
       case "GetRemoteValue:ShieldTranslations":
         const strings = {};
         for (let str of gStringBundle.getSimpleEnumeration()) {
           strings[str.key] = str.value;
         }
         const brandName = gBrandBundle.GetStringFromName("brandShortName");
-        strings.enabledList = gStringBundle.formatStringFromName("enabledList", [brandName], 1);
+        strings.enabledList = gStringBundle.formatStringFromName("enabledList", [brandName]);
 
         this.triggerPageCallback(
           "ReceiveRemoteValue:ShieldTranslations",
            strings
         );
         break;
     }
   }
--- a/toolkit/components/passwordmgr/LoginAutoCompleteResult.jsm
+++ b/toolkit/components/passwordmgr/LoginAutoCompleteResult.jsm
@@ -73,17 +73,17 @@ function findDuplicates(loginList) {
     }
     seen.add(login.username);
   }
   return duplicates;
 }
 
 function getLocalizedString(key, formatArgs = null) {
   if (formatArgs) {
-    return passwordMgrBundle.formatStringFromName(key, formatArgs, formatArgs.length);
+    return passwordMgrBundle.formatStringFromName(key, formatArgs);
   }
   return passwordMgrBundle.GetStringFromName(key);
 }
 
 
 class AutocompleteItem {
   constructor(style) {
     this.comment = "";
--- a/toolkit/components/passwordmgr/LoginManagerContextMenu.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContextMenu.jsm
@@ -173,17 +173,17 @@ this.LoginManagerContextMenu = {
    * @param {string[]} formatArgs
    *        An array of formatting argument string
    *
    * @returns {string} the localized string for the specified key,
    *          formatted with arguments if required.
    */
   _getLocalizedString(key, formatArgs) {
     if (formatArgs) {
-      return this._stringBundle.formatStringFromName(key, formatArgs, formatArgs.length);
+      return this._stringBundle.formatStringFromName(key, formatArgs);
     }
     return this._stringBundle.GetStringFromName(key);
   },
 };
 
 XPCOMUtils.defineLazyGetter(LoginManagerContextMenu, "_stringBundle", function() {
   return Services.strings.
          createBundle("chrome://passwordmgr/locale/passwordmgr.properties");
--- a/toolkit/components/passwordmgr/LoginManagerPrompter.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerPrompter.jsm
@@ -1401,18 +1401,17 @@ LoginManagerPrompter.prototype = {
    *   (etc)
    *
    * Returns the localized string for the specified key,
    * formatted if required.
    *
    */
   _getLocalizedString(key, formatArgs) {
     if (formatArgs) {
-      return this._strBundle.formatStringFromName(
-        key, formatArgs, formatArgs.length);
+      return this._strBundle.formatStringFromName(key, formatArgs);
     }
     return this._strBundle.GetStringFromName(key);
   },
 
 
   /**
    * Sanitizes the specified username, by stripping quotes and truncating if
    * it's too long. This helps prevent an evil site from messing with the
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -530,17 +530,17 @@ var PlacesUtils = {
   toISupportsString: function PU_toISupportsString(aString) {
     let s = Cc["@mozilla.org/supports-string;1"].
             createInstance(Ci.nsISupportsString);
     s.data = aString;
     return s;
   },
 
   getFormattedString: function PU_getFormattedString(key, params) {
-    return bundle.formatStringFromName(key, params, params.length);
+    return bundle.formatStringFromName(key, params);
   },
 
   getString: function PU_getString(key) {
     return bundle.GetStringFromName(key);
   },
 
   /**
    * Makes a moz-action URI for the given action and set of parameters.
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -3283,21 +3283,20 @@ void nsNavHistory::SendPageChangedNotifi
   NOTIFY_OBSERVERS(mCanNotify, mObservers, nsINavHistoryObserver,
                    OnPageChanged(aURI, aChangedAttribute, aNewValue, aGUID));
 }
 
 void nsNavHistory::GetAgeInDaysString(int32_t aInt, const char* aName,
                                       nsACString& aResult) {
   nsIStringBundle* bundle = GetBundle();
   if (bundle) {
-    nsAutoString intString;
-    intString.AppendInt(aInt);
-    const char16_t* strings[1] = {intString.get()};
+    AutoTArray<nsString, 1> strings;
+    strings.AppendElement()->AppendInt(aInt);
     nsAutoString value;
-    nsresult rv = bundle->FormatStringFromName(aName, strings, 1, value);
+    nsresult rv = bundle->FormatStringFromName(aName, strings, value);
     if (NS_SUCCEEDED(rv)) {
       CopyUTF16toUTF8(value, aResult);
       return;
     }
   }
   aResult.Assign(aName);
 }
 
--- a/toolkit/components/processsingleton/MainProcessSingleton.jsm
+++ b/toolkit/components/processsingleton/MainProcessSingleton.jsm
@@ -38,17 +38,17 @@ MainProcessSingleton.prototype = {
     } catch (ex) {
       Cu.reportError("Invalid argument passed to window.external.AddSearchProvider: " + ex);
 
       var searchBundle = Services.strings.createBundle("chrome://global/locale/search/search.properties");
       var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
       var brandName = brandBundle.GetStringFromName("brandShortName");
       var title = searchBundle.GetStringFromName("error_invalid_format_title");
       var msg = searchBundle.formatStringFromName("error_invalid_engine_msg2",
-                                                  [brandName, engineURL.spec], 2);
+                                                  [brandName, engineURL.spec]);
       Services.ww.getNewPrompter(browser.ownerGlobal).alert(title, msg);
       return;
     }
 
     Services.search.addEngine(engineURL.spec, iconURL ? iconURL.spec : null, true)
       .catch(ex => Cu.reportError("Unable to add search engine to the search service: " + ex));
   },
 
--- a/toolkit/components/prompts/src/Prompter.jsm
+++ b/toolkit/components/prompts/src/Prompter.jsm
@@ -110,17 +110,17 @@ Prompter.prototype = {
 
 
 // Common utils not specific to a particular prompter style.
 var PromptUtilsTemp = {
     __proto__: PromptUtils,
 
     getLocalizedString(key, formatArgs) {
         if (formatArgs)
-            return this.strBundle.formatStringFromName(key, formatArgs, formatArgs.length);
+            return this.strBundle.formatStringFromName(key, formatArgs);
         return this.strBundle.GetStringFromName(key);
     },
 
     confirmExHelper(flags, button0, button1, button2) {
         const BUTTON_DEFAULT_MASK = 0x03000000;
         let defaultButtonNum = (flags & BUTTON_DEFAULT_MASK) >> 24;
         let isDelayEnabled = (flags & Ci.nsIPrompt.BUTTON_DELAY_ENABLE);
 
--- a/toolkit/components/resistfingerprinting/RFPHelper.jsm
+++ b/toolkit/components/resistfingerprinting/RFPHelper.jsm
@@ -242,17 +242,17 @@ class _RFPHelper {
     // Display two buttons, both with string titles.
     let flags = Services.prompt.STD_YES_NO_BUTTONS;
     let brandBundle = Services.strings.createBundle(
       "chrome://branding/locale/brand.properties");
     let brandShortName = brandBundle.GetStringFromName("brandShortName");
     let navigatorBundle = Services.strings.createBundle(
       "chrome://browser/locale/browser.properties");
     let message = navigatorBundle.formatStringFromName(
-      "privacy.spoof_english", [brandShortName], 1);
+      "privacy.spoof_english", [brandShortName]);
     let response = Services.prompt.confirmEx(
       null, "", message, flags, null, null, null, null, {value: false});
 
     // Update preferences to reflect their response and to prevent the prompt
     // from being displayed again.
     Services.prefs.setIntPref(kPrefSpoofEnglish, (response == 0) ? 2 : 1);
   }
 
--- a/toolkit/components/search/SearchEngine.jsm
+++ b/toolkit/components/search/SearchEngine.jsm
@@ -848,17 +848,17 @@ SearchEngine.prototype = {
 
   _confirmAddEngine() {
     var stringBundle = Services.strings.createBundle(SEARCH_BUNDLE);
     var titleMessage = stringBundle.GetStringFromName("addEngineConfirmTitle");
 
     // Display only the hostname portion of the URL.
     var dialogMessage =
         stringBundle.formatStringFromName("addEngineConfirmation",
-                                          [this._name, this._uri.host], 2);
+                                          [this._name, this._uri.host]);
     var checkboxMessage = null;
     if (!Services.prefs.getBoolPref(SearchUtils.BROWSER_SEARCH_PREF + "noCurrentEngine", false))
       checkboxMessage = stringBundle.GetStringFromName("addEngineAsCurrentText");
 
     var addButtonLabel =
         stringBundle.GetStringFromName("addEngineAddButtonLabel");
 
     var ps = Services.prompt;
@@ -914,18 +914,17 @@ SearchEngine.prototype = {
       var brandBundle = Services.strings.createBundle(BRAND_BUNDLE);
       var brandName = brandBundle.GetStringFromName("brandShortName");
 
       var searchBundle = Services.strings.createBundle(SEARCH_BUNDLE);
       var msgStringName = strings.error || "error_loading_engine_msg2";
       var titleStringName = strings.title || "error_loading_engine_title";
       var title = searchBundle.GetStringFromName(titleStringName);
       var text = searchBundle.formatStringFromName(msgStringName,
-                                                   [brandName, engine._location],
-                                                   2);
+                                                   [brandName, engine._location]);
 
       Services.ww.getNewPrompter(null).alert(title, text);
     }
 
     if (!bytes) {
       promptError();
       return;
     }
--- a/toolkit/content/aboutTelemetry.js
+++ b/toolkit/content/aboutTelemetry.js
@@ -905,17 +905,17 @@ var StackRenderer = {
    */
   renderHeader: function StackRenderer_renderHeader(aPrefix, aFormatArgs) {
     let div = document.getElementById(aPrefix);
 
     let titleElement = document.createElement("span");
     titleElement.className = "stack-title";
 
     let titleText = bundle.formatStringFromName(
-      aPrefix + "-title", aFormatArgs, aFormatArgs.length);
+      aPrefix + "-title", aFormatArgs);
     titleElement.appendChild(document.createTextNode(titleText));
 
     div.appendChild(titleElement);
     div.appendChild(document.createElement("br"));
   },
 };
 
 var RawPayloadData = {
--- a/toolkit/content/aboutwebrtc/aboutWebrtc.js
+++ b/toolkit/content/aboutwebrtc/aboutWebrtc.js
@@ -179,17 +179,17 @@ SavePage.prototype.onClick = function() 
 
       fout.write(content.outerHTML, content.outerHTML.length);
       FileUtils.closeAtomicFileOutputStream(fout);
 
       for (let node of noPrintList) {
         node.style.removeProperty("display");
       }
 
-      this._message = formatString("save_page_msg", [FilePicker.file.path], 1);
+      this._message = formatString("save_page_msg", [FilePicker.file.path]);
       this.update();
     }
   });
 };
 
 function DebugMode() {
   Control.call(this);
   this._messageHeader = getString("debug_mode_msg_label");
@@ -204,27 +204,27 @@ function DebugMode() {
 
 DebugMode.prototype = Object.create(Control.prototype);
 DebugMode.prototype.constructor = DebugMode;
 
 DebugMode.prototype.onState = function() {
   this._label = getString("debug_mode_on_state_label");
   try {
     let file = Services.prefs.getCharPref("media.webrtc.debug.log_file");
-    this._message = formatString("debug_mode_on_state_msg", [file], 1);
+    this._message = formatString("debug_mode_on_state_msg", [file]);
   } catch (e) {
     this._message = null;
   }
 };
 
 DebugMode.prototype.offState = function() {
   this._label = getString("debug_mode_off_state_label");
   try {
     let file = Services.prefs.getCharPref("media.webrtc.debug.log_file");
-    this._message = formatString("debug_mode_off_state_msg", [file], 1);
+    this._message = formatString("debug_mode_off_state_msg", [file]);
   } catch (e) {
     this._message = null;
   }
 };
 
 DebugMode.prototype.onClick = function() {
   if (WebrtcGlobalInformation.debugLevel > 0) {
     WebrtcGlobalInformation.debugLevel = 0;
@@ -251,17 +251,17 @@ function AecLogging() {
 
 AecLogging.prototype = Object.create(Control.prototype);
 AecLogging.prototype.constructor = AecLogging;
 
 AecLogging.prototype.offState = function() {
   this._label = getString("aec_logging_off_state_label");
   try {
     let file = WebrtcGlobalInformation.aecDebugLogDir;
-    this._message = formatString("aec_logging_off_state_msg", [file], 1);
+    this._message = formatString("aec_logging_off_state_msg", [file]);
   } catch (e) {
     this._message = null;
   }
 };
 
 AecLogging.prototype.onState = function() {
   this._label = getString("aec_logging_on_state_label");
   try {
--- a/toolkit/content/contentAreaUtils.js
+++ b/toolkit/content/contentAreaUtils.js
@@ -510,17 +510,17 @@ function internalPersist(persistArgs) {
     var filesFolder = null;
     if (persistArgs.targetContentType != "text/plain") {
       // Create the local directory into which to save associated files.
       filesFolder = persistArgs.targetFile.clone();
 
       var nameWithoutExtension = getFileBaseName(filesFolder.leafName);
       var filesFolderLeafName =
         ContentAreaUtils.stringBundle
-                        .formatStringFromName("filesFolder", [nameWithoutExtension], 1);
+                        .formatStringFromName("filesFolder", [nameWithoutExtension]);
 
       filesFolder.leafName = filesFolderLeafName;
     }
 
     var encodingFlags = 0;
     if (persistArgs.targetContentType == "text/plain") {
       encodingFlags |= nsIWBP.ENCODE_FLAGS_FORMATTED;
       encodingFlags |= nsIWBP.ENCODE_FLAGS_ABSOLUTE_LINKS;
--- a/toolkit/content/widgets/autocomplete-richlistitem.js
+++ b/toolkit/content/widgets/autocomplete-richlistitem.js
@@ -570,17 +570,17 @@ MozElements.MozAutocompleteRichlistitem 
               engineName = override;
               action.params.engineName = override;
               let newURL =
                 PlacesUtils.mozActionURI(action.type, action.params);
               this.setAttribute("url", newURL);
             }
 
             let engineStr =
-              this._stringBundle.formatStringFromName("searchWithEngine", [engineName], 1);
+              this._stringBundle.formatStringFromName("searchWithEngine", [engineName]);
             this._setUpDescription(this._actionText, engineStr, true);
 
             // Make the title by generating an array of pairs and its
             // corresponding interpolation string (e.g., "%1$S") to pass to
             // _generateEmphasisPairs.
             let pairs;
             if (searchSuggestion) {
               // Check if the search query appears in the suggestion.  It may
--- a/toolkit/content/widgets/stringbundle.js
+++ b/toolkit/content/widgets/stringbundle.js
@@ -44,17 +44,17 @@ class MozStringbundle extends MozXULElem
     } catch (e) {
       dump("*** Failed to get string " + aStringKey + " in bundle: " + this.src + "\n");
       throw e;
     }
   }
 
   getFormattedString(aStringKey, aStringsArray) {
     try {
-      return this.stringBundle.formatStringFromName(aStringKey, aStringsArray, aStringsArray.length);
+      return this.stringBundle.formatStringFromName(aStringKey, aStringsArray);
     } catch (e) {
       dump("*** Failed to format string " + aStringKey + " in bundle: " + this.src + "\n");
       throw e;
     }
   }
 }
 
 customElements.define("stringbundle", MozStringbundle);
--- a/toolkit/content/widgets/wizard.xml
+++ b/toolkit/content/widgets/wizard.xml
@@ -365,23 +365,23 @@
 
       <method name="_adjustWizardHeader">
         <body><![CDATA[
           var label = this.currentPage.getAttribute("label");
           if (!label && this.onFirstPage && this._bundle) {
             if (/Mac/.test(navigator.platform)) {
               label = this._bundle.GetStringFromName("default-first-title-mac");
             } else {
-              label = this._bundle.formatStringFromName("default-first-title", [this.title], 1);
+              label = this._bundle.formatStringFromName("default-first-title", [this.title]);
             }
           } else if (!label && this.onLastPage && this._bundle) {
             if (/Mac/.test(navigator.platform)) {
               label = this._bundle.GetStringFromName("default-last-title-mac");
             } else {
-              label = this._bundle.formatStringFromName("default-last-title", [this.title], 1);
+              label = this._bundle.formatStringFromName("default-last-title", [this.title]);
             }
           }
           this._wizardHeader.
             querySelector(".wizard-header-label").textContent = label;
           let headerDescEl =
             this._wizardHeader.querySelector(".wizard-header-description");
           if (headerDescEl) {
             headerDescEl.textContent =
--- a/toolkit/mozapps/downloads/DownloadUtils.jsm
+++ b/toolkit/mozapps/downloads/DownloadUtils.jsm
@@ -115,22 +115,20 @@ var DownloadUtils = {
       = this._deriveTransferRate(aCurrBytes, aMaxBytes, aSpeed, aLastSec);
 
     let [rate, unit] = DownloadUtils.convertByteUnits(normalizedSpeed);
 
     let status;
     if (rate === "Infinity") {
       // Infinity download speed doesn't make sense. Show a localized phrase instead.
       let params = [transfer, gBundle.GetStringFromName(gStr.infiniteRate), timeLeft];
-      status = gBundle.formatStringFromName(gStr.statusFormatInfiniteRate, params,
-                                            params.length);
+      status = gBundle.formatStringFromName(gStr.statusFormatInfiniteRate, params);
     } else {
       let params = [transfer, rate, unit, timeLeft];
-      status = gBundle.formatStringFromName(gStr.statusFormat, params,
-                                            params.length);
+      status = gBundle.formatStringFromName(gStr.statusFormat, params);
     }
     return [status, newLast];
   },
 
   /**
    * Generate a status string for a download given its current progress,
    * total size, speed, last time remaining. The status string contains the
    * time remaining, as well as the total bytes downloaded. Unlike
@@ -148,18 +146,17 @@ var DownloadUtils = {
    */
   getDownloadStatusNoRate:
   function DU_getDownloadStatusNoRate(aCurrBytes, aMaxBytes, aSpeed,
                                       aLastSec) {
     let [transfer, timeLeft, newLast]
       = this._deriveTransferRate(aCurrBytes, aMaxBytes, aSpeed, aLastSec);
 
     let params = [transfer, timeLeft];
-    let status = gBundle.formatStringFromName(gStr.statusFormatNoRate, params,
-                                              params.length);
+    let status = gBundle.formatStringFromName(gStr.statusFormatNoRate, params);
     return [status, newLast];
   },
 
   /**
    * Helper function that returns a transfer string, a time remaining string,
    * and a new value of "last seconds".
    * @param aCurrBytes
    *        Number of bytes transferred so far
@@ -229,17 +226,17 @@ var DownloadUtils = {
       values = [
         progress,
         progressUnits,
         total,
         totalUnits,
       ];
     }
 
-    return gBundle.formatStringFromName(name, values, values.length);
+    return gBundle.formatStringFromName(name, values);
   },
 
   /**
    * Generate a "time left" string given an estimate on the time left and the
    * last time. The extra time is used to give a better estimate on the time to
    * show. Both the time values are doubles instead of integers to help get
    * sub-second accuracy for current and future estimates.
    *
@@ -288,29 +285,29 @@ var DownloadUtils = {
       // Be friendly in the last few seconds
       timeLeft = gBundle.GetStringFromName(gStr.timeFewSeconds);
     } else {
       // Convert the seconds into its two largest units to display
       let [time1, unit1, time2, unit2] =
         DownloadUtils.convertTimeUnits(aSeconds);
 
       let pair1 =
-        gBundle.formatStringFromName(gStr.timePair, [nf.format(time1), unit1], 2);
+        gBundle.formatStringFromName(gStr.timePair, [nf.format(time1), unit1]);
       let pair2 =
-        gBundle.formatStringFromName(gStr.timePair, [nf.format(time2), unit2], 2);
+        gBundle.formatStringFromName(gStr.timePair, [nf.format(time2), unit2]);
 
       // Only show minutes for under 1 hour unless there's a few minutes left;
       // or the second pair is 0.
       if ((aSeconds < 3600 && time1 >= 4) || time2 == 0) {
         timeLeft = gBundle.formatStringFromName(gStr.timeLeftSingle,
-                                                [pair1], 1);
+                                                [pair1]);
       } else {
         // We've got 2 pairs of times to display
         timeLeft = gBundle.formatStringFromName(gStr.timeLeftDouble,
-                                                [pair1, pair2], 2);
+                                                [pair1, pair2]);
       }
     }
 
     return [timeLeft, aSeconds];
   },
 
   /**
    * Converts a Date object to two readable formats, one compact, one complete.
@@ -413,17 +410,17 @@ var DownloadUtils = {
     // Check if we need to show something else for the host
     if (uri.scheme == "file") {
       // Display special text for file protocol
       displayHost = gBundle.GetStringFromName(gStr.doneFileScheme);
       fullHost = displayHost;
     } else if (displayHost.length == 0) {
       // Got nothing; show the scheme (data: about: moz-icon:)
       displayHost =
-        gBundle.formatStringFromName(gStr.doneScheme, [uri.scheme], 1);
+        gBundle.formatStringFromName(gStr.doneScheme, [uri.scheme]);
       fullHost = displayHost;
     } else if (uri.port != -1) {
       // Tack on the port if it's not the default port
       let port = ":" + uri.port;
       displayHost += port;
       fullHost += port;
     }
 
--- a/toolkit/mozapps/extensions/AddonContentPolicy.cpp
+++ b/toolkit/mozapps/extensions/AddonContentPolicy.cpp
@@ -273,18 +273,19 @@ class CSPValidator final : public nsCSPS
   inline nsAString& GetError() { return mError; };
 
   inline bool FoundSelf() { return mFoundSelf; };
 
   // Formatters
 
   template <typename... T>
   inline void FormatError(const char* aName, const T... aParams) {
-    const char16_t* params[] = {mDirective.get(), aParams.get()...};
-    FormatErrorParams(aName, params, MOZ_ARRAY_LENGTH(params));
+    AutoTArray<nsString, sizeof...(aParams) + 1> params = {mDirective,
+                                                           aParams...};
+    FormatErrorParams(aName, params);
   };
 
  private:
   // Validators
 
   bool HostIsAllowed(nsAString& host) {
     if (host.First() == '*') {
       if (host.EqualsLiteral("*") || host[1] != '.') {
@@ -329,24 +330,23 @@ class CSPValidator final : public nsCSPS
 
     nsCOMPtr<nsIStringBundle> stringBundle;
     sbs->CreateBundle("chrome://global/locale/extensions.properties",
                       getter_AddRefs(stringBundle));
 
     return stringBundle.forget();
   };
 
-  void FormatErrorParams(const char* aName, const char16_t** aParams,
-                         int32_t aLength) {
+  void FormatErrorParams(const char* aName, const nsTArray<nsString>& aParams) {
     nsresult rv = NS_ERROR_FAILURE;
 
     nsCOMPtr<nsIStringBundle> stringBundle = GetStringBundle();
 
     if (stringBundle) {
-      rv = stringBundle->FormatStringFromName(aName, aParams, aLength, mError);
+      rv = stringBundle->FormatStringFromName(aName, aParams, mError);
     }
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mError.AssignLiteral("An unexpected error occurred");
     }
   };
 
   // Data members
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -900,17 +900,17 @@ var gViewController = {
     this.currentViewObj.node.setAttribute("loading", "true");
 
     recordViewTelemetry(view.param);
 
     let headingName = document.getElementById("heading-name");
     let headingLabel;
     if (view.type == "discover") {
       headingLabel = gStrings.ext.formatStringFromName(
-        "listHeading.discover", [gStrings.brandShortName], 1);
+        "listHeading.discover", [gStrings.brandShortName]);
     } else {
       try {
         headingLabel = gStrings.ext.GetStringFromName(`listHeading.${view.param}`);
       } catch (e) {
         // Some views don't have a label, like the updates view.
         headingLabel = "";
       }
     }
@@ -2569,17 +2569,17 @@ var gListView = {
       if (showLegacyInfo) {
         let el = document.getElementById("legacy-extensions-description");
         if (el.childNodes[0].nodeName == "#text") {
           el.removeChild(el.childNodes[0]);
         }
 
         let descriptionId = (aType == "theme") ?
                             "legacyThemeWarning.description" : "legacyWarning.description";
-        let text = gStrings.ext.formatStringFromName(descriptionId, [gStrings.brandShortName], 1) + " ";
+        let text = gStrings.ext.formatStringFromName(descriptionId, [gStrings.brandShortName]) + " ";
         el.insertBefore(document.createTextNode(text), el.childNodes[0]);
         legacyNotice.hidden = false;
       } else {
         legacyNotice.hidden = true;
       }
 
       gEventManager.registerInstallListener(this);
       gViewController.updateCommands();
@@ -3031,113 +3031,113 @@ var gDetailView = {
     if (pending & AddonManager.PENDING_UNINSTALL) {
       this.node.removeAttribute("notification");
 
       // We don't care about pending operations other than uninstall.
       // They're transient, and cannot be undone.
       this.node.setAttribute("pending", "uninstall");
       document.getElementById("detail-pending").textContent = gStrings.ext.formatStringFromName(
         "details.notification.restartless-uninstall",
-        [this._addon.name], 1);
+        [this._addon.name]);
     } else {
       this.node.removeAttribute("pending");
 
       if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
         this.node.setAttribute("notification", "error");
         document.getElementById("detail-error").textContent = gStrings.ext.formatStringFromName(
           "details.notification.blocked",
-          [this._addon.name], 1
+          [this._addon.name]
         );
         var errorLink = document.getElementById("detail-error-link");
         errorLink.value = gStrings.ext.GetStringFromName("details.notification.blocked.link");
         this._addon.getBlocklistURL().then(url => {
           errorLink.href = url;
           errorLink.hidden = false;
         });
       } else if (isDisabledUnsigned(this._addon)) {
         this.node.setAttribute("notification", "error");
         document.getElementById("detail-error").textContent = gStrings.ext.formatStringFromName(
-          "details.notification.unsignedAndDisabled", [this._addon.name, gStrings.brandShortName], 2
+          "details.notification.unsignedAndDisabled", [this._addon.name, gStrings.brandShortName]
         );
         let errorLink = document.getElementById("detail-error-link");
         errorLink.value = gStrings.ext.GetStringFromName("details.notification.unsigned.link");
         errorLink.href = SUPPORT_URL + "unsigned-addons";
         errorLink.hidden = false;
       } else if (!this._addon.isCompatible && (AddonManager.checkCompatibility ||
         (this._addon.blocklistState != Ci.nsIBlocklistService.STATE_SOFTBLOCKED))) {
         this.node.setAttribute("notification", "warning");
         document.getElementById("detail-warning").textContent = gStrings.ext.formatStringFromName(
           "details.notification.incompatible",
-          [this._addon.name, gStrings.brandShortName, gStrings.appVersion], 3
+          [this._addon.name, gStrings.brandShortName, gStrings.appVersion]
         );
         document.getElementById("detail-warning-link").hidden = true;
       } else if (!isCorrectlySigned(this._addon)) {
         this.node.setAttribute("notification", "warning");
         document.getElementById("detail-warning").textContent = gStrings.ext.formatStringFromName(
-          "details.notification.unsigned", [this._addon.name, gStrings.brandShortName], 2
+          "details.notification.unsigned", [this._addon.name, gStrings.brandShortName]
         );
         var warningLink = document.getElementById("detail-warning-link");
         warningLink.value = gStrings.ext.GetStringFromName("details.notification.unsigned.link");
         warningLink.href = SUPPORT_URL + "unsigned-addons";
         warningLink.hidden = false;
       } else if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED) {
         this.node.setAttribute("notification", "warning");
         document.getElementById("detail-warning").textContent = gStrings.ext.formatStringFromName(
           "details.notification.softblocked",
-          [this._addon.name], 1
+          [this._addon.name]
         );
         let warningLink = document.getElementById("detail-warning-link");
         warningLink.value = gStrings.ext.GetStringFromName("details.notification.softblocked.link");
         this._addon.getBlocklistURL().then(url => {
           warningLink.href = url;
           warningLink.hidden = false;
         });
       } else if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
         this.node.setAttribute("notification", "warning");
         document.getElementById("detail-warning").textContent = gStrings.ext.formatStringFromName(
           "details.notification.outdated",
-          [this._addon.name], 1
+          [this._addon.name]
         );
         let warningLink = document.getElementById("detail-warning-link");
         warningLink.value = gStrings.ext.GetStringFromName("details.notification.outdated.link");
         this._addon.getBlocklistURL().then(url => {
           warningLink.href = url;
           warningLink.hidden = false;
         });
       } else if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) {
         this.node.setAttribute("notification", "error");
         document.getElementById("detail-error").textContent = gStrings.ext.formatStringFromName(
           "details.notification.vulnerableUpdatable",
-          [this._addon.name], 1
+          [this._addon.name]
         );
         let errorLink = document.getElementById("detail-error-link");
         errorLink.value = gStrings.ext.GetStringFromName("details.notification.vulnerableUpdatable.link");
         this._addon.getBlocklistURL().then(url => {
           errorLink.href = url;
           errorLink.hidden = false;
         });
       } else if (this._addon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) {
         this.node.setAttribute("notification", "error");
         document.getElementById("detail-error").textContent = gStrings.ext.formatStringFromName(
           "details.notification.vulnerableNoUpdate",
-          [this._addon.name], 1
+          [this._addon.name]
         );
         let errorLink = document.getElementById("detail-error-link");
         errorLink.value = gStrings.ext.GetStringFromName("details.notification.vulnerableNoUpdate.link");
         this._addon.getBlocklistURL().then(url => {
           errorLink.href = url;
           errorLink.hidden = false;
         });
       } else if (this._addon.isGMPlugin && !this._addon.isInstalled &&
                  this._addon.isActive) {
         this.node.setAttribute("notification", "warning");
         let warning = document.getElementById("detail-warning");
         warning.textContent =
           gStrings.ext.formatStringFromName("details.notification.gmpPending",
-                                            [this._addon.name], 1);
+                                            [this._addon.name]);
       } else {
         this.node.removeAttribute("notification");
       }
     }
 
     let menulist = document.getElementById("detail-state-menulist");
     let addonType = AddonManager.addonTypes[this._addon.type];
     if (addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) {
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -982,133 +982,133 @@
           if (pending & AddonManager.PENDING_UNINSTALL) {
             this.removeAttribute("notification");
 
             // We don't care about pending operations other than uninstall.
             // They're transient, and cannot be undone.
             this.setAttribute("pending", "uninstall");
             this._pending.textContent = gStrings.ext.formatStringFromName(
               "notification.restartless-uninstall",
-              [this.mAddon.name], 1);
+              [this.mAddon.name]);
           } else {
             this.removeAttribute("pending");
 
             var isUpgrade = this.hasAttribute("upgrade");
             var install = this._installStatus.mInstall;
 
             if (install && install.state == AddonManager.STATE_DOWNLOAD_FAILED) {
               this.setAttribute("notification", "warning");
               this._warning.textContent = gStrings.ext.formatStringFromName(
                 "notification.downloadError",
-                [this.mAddon.name], 1
+                [this.mAddon.name]
               );
               this._warningBtn.label = gStrings.ext.GetStringFromName("notification.downloadError.retry");
               this._warningBtn.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip");
               this._warningBtn.setAttribute("oncommand", "document.getBindingParent(this).retryInstall();");
               this._warningBtn.hidden = false;
               this._warningLink.hidden = true;
             } else if (install && install.state == AddonManager.STATE_INSTALL_FAILED) {
               this.setAttribute("notification", "warning");
               this._warning.textContent = gStrings.ext.formatStringFromName(
                 "notification.installError",
-                [this.mAddon.name], 1
+                [this.mAddon.name]
               );
               this._warningBtn.label = gStrings.ext.GetStringFromName("notification.installError.retry");
               this._warningBtn.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip");
               this._warningBtn.setAttribute("oncommand", "document.getBindingParent(this).retryInstall();");
               this._warningBtn.hidden = false;
               this._warningLink.hidden = true;
             } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) {
               this.setAttribute("notification", "error");
               this._error.textContent = gStrings.ext.formatStringFromName(
                 "notification.blocked",
-                [this.mAddon.name], 1
+                [this.mAddon.name]
               );
               this._errorLink.value = gStrings.ext.GetStringFromName("notification.blocked.link");
               this.mAddon.getBlocklistURL().then(url => {
                 this._errorLink.href = url;
                 this._errorLink.hidden = false;
               });
             } else if (!isUpgrade && isDisabledUnsigned(this.mAddon)) {
               this.setAttribute("notification", "error");
               this._error.textContent = gStrings.ext.formatStringFromName(
-                "notification.unsignedAndDisabled", [this.mAddon.name, gStrings.brandShortName], 2
+                "notification.unsignedAndDisabled", [this.mAddon.name, gStrings.brandShortName]
               );
               this._errorLink.value = gStrings.ext.GetStringFromName("notification.unsigned.link");
               this._errorLink.href = SUPPORT_URL + "unsigned-addons";
               this._errorLink.hidden = false;
             } else if ((!isUpgrade && !this.mAddon.isCompatible) && (AddonManager.checkCompatibility
             || (this.mAddon.blocklistState != Ci.nsIBlocklistService.STATE_SOFTBLOCKED))) {
               this.setAttribute("notification", "warning");
               this._warning.textContent = gStrings.ext.formatStringFromName(
                 "notification.incompatible",
-                [this.mAddon.name, gStrings.brandShortName, gStrings.appVersion], 3
+                [this.mAddon.name, gStrings.brandShortName, gStrings.appVersion]
               );
               this._warningLink.hidden = true;
               this._warningBtn.hidden = true;
             } else if (!isUpgrade && !isCorrectlySigned(this.mAddon)) {
               this.setAttribute("notification", "warning");
               this._warning.textContent = gStrings.ext.formatStringFromName(
-                "notification.unsigned", [this.mAddon.name, gStrings.brandShortName], 2
+                "notification.unsigned", [this.mAddon.name, gStrings.brandShortName]
               );
               this._warningLink.value = gStrings.ext.GetStringFromName("notification.unsigned.link");
               this._warningLink.href = SUPPORT_URL + "unsigned-addons";
               this._warningLink.hidden = false;
             } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED) {
               this.setAttribute("notification", "warning");
               this._warning.textContent = gStrings.ext.formatStringFromName(
                 "notification.softblocked",
-                [this.mAddon.name], 1
+                [this.mAddon.name]
               );
               this._warningLink.value = gStrings.ext.GetStringFromName("notification.softblocked.link");
               this.mAddon.getBlocklistURL().then(url => {
                 this._warningLink.href = url;
                 this._warningLink.hidden = false;
               });
               this._warningBtn.hidden = true;
             } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
               this.setAttribute("notification", "warning");
               this._warning.textContent = gStrings.ext.formatStringFromName(
                 "notification.outdated",
-                [this.mAddon.name], 1
+                [this.mAddon.name]
               );
               this._warningLink.value = gStrings.ext.GetStringFromName("notification.outdated.link");
               this.mAddon.getBlocklistURL().then(url => {
                 this._warningLink.href = url;
                 this._warningLink.hidden = false;
               });
               this._warningBtn.hidden = true;
             } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) {
               this.setAttribute("notification", "error");
               this._error.textContent = gStrings.ext.formatStringFromName(
                 "notification.vulnerableUpdatable",
-                [this.mAddon.name], 1
+                [this.mAddon.name]
               );
               this._errorLink.value = gStrings.ext.GetStringFromName("notification.vulnerableUpdatable.link");
               this.mAddon.getBlocklistURL().then(url => {
                 this._errorLink.href = url;
                 this._errorLink.hidden = false;
               });
             } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) {
               this.setAttribute("notification", "error");
               this._error.textContent = gStrings.ext.formatStringFromName(
                 "notification.vulnerableNoUpdate",
-                [this.mAddon.name], 1
+                [this.mAddon.name]
               );
               this._errorLink.value = gStrings.ext.GetStringFromName("notification.vulnerableNoUpdate.link");
               this.mAddon.getBlocklistURL().then(url => {
                 this._errorLink.href = url;
                 this._errorLink.hidden = false;
               });
             } else if (this.mAddon.isGMPlugin && !this.mAddon.isInstalled &&
                        this.mAddon.isActive) {
               this.setAttribute("notification", "warning");
               this._warning.textContent =
                 gStrings.ext.formatStringFromName("notification.gmpPending",
-                                                  [this.mAddon.name], 1);
+                                                  [this.mAddon.name]);
             } else {
               this.removeAttribute("notification");
             }
           }
 
           this._preferencesBtn.hidden = !this.mAddon.optionsType && this.mAddon.type != "plugin";
 
           if (this.typeHasFlag("SUPPORTS_ASK_TO_ACTIVATE")) {
@@ -1497,18 +1497,17 @@
                     oncommand="document.getBindingParent(this).cancelUninstall();"/>
         <xul:spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
       </xul:hbox>
     </content>
 
     <implementation>
       <constructor><![CDATA[
         this._notice.textContent = gStrings.ext.formatStringFromName("uninstallNotice",
-                                                                     [this.mAddon.name],
-                                                                     1);
+                                                                     [this.mAddon.name]);
 
         gEventManager.registerAddonListener(this, this.mAddon.id);
       ]]></constructor>
 
       <destructor><![CDATA[
         gEventManager.unregisterAddonListener(this, this.mAddon.id);
       ]]></destructor>
 
@@ -1667,25 +1666,25 @@
               this._name.value = url.fileName;
             }
           }
 
           if (this.mInstall.state == AddonManager.STATE_DOWNLOAD_FAILED) {
             this.setAttribute("notification", "warning");
             this._warning.textContent = gStrings.ext.formatStringFromName(
               "notification.downloadError",
-              [this._name.value], 1
+              [this._name.value]
             );
             this._warningLink.label = gStrings.ext.GetStringFromName("notification.downloadError.retry");
             this._warningLink.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip");
           } else if (this.mInstall.state == AddonManager.STATE_INSTALL_FAILED) {
             this.setAttribute("notification", "warning");
             this._warning.textContent = gStrings.ext.formatStringFromName(
               "notification.installError",
-              [this._name.value], 1
+              [this._name.value]
             );
             this._warningLink.label = gStrings.ext.GetStringFromName("notification.installError.retry");
             this._warningLink.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip");
           } else {
             this.removeAttribute("notification");
           }
         ]]></body>
       </method>
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -473,17 +473,17 @@ function wait_for_window_open(aCallback)
 
   return log_callback(p, aCallback);
 }
 
 function get_string(aName, ...aArgs) {
   var bundle = Services.strings.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
   if (aArgs.length == 0)
     return bundle.GetStringFromName(aName);
-  return bundle.formatStringFromName(aName, aArgs, aArgs.length);
+  return bundle.formatStringFromName(aName, aArgs);
 }
 
 function formatDate(aDate) {
   const dtOptions = { year: "numeric", month: "long", day: "numeric" };
   return aDate.toLocaleDateString(undefined, dtOptions);
 }
 
 function is_hidden(aElement) {
--- a/toolkit/mozapps/handling/ContentDispatchChooser.jsm
+++ b/toolkit/mozapps/handling/ContentDispatchChooser.jsm
@@ -31,20 +31,20 @@ nsContentDispatchChooser.prototype =
 
     // TODO when this is hooked up for content, we will need different strings
     //      for most of these
     var arr = [bundle.GetStringFromName("protocol.title"),
                "",
                bundle.GetStringFromName("protocol.description"),
                bundle.GetStringFromName("protocol.choices.label"),
                bundle.formatStringFromName("protocol.checkbox.label",
-                                           [aURI.scheme], 1),
+                                           [aURI.scheme]),
                bundle.GetStringFromName("protocol.checkbox.accesskey"),
                bundle.formatStringFromName("protocol.checkbox.extra",
-                                           [Services.appinfo.name], 1)];
+                                           [Services.appinfo.name])];
 
     var params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
     let SupportsString = Components.Constructor(
                            "@mozilla.org/supports-string;1",
                            "nsISupportsString");
     for (let text of arr) {
       let string = new SupportsString;
       string.data = text;
--- a/toolkit/mozapps/update/UpdateService.jsm
+++ b/toolkit/mozapps/update/UpdateService.jsm
@@ -1614,17 +1614,17 @@ function Update(update) {
   }
 
   if (!this.name) {
     // When the update doesn't provide a name fallback to using
     // "<App Name> <Update App Version>"
     let brandBundle = Services.strings.createBundle(URI_BRAND_PROPERTIES);
     let appName = brandBundle.GetStringFromName("brandShortName");
     this.name = gUpdateBundle.formatStringFromName("updateName",
-                                                   [appName, this.displayVersion], 2);
+                                                   [appName, this.displayVersion]);
   }
 }
 Update.prototype = {
   // nsIUpdate attribute names used to prevent nsIWritablePropertyBag from over
   // writing nsIUpdate attributes.
   _attrNames: [
     "appVersion", "buildID", "channel", "detailsURL", "displayVersion",
     "elevationFailure", "errorCode", "installDate", "isCompleteUpdate", "name",
--- a/toolkit/xre/ProfileReset.cpp
+++ b/toolkit/xre/ProfileReset.cpp
@@ -51,22 +51,22 @@ nsresult ProfileResetCleanup(nsToolkitPr
       mozilla::services::GetStringBundleService();
   if (!sbs) return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIStringBundle> sb;
   Unused << sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
   if (!sb) return NS_ERROR_FAILURE;
 
   NS_ConvertUTF8toUTF16 appName(gAppData->name);
-  const char16_t* params[] = {appName.get(), appName.get()};
+  AutoTArray<nsString, 2> params = {appName, appName};
 
   nsAutoString resetBackupDirectoryName;
 
   static const char* kResetBackupDirectory = "resetBackupDirectory";
-  rv = sb->FormatStringFromName(kResetBackupDirectory, params, 2,
+  rv = sb->FormatStringFromName(kResetBackupDirectory, params,
                                 resetBackupDirectoryName);
   if (NS_FAILED(rv)) return rv;
 
   // Get info to copy the old root profile dir to the desktop as a backup.
   nsCOMPtr<nsIFile> backupDest, containerDest, profileDest;
   rv = NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(backupDest));
   if (NS_FAILED(rv)) {
     // Fall back to the home directory if the desktop is not available.
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1686,26 +1686,26 @@ static nsresult ProfileMissingDialog(nsI
         mozilla::services::GetStringBundleService();
     NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE);
 
     nsCOMPtr<nsIStringBundle> sb;
     sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
     NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE);
 
     NS_ConvertUTF8toUTF16 appName(gAppData->name);
-    const char16_t* params[] = {appName.get(), appName.get()};
+    AutoTArray<nsString, 2> params = {appName, appName};
 
     // profileMissing
     nsAutoString missingMessage;
-    rv = sb->FormatStringFromName("profileMissing", params, 2, missingMessage);
+    rv = sb->FormatStringFromName("profileMissing", params, missingMessage);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_ABORT);
 
     nsAutoString missingTitle;
-    rv = sb->FormatStringFromName("profileMissingTitle", params, 1,
-                                  missingTitle);
+    params.SetLength(1);
+    rv = sb->FormatStringFromName("profileMissingTitle", params, missingTitle);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_ABORT);
 
     nsCOMPtr<nsIPromptService> ps(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
     NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE);
 
     ps->Alert(nullptr, missingTitle.get(), missingMessage.get());
 
     return NS_ERROR_ABORT;
@@ -1740,32 +1740,33 @@ static ReturnAbortOnError ProfileLockedD
         mozilla::services::GetStringBundleService();
     NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE);
 
     nsCOMPtr<nsIStringBundle> sb;
     sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
     NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE);
 
     NS_ConvertUTF8toUTF16 appName(gAppData->name);
-    const char16_t* params[] = {appName.get(), appName.get()};
+    AutoTArray<nsString, 2> params = {appName, appName};
 
     nsAutoString killMessage;
 #ifndef XP_MACOSX
     rv = sb->FormatStringFromName(
         aUnlocker ? "restartMessageUnlocker" : "restartMessageNoUnlocker",
-        params, 2, killMessage);
+        params, killMessage);
 #else
     rv = sb->FormatStringFromName(
         aUnlocker ? "restartMessageUnlockerMac" : "restartMessageNoUnlockerMac",
-        params, 2, killMessage);
+        params, killMessage);
 #endif
     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
+    params.SetLength(1);
     nsAutoString killTitle;
-    rv = sb->FormatStringFromName("restartTitle", params, 1, killTitle);
+    rv = sb->FormatStringFromName("restartTitle", params, killTitle);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
     if (gfxPlatform::IsHeadless()) {
       // TODO: make a way to turn off all dialogs when headless.
       Output(true, "%s\n", NS_LossyConvertUTF16toASCII(killMessage).get());
       return NS_ERROR_FAILURE;
     }
 
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -1845,35 +1845,34 @@ void nsExternalAppHandler::SendStatusCha
   nsCOMPtr<nsIStringBundleService> stringService =
       mozilla::services::GetStringBundleService();
   if (stringService) {
     nsCOMPtr<nsIStringBundle> bundle;
     if (NS_SUCCEEDED(stringService->CreateBundle(
             "chrome://global/locale/nsWebBrowserPersist.properties",
             getter_AddRefs(bundle)))) {
       nsAutoString msgText;
-      const char16_t* strings[] = {path.get()};
-      if (NS_SUCCEEDED(
-              bundle->FormatStringFromName(msgId, strings, 1, msgText))) {
+      AutoTArray<nsString, 1> strings = {path};
+      if (NS_SUCCEEDED(bundle->FormatStringFromName(msgId, strings, msgText))) {
         if (mDialogProgressListener) {
           // We have a listener, let it handle the error.
           mDialogProgressListener->OnStatusChange(
               nullptr, (type == kReadError) ? aRequest : nullptr, rv,
               msgText.get());
         } else if (mTransfer) {
           mTransfer->OnStatusChange(nullptr,
                                     (type == kReadError) ? aRequest : nullptr,
                                     rv, msgText.get());
         } else if (XRE_IsParentProcess()) {
           // We don't have a listener.  Simply show the alert ourselves.
           nsresult qiRv;
           nsCOMPtr<nsIPrompt> prompter(
               do_GetInterface(GetDialogParent(), &qiRv));
           nsAutoString title;
-          bundle->FormatStringFromName("title", strings, 1, title);
+          bundle->FormatStringFromName("title", strings, title);
 
           MOZ_LOG(
               nsExternalHelperAppService::mLog, LogLevel::Debug,
               ("mContentContext=0x%p, prompter=0x%p, qi rv=0x%08" PRIX32
                ", title='%s', msg='%s'",
                mContentContext.get(), prompter.get(),
                static_cast<uint32_t>(qiRv), NS_ConvertUTF16toUTF8(title).get(),
                NS_ConvertUTF16toUTF8(msgText).get()));
--- a/widget/cocoa/OSXNotificationCenter.mm
+++ b/widget/cocoa/OSXNotificationCenter.mm
@@ -261,20 +261,19 @@ OSXNotificationCenter::ShowAlertWithIcon
   nsAutoString hostPort;
   rv = aAlert->GetSource(hostPort);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIStringBundle> bundle;
   nsCOMPtr<nsIStringBundleService> sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
   sbs->CreateBundle("chrome://alerts/locale/alert.properties", getter_AddRefs(bundle));
 
   if (!hostPort.IsEmpty() && bundle) {
-    const char16_t* formatStrings[] = {hostPort.get()};
+    AutoTArray<nsString, 1> formatStrings = {hostPort};
     nsAutoString notificationSource;
-    bundle->FormatStringFromName("source.label", formatStrings, ArrayLength(formatStrings),
-                                 notificationSource);
+    bundle->FormatStringFromName("source.label", formatStrings, notificationSource);
     notification.subtitle = nsCocoaUtils::ToNSString(notificationSource);
   }
 
   nsAutoString text;
   rv = aAlert->GetText(text);
   NS_ENSURE_SUCCESS(rv, rv);
   notification.informativeText = nsCocoaUtils::ToNSString(text);
 
@@ -284,19 +283,19 @@ OSXNotificationCenter::ShowAlertWithIcon
   // If this is not an application/extension alert, show additional actions dealing with
   // permissions.
   bool isActionable;
   if (bundle && NS_SUCCEEDED(aAlert->GetActionable(&isActionable)) && isActionable) {
     nsAutoString closeButtonTitle, actionButtonTitle, disableButtonTitle, settingsButtonTitle;
     bundle->GetStringFromName("closeButton.title", closeButtonTitle);
     bundle->GetStringFromName("actionButton.label", actionButtonTitle);
     if (!hostPort.IsEmpty()) {
-      const char16_t* formatStrings[] = {hostPort.get()};
+      AutoTArray<nsString, 1> formatStrings = {hostPort};
       bundle->FormatStringFromName("webActions.disableForOrigin.label", formatStrings,
-                                   ArrayLength(formatStrings), disableButtonTitle);
+                                   disableButtonTitle);
     }
     bundle->GetStringFromName("webActions.settings.label", settingsButtonTitle);
 
     notification.otherButtonTitle = nsCocoaUtils::ToNSString(closeButtonTitle);
 
     // OS X 10.8 only shows action buttons if the "Alerts" style is set in
     // Notification Center preferences, and doesn't support the alternate
     // action menu.
--- a/widget/windows/ToastNotificationHandler.cpp
+++ b/widget/windows/ToastNotificationHandler.cpp
@@ -273,27 +273,26 @@ bool ToastNotificationHandler::ShowAlert
   nsCOMPtr<nsIStringBundle> bundle;
   sbs->CreateBundle("chrome://alerts/locale/alert.properties",
                     getter_AddRefs(bundle));
   if (NS_WARN_IF(!bundle)) {
     return false;
   }
 
   if (!mHostPort.IsEmpty()) {
-    const char16_t* formatStrings[] = {mHostPort.get()};
+    AutoTArray<nsString, 1> formatStrings = {mHostPort};
 
     ComPtr<IXmlNode> urlTextNodeRoot;
     hr = toastTextElements->Item(2, &urlTextNodeRoot);
     if (NS_WARN_IF(FAILED(hr))) {
       return false;
     }
 
     nsAutoString urlReference;
-    bundle->FormatStringFromName("source.label", formatStrings,
-                                 ArrayLength(formatStrings), urlReference);
+    bundle->FormatStringFromName("source.label", formatStrings, urlReference);
 
     if (NS_WARN_IF(!SetNodeValueString(urlReference, urlTextNodeRoot.Get(),
                                        toastXml.Get()))) {
       return false;
     }
 
     if (IsWin10AnniversaryUpdateOrLater()) {
       ComPtr<IXmlElement> placementText;
@@ -302,18 +301,17 @@ bool ToastNotificationHandler::ShowAlert
         // placement is supported on Windows 10 Anniversary Update or later
         SetAttribute(placementText.Get(), HStringReference(L"placement").Get(),
                      NS_LITERAL_STRING("attribution"));
       }
     }
 
     nsAutoString disableButtonTitle;
     bundle->FormatStringFromName("webActions.disableForOrigin.label",
-                                 formatStrings, ArrayLength(formatStrings),
-                                 disableButtonTitle);
+                                 formatStrings, disableButtonTitle);
 
     AddActionNode(toastXml.Get(), actionsNode.Get(), disableButtonTitle,
                   NS_LITERAL_STRING("snooze"));
   }
 
   nsAutoString settingsButtonTitle;
   bundle->GetStringFromName("webActions.settings.label", settingsButtonTitle);
   AddActionNode(toastXml.Get(), actionsNode.Get(), settingsButtonTitle,