Merge mozilla-inbound to mozilla-central. a=merge
authorAndreea Pavel <apavel@mozilla.com>
Thu, 06 Sep 2018 07:37:40 +0300
changeset 435029 1ef75708e4e122f25d7ea90f3993dc958fb1350a
parent 435028 52dabf91acd0e3fb7f1683b9f80df072f24cc8e4 (current diff)
parent 434986 14d56f95b51f5fd6ef7f0348ad8f24fe051b5392 (diff)
child 435030 5f5d7a3ce3326619529322a3d8c3f7fffeb06f5b
child 435040 9bfe2dbce76cd91f4cdcbbeba5df701e2a334971
child 435045 1552d735c49f5479e468a0b9e2ac11ed03575219
push id107530
push userapavel@mozilla.com
push dateThu, 06 Sep 2018 04:44:27 +0000
treeherdermozilla-inbound@5f5d7a3ce332 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.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
Merge mozilla-inbound to mozilla-central. a=merge
js/public/AutoByteString.h
layout/forms/nsFieldSetFrame.cpp
layout/generic/nsImageFrame.cpp
mobile/android/base/java/org/mozilla/gecko/notifications/NotificationHelper.java
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -178,17 +178,17 @@ AboutRedirector::NewChannel(nsIURI* aURI
         nsCOMPtr<nsIAboutNewTabService> aboutNewTabService =
           do_GetService("@mozilla.org/browser/aboutnewtab-service;1", &rv);
         NS_ENSURE_SUCCESS(rv, rv);
         rv = aboutNewTabService->GetWelcomeURL(url);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       if (sNewCertErrorPageEnabled && path.EqualsLiteral("certerror")) {
-        url.AssignASCII("chrome://browser/content/aboutNetError-new.xhtml");
+        url.Assign(NS_LITERAL_CSTRING("chrome://browser/content/aboutNetError-new.xhtml"));
       }
 
       // fall back to the specified url in the map
       if (url.IsEmpty()) {
         url.AssignASCII(redir.url);
       }
 
       nsCOMPtr<nsIChannel> tempChannel;
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -651,20 +651,32 @@ var gPrivacyPane = {
    */
   updateContentBlockingControls() {
     let dependentControls = [
       "#content-blocking-categories-label",
       ".content-blocking-checkbox",
       "#changeBlockListLink",
       "#contentBlockingChangeCookieSettings",
       "#blockCookiesCB, #blockCookiesCB > radio",
+      "#blockCookies, #blockCookies > radio",
     ];
 
     this._toggleControls(dependentControls, contentBlockingEnabled);
 
+    // The list of dependent controls here would normally include #blockCookiesLabel,
+    // #blockCookiesMenu, #keepUntil and #keepCookiesUntil, but in order to avoid
+    // having two parts of the code responsible for figuring out whether these
+    // controls must be enabled or disabled, we offload that responsibility to
+    // networkCookieBehaviorReadPrefs() which already knows how to deal with it.
+    this.networkCookieBehaviorReadPrefs();
+
+    // If Content Blocking gets disabled, show the warning in the Cookies and Site Data section.
+    let blockCookiesWarning = document.getElementById("blockCookiesWarning");
+    blockCookiesWarning.hidden = contentBlockingEnabled;
+
     // Need to make sure we account for pref locking/extension overrides when enabling the TP menu.
     this._updateTrackingProtectionUI();
 
     // If we are turning Content Blocking on, we may need to keep some parts of the Third-Party Cookies
     // UI off, depending on the value of the cookieBehavior pref.  readBlockCookiesCheckbox() can do
     // the work that is needed for that.
     this.readBlockCookiesCheckbox();
   },
@@ -726,25 +738,27 @@ var gPrivacyPane = {
   networkCookieBehaviorReadPrefs() {
     let behavior = Preferences.get("network.cookie.cookieBehavior").value;
     let blockCookiesCtrl = document.getElementById("blockCookies");
     let blockCookiesLabel = document.getElementById("blockCookiesLabel");
     let blockCookiesMenu = document.getElementById("blockCookiesMenu");
     let keepUntilLabel = document.getElementById("keepUntil");
     let keepUntilMenu = document.getElementById("keepCookiesUntil");
 
+    let disabledByCB = contentBlockingUiEnabled ? !contentBlockingEnabled : false;
     let blockCookies = (behavior != 0);
     let cookieBehaviorLocked = Services.prefs.prefIsLocked("network.cookie.cookieBehavior");
-    let blockCookiesControlsDisabled = !blockCookies || cookieBehaviorLocked;
+    let blockCookiesControlsDisabled = !blockCookies || cookieBehaviorLocked || disabledByCB;
     blockCookiesLabel.disabled = blockCookiesMenu.disabled = blockCookiesControlsDisabled;
 
     let completelyBlockCookies = (behavior == 2);
     let privateBrowsing = Preferences.get("browser.privatebrowsing.autostart").value;
     let cookieExpirationLocked = Services.prefs.prefIsLocked("network.cookie.lifetimePolicy");
-    let keepUntilControlsDisabled = privateBrowsing || completelyBlockCookies || cookieExpirationLocked;
+    let keepUntilControlsDisabled = privateBrowsing || completelyBlockCookies ||
+                                    cookieExpirationLocked || disabledByCB;
     keepUntilLabel.disabled = keepUntilMenu.disabled = keepUntilControlsDisabled;
 
     switch (behavior) {
       case Ci.nsICookieService.BEHAVIOR_ACCEPT:
         blockCookiesCtrl.value = "allow";
         break;
       case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
         blockCookiesCtrl.value = "disallow";
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -167,16 +167,19 @@
 
   <hbox data-subcategory="sitedata" align="baseline">
     <vbox flex="1">
       <description class="description-with-side-element" flex="1">
         <html:span id="totalSiteDataSize" class="tail-with-learn-more"></html:span>
         <label id="siteDataLearnMoreLink"
           class="learnMore text-link" data-l10n-id="sitedata-learn-more"/>
       </description>
+      <description class="description-with-side-element warning-description"
+                   flex="1" hidden="true" id="blockCookiesWarning"
+                   data-l10n-id="sitedata-warning-your-settings-prevent-changes"/>
       <radiogroup id="blockCookies"
                   preference="network.cookie.cookieBehavior"
                   onsyncfrompreference="return gPrivacyPane.readBlockCookies();"
                   onsynctopreference="return gPrivacyPane.writeBlockCookies();">
         <radio value="allow"
                data-l10n-id="sitedata-allow-cookies-option"
                flex="1" />
         <radio value="disallow"
@@ -388,19 +391,19 @@
             <vbox class="content-blocking-category-labels" flex="1">
               <hbox>
                 <vbox flex="1">
                   <deck id="blockCookiesCBDeck">
                     <description id="blockCookiesCBDesc"
                                  data-l10n-id="content-blocking-reject-trackers-description"
                                  class="content-blocking-category-description"/>
                     <description data-l10n-id="content-blocking-reject-trackers-warning-your-settings-prevent-changes"
-                                 class="content-blocking-category-description description-with-side-element reject-trackers-warning-icon"/>
+                                 class="content-blocking-category-description description-with-side-element warning-description"/>
                     <description data-l10n-id="content-blocking-reject-trackers-warning-your-settings-prevent-changes"
-                                 class="content-blocking-category-description description-with-side-element reject-trackers-warning-icon"/>
+                                 class="content-blocking-category-description description-with-side-element warning-description"/>
                   </deck>
                 </vbox>
                 <hbox align="center" pack="end">
                   <vbox align="center">
                     <button id="contentBlockingChangeCookieSettings"
                             class="accessory-button"
                             flex="1"
                             hidden="true"
--- a/browser/components/preferences/in-content/tests/browser_contentblocking.js
+++ b/browser/components/preferences/in-content/tests/browser_contentblocking.js
@@ -325,19 +325,24 @@ add_task(async function testContentBlock
     [NCB_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT],
   ]});
 
   let dependentControls = [
     ".content-blocking-checkbox",
     "#content-blocking-categories-label",
     "#changeBlockListLink",
     "#contentBlockingChangeCookieSettings",
+    "#blockCookies, #blockCookies > radio",
+    "#keepUntil",
+    "#keepCookiesUntil",
   ];
   let alwaysDisabledControls = [
     "#blockCookiesCB, #blockCookiesCB > radio",
+    "#blockCookiesLabel",
+    "#blockCookiesMenu",
   ];
 
   await doDependentControlChecks(dependentControls, alwaysDisabledControls);
 
   // In Block Cookies from Trackers (or Block Cookies from All Third-Parties) mode, the
   // radiogroup's disabled status must obey the content blocking enabled state.
   SpecialPowers.pushPrefEnv({set: [
     [CB_UI_PREF, true],
@@ -348,16 +353,21 @@ add_task(async function testContentBlock
   ]});
 
   dependentControls = [
     ".content-blocking-checkbox",
     "#content-blocking-categories-label",
     "#changeBlockListLink",
     "#contentBlockingChangeCookieSettings",
     "#blockCookiesCB, #blockCookiesCB > radio",
+    "#blockCookies, #blockCookies > radio",
+    "#blockCookiesLabel",
+    "#blockCookiesMenu",
+    "#keepUntil",
+    "#keepCookiesUntil",
   ];
 
   await doDependentControlChecks(dependentControls);
 });
 
 // Checks that the controls for tracking protection are disabled when all TP prefs are off.
 add_task(async function testContentBlockingDependentTPControls() {
   SpecialPowers.pushPrefEnv({set: [
@@ -371,31 +381,79 @@ add_task(async function testContentBlock
   ]});
 
   let dependentControls = [
     "#content-blocking-categories-label",
     "[control=trackingProtectionMenu]",
     "#changeBlockListLink",
     "#contentBlockingChangeCookieSettings",
     "#blockCookiesCB, #blockCookiesCB > radio",
+    "#blockCookies, #blockCookies > radio",
+    "#blockCookiesLabel",
+    "#blockCookiesMenu",
+    "#keepUntil",
+    "#keepCookiesUntil",
   ];
   let alwaysDisabledControls = [
     "#trackingProtectionMenu",
   ];
 
   await doDependentControlChecks(dependentControls, alwaysDisabledControls);
 });
 
 
 // Checks that the granular controls are disabled or enabled depending on the master pref for CB
 // when the Cookies and Site Data section is set to block either "All Cookies" or "Cookies from
 // unvisited websites".
 add_task(async function testContentBlockingDependentControlsOnSiteDataUI() {
   let prefValuesToTest = [
     Ci.nsICookieService.BEHAVIOR_REJECT,        // Block All Cookies
+  ];
+  for (let value of prefValuesToTest) {
+    await SpecialPowers.pushPrefEnv({set: [
+      [CB_UI_PREF, true],
+      [CB_FB_UI_PREF, true],
+      [CB_TP_UI_PREF, true],
+      [CB_RT_UI_PREF, true],
+      [TP_PREF, false],
+      [TP_PBM_PREF, true],
+      [NCB_PREF, value],
+    ]});
+
+    // When Block All Cookies is selected, the Third-Party Cookies section under Content Blocking
+    // as well as the Keep Until controls under Cookies and Site Data should get disabled
+    // unconditionally.
+    let dependentControls = [
+      "#content-blocking-categories-label",
+      "#contentBlockingFastBlockCheckbox",
+      "#contentBlockingTrackingProtectionCheckbox",
+      ".fastblock-icon",
+      ".tracking-protection-icon",
+      "#trackingProtectionMenu",
+      "[control=trackingProtectionMenu]",
+      "#changeBlockListLink",
+      "#contentBlockingChangeCookieSettings",
+      "#blockCookies, #blockCookies > radio",
+      "#blockCookiesLabel",
+      "#blockCookiesMenu",
+    ];
+    let alwaysDisabledControls = [
+      "[control=blockCookiesCB]",
+      "#blockCookiesCBDeck",
+      "#blockCookiesCB, #blockCookiesCB > radio",
+      "#keepUntil",
+      "#keepCookiesUntil",
+    ];
+
+    await doDependentControlChecks(dependentControls, alwaysDisabledControls);
+  }
+
+  // When Block Cookies from unvisited websites is selected, the Third-Party Cookies section under
+  // Content Blocking should get disabled unconditionally.
+  prefValuesToTest = [
     Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN, // Block Cookies from unvisited websites
   ];
   for (let value of prefValuesToTest) {
     await SpecialPowers.pushPrefEnv({set: [
       [CB_UI_PREF, true],
       [CB_FB_UI_PREF, true],
       [CB_TP_UI_PREF, true],
       [CB_RT_UI_PREF, true],
@@ -409,26 +467,34 @@ add_task(async function testContentBlock
       "#contentBlockingFastBlockCheckbox",
       "#contentBlockingTrackingProtectionCheckbox",
       ".fastblock-icon",
       ".tracking-protection-icon",
       "#trackingProtectionMenu",
       "[control=trackingProtectionMenu]",
       "#changeBlockListLink",
       "#contentBlockingChangeCookieSettings",
+      "#blockCookies, #blockCookies > radio",
+      "#blockCookiesLabel",
+      "#blockCookiesMenu",
+      "#keepUntil",
+      "#keepCookiesUntil",
     ];
     let alwaysDisabledControls = [
       "[control=blockCookiesCB]",
       "#blockCookiesCBDeck",
       "#blockCookiesCB, #blockCookiesCB > radio",
     ];
 
     await doDependentControlChecks(dependentControls, alwaysDisabledControls);
   }
 
+  // When Accept All Cookies is selected, the radio buttons under Third-Party Cookies
+  // in Content Blocking as well as the Type blocked controls in Cookies and Site Data
+  // must remain disabled unconditionally.
   prefValuesToTest = [
     Ci.nsICookieService.BEHAVIOR_ACCEPT,         // Accept All Cookies
   ];
   for (let value of prefValuesToTest) {
     await SpecialPowers.pushPrefEnv({set: [
       [CB_UI_PREF, true],
       [CB_FB_UI_PREF, true],
       [CB_TP_UI_PREF, true],
@@ -440,25 +506,31 @@ add_task(async function testContentBlock
 
     let dependentControls = [
       "#content-blocking-categories-label",
       ".content-blocking-checkbox",
       "#trackingProtectionMenu",
       "[control=trackingProtectionMenu]",
       "#changeBlockListLink",
       "#contentBlockingChangeCookieSettings",
+      "#blockCookies, #blockCookies > radio",
+      "#keepUntil",
+      "#keepCookiesUntil",
     ];
     let alwaysDisabledControls = [
       "#blockCookiesCB, #blockCookiesCB > radio",
+      "#blockCookiesLabel",
+      "#blockCookiesMenu",
     ];
 
     await doDependentControlChecks(dependentControls, alwaysDisabledControls);
   }
 
-  // The rest of the values
+  // For other choices of cookie policies, no parts of the UI should get disabled
+  // unconditionally.
   prefValuesToTest = [
     Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN, // Block All Third-Party Cookies
     Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER, // Block Cookies from third-party trackers
   ];
   for (let value of prefValuesToTest) {
     await SpecialPowers.pushPrefEnv({set: [
       [CB_UI_PREF, true],
       [CB_FB_UI_PREF, true],
@@ -472,16 +544,21 @@ add_task(async function testContentBlock
     let dependentControls = [
       "#content-blocking-categories-label",
       ".content-blocking-checkbox",
       "#trackingProtectionMenu",
       "[control=trackingProtectionMenu]",
       "#changeBlockListLink",
       "#contentBlockingChangeCookieSettings",
       "#blockCookiesCB, #blockCookiesCB > radio",
+      "#blockCookies, #blockCookies > radio",
+      "#blockCookiesLabel",
+      "#blockCookiesMenu",
+      "#keepUntil",
+      "#keepCookiesUntil",
     ];
 
     await doDependentControlChecks(dependentControls);
   }
 });
 
 
 // Checks that the warnings in the Content Blocking Third-Party Cookies section correctly appear based on
--- a/browser/components/preferences/in-content/tests/siteData/browser_siteData_multi_select.js
+++ b/browser/components/preferences/in-content/tests/siteData/browser_siteData_multi_select.js
@@ -63,22 +63,33 @@ add_task(async function() {
   fakeHosts.slice(0, 2).forEach(host => {
     let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
     sitesList.addItemToSelection(site);
   });
 
   is(removeBtn.disabled, false, "Should enable the removeSelected button");
   removeBtn.doCommand();
   is(sitesList.selectedIndex, 0, "Should select next item");
+  assertSitesListed(doc, fakeHosts.slice(2));
+
+  // Select some other sites to remove with Delete.
+  fakeHosts.slice(2, 4).forEach(host => {
+    let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
+    sitesList.addItemToSelection(site);
+  });
+
+  is(removeBtn.disabled, false, "Should enable the removeSelected button");
+  EventUtils.synthesizeKey("VK_DELETE");
+  is(sitesList.selectedIndex, 0, "Should select next item");
+  assertSitesListed(doc, fakeHosts.slice(4));
 
   let saveBtn = frameDoc.getElementById("save");
-  assertSitesListed(doc, fakeHosts.slice(2));
   saveBtn.doCommand();
 
   await removeDialogOpenPromise;
   await settingsDialogClosePromise;
   await openSiteDataSettingsDialog();
 
-  assertSitesListed(doc, fakeHosts.slice(2));
+  assertSitesListed(doc, fakeHosts.slice(4));
 
   await mockSiteDataManager.unregister();
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
--- a/browser/components/preferences/siteDataSettings.js
+++ b/browser/components/preferences/siteDataSettings.js
@@ -1,12 +1,13 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "SiteDataManager",
                                "resource:///modules/SiteDataManager.jsm");
 ChromeUtils.defineModuleGetter(this, "DownloadUtils",
                                "resource://gre/modules/DownloadUtils.jsm");
 
@@ -109,17 +110,17 @@ let gSiteDataSettings = {
     setEventListener("hostCol", "click", this.onClickTreeCol);
     setEventListener("usageCol", "click", this.onClickTreeCol);
     setEventListener("lastAccessedCol", "click", this.onClickTreeCol);
     setEventListener("cookiesCol", "click", this.onClickTreeCol);
     setEventListener("cancel", "command", this.close);
     setEventListener("save", "command", this.saveChanges);
     setEventListener("searchBox", "command", this.onCommandSearch);
     setEventListener("removeAll", "command", this.onClickRemoveAll);
-    setEventListener("removeSelected", "command", this.onClickRemoveSelected);
+    setEventListener("removeSelected", "command", this.removeSelected);
   },
 
   _updateButtonsState() {
     let items = this._list.getElementsByTagName("richlistitem");
     let removeSelectedBtn = document.getElementById("removeSelected");
     let removeAllBtn = document.getElementById("removeAll");
     removeSelectedBtn.disabled = this._list.selectedItems.length == 0;
     removeAllBtn.disabled = items.length == 0;
@@ -248,52 +249,56 @@ let gSiteDataSettings = {
       this.close();
     }
   },
 
   close() {
     window.close();
   },
 
-  onClickTreeCol(e) {
-    this._sortSites(this._sites, e.target);
-    this._buildSitesList(this._sites);
-    this._list.clearSelection();
-  },
-
-  onCommandSearch() {
-    this._buildSitesList(this._sites);
-    this._list.clearSelection();
-  },
-
-  onClickRemoveSelected() {
+  removeSelected() {
     let lastIndex = this._list.selectedItems.length - 1;
     let lastSelectedItem = this._list.selectedItems[lastIndex];
     let lastSelectedItemPosition = this._list.getIndexOfItem(lastSelectedItem);
     let nextSelectedItem = this._list.getItemAtIndex(lastSelectedItemPosition + 1);
 
     this._removeSiteItems(this._list.selectedItems);
     this._list.clearSelection();
 
     if (nextSelectedItem) {
       this._list.selectedItem = nextSelectedItem;
     } else {
       this._list.selectedIndex = this._list.itemCount - 1;
     }
   },
 
+  onClickTreeCol(e) {
+    this._sortSites(this._sites, e.target);
+    this._buildSitesList(this._sites);
+    this._list.clearSelection();
+  },
+
+  onCommandSearch() {
+    this._buildSitesList(this._sites);
+    this._list.clearSelection();
+  },
+
   onClickRemoveAll() {
     let siteItems = this._list.getElementsByTagName("richlistitem");
     if (siteItems.length > 0) {
       this._removeSiteItems(siteItems);
     }
   },
 
   onKeyPress(e) {
     if (e.keyCode == KeyEvent.DOM_VK_ESCAPE) {
       this.close();
+    } else if (e.keyCode == KeyEvent.DOM_VK_DELETE ||
+               (AppConstants.platform == "macosx" &&
+                e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+      this.removeSelected();
     }
   },
 
   onSelect() {
     this._updateButtonsState();
   },
 };
--- a/browser/config/mozconfigs/macosx64/beta
+++ b/browser/config/mozconfigs/macosx64/beta
@@ -2,9 +2,11 @@ if [ -n "$ENABLE_RELEASE_PROMOTION" ]; t
   MOZ_AUTOMATION_UPDATE_PACKAGING=1
 fi
 
 . "$topsrcdir/browser/config/mozconfigs/macosx64/common-opt"
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
+ac_add_options --enable-lto
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/macosx64/nightly
+++ b/browser/config/mozconfigs/macosx64/nightly
@@ -4,11 +4,13 @@ ac_add_options --disable-install-strip
 ac_add_options --enable-verify-mar
 ac_add_options --enable-instruments
 
 # Cross-compiled builds fail when dtrace is enabled
 if test `uname -s` != Linux; then
   ac_add_options --enable-dtrace
 fi
 
+ac_add_options --enable-lto
+
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/macosx64/release
+++ b/browser/config/mozconfigs/macosx64/release
@@ -5,13 +5,15 @@ if [ -n "$ENABLE_RELEASE_PROMOTION" ]; t
   MOZ_AUTOMATION_UPDATE_PACKAGING=1
 fi
 
 . "$topsrcdir/browser/config/mozconfigs/macosx64/common-opt"
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
+ac_add_options --enable-lto
+
 # safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
 # defines.sh during the beta cycle
 export BUILDING_RELEASE=1
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -430,16 +430,21 @@ button > hbox > label {
   justify-content: space-between;
 }
 
 #blockCookies,
 #keepRow {
   margin-top: 1.5em;
 }
 
+#blockCookiesWarning {
+  margin-top: 1.5em !important;
+  margin-bottom: -1em !important;
+}
+
 /* Collapse the non-active vboxes in decks to use only the height the
    active vbox needs */
 #historyPane:not([selectedIndex="1"]) > #historyDontRememberPane,
 #historyPane:not([selectedIndex="2"]) > #historyCustomPane,
 #weavePrefsDeck:not([selectedIndex="1"]) > #hasFxaAccount,
 #fxaLoginStatus:not([selectedIndex="1"]) > #fxaLoginUnverified,
 #fxaLoginStatus:not([selectedIndex="2"]) > #fxaLoginRejected {
   visibility: collapse;
--- a/browser/themes/shared/incontentprefs/privacy.css
+++ b/browser/themes/shared/incontentprefs/privacy.css
@@ -142,18 +142,19 @@
   margin-top: 1em !important;
 }
 
 .content-blocking-category-description {
   font-size: 90%;
   opacity: 0.6;
 }
 
-.reject-trackers-warning-icon {
+.warning-description {
   background: url(chrome://browser/skin/controlcenter/warning.svg) no-repeat 0 5px;
+  opacity: 0.6;
   -moz-context-properties: fill, stroke;
   fill: #d7b600;
   stroke: white;
   padding-inline-start: 20px;
 }
 
 .reject-trackers-warning-icon:-moz-locale-dir(rtl) {
   background-position-x: right 0;
--- a/build.gradle
+++ b/build.gradle
@@ -54,17 +54,17 @@ buildscript {
     ext.support_library_version = '26.1.0'
     ext.jacoco_version = '0.8.1'
 
     if (gradle.mozconfig.substs.MOZ_ANDROID_GOOGLE_PLAY_SERVICES) {
         ext.google_play_services_version = '15.0.1'
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.1.0'
+        classpath 'com.android.tools.build:gradle:3.1.4'
         classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.2'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
     }
 }
 
 if ('multi' == System.env.AB_CD) {
     // Multi-l10n builds set `AB_CD=multi`, which isn't a valid locale.  This
     // causes the
@@ -141,17 +141,17 @@ afterEvaluate {
         }
     }
 }
 
 apply plugin: 'idea'
 
 idea {
     project {
-        languageLevel = '1.7'
+        languageLevel = '1.8'
     }
 
     module {
         // Object directories take a huge amount of time for IntelliJ to index.
         // Exclude them.  Convention is that object directories start with obj.
         // IntelliJ is clever and will not exclude the parts of the object
         // directory that are referenced, if there are any.  In practice,
         // indexing the entirety of the tree is taking too long, so exclude all
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1624,30 +1624,25 @@ option('--enable-gold',
 imply_option('--enable-linker', 'gold', when='--enable-gold')
 
 js_option('--enable-linker', nargs=1,
           help='Select the linker {bfd, gold, ld64, lld, lld-*}',
           when=is_linker_option_enabled)
 
 
 @depends('--enable-linker', c_compiler, developer_options, '--enable-gold',
-         extra_toolchain_flags, target, lto.enabled,
-         when=is_linker_option_enabled)
+         extra_toolchain_flags, target, when=is_linker_option_enabled)
 @checking('for linker', lambda x: x.KIND)
 @imports('os')
 @imports('shutil')
 def select_linker(linker, c_compiler, developer_options, enable_gold,
-                  toolchain_flags, target, lto):
+                  toolchain_flags, target):
 
     if linker:
         linker = linker[0]
-    elif lto and c_compiler.type == 'clang' and target.kernel != 'Darwin':
-        # If no linker was explicitly given, and building with clang for non-macOS,
-        # prefer lld. For macOS, we prefer ld64, or whatever the default linker is.
-        linker = 'lld'
     else:
         linker = None
 
     def is_valid_linker(linker):
         if target.kernel == 'Darwin':
             valid_linkers = ('ld64', 'lld')
         else:
             valid_linkers = ('bfd', 'gold', 'lld')
--- a/devtools/client/inspector/flexbox/components/FlexContainer.js
+++ b/devtools/client/inspector/flexbox/components/FlexContainer.js
@@ -1,18 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { PureComponent } = require("devtools/client/shared/vendor/react");
+const { createRef, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 const { translateNodeFrontToGrip } = require("devtools/client/inspector/shared/utils");
 
 // Reps
 const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
 const { Rep } = REPS;
 const ElementNode = REPS.ElementNode;
 
 const Types = require("../types");
@@ -28,52 +27,53 @@ class FlexContainer extends PureComponen
       onToggleFlexboxHighlighter: PropTypes.func.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
+    this.colorValueEl = createRef();
+    this.swatchEl = createRef();
+
     this.onFlexboxCheckboxClick = this.onFlexboxCheckboxClick.bind(this);
     this.onFlexboxInspectIconClick = this.onFlexboxInspectIconClick.bind(this);
     this.setFlexboxColor = this.setFlexboxColor.bind(this);
   }
 
   componentDidMount() {
     const {
       flexbox,
       getSwatchColorPickerTooltip,
       onSetFlexboxOverlayColor,
     } = this.props;
 
-    const swatchEl = findDOMNode(this).querySelector(".flexbox-color-swatch");
     const tooltip = getSwatchColorPickerTooltip();
 
     let previousColor;
-    tooltip.addSwatch(swatchEl, {
+    tooltip.addSwatch(this.swatchEl.current, {
       onCommit: this.setFlexboxColor,
       onPreview: this.setFlexboxColor,
       onRevert: () => {
         onSetFlexboxOverlayColor(previousColor);
       },
       onShow: () => {
         previousColor = flexbox.color;
       },
     });
   }
 
   componentWillUnMount() {
-    const swatchEl = findDOMNode(this).querySelector(".flexbox-color-swatch");
     const tooltip = this.props.getSwatchColorPickerTooltip();
-    tooltip.removeSwatch(swatchEl);
+    tooltip.removeSwatch(this.swatchEl.current);
   }
 
   setFlexboxColor() {
-    const color = findDOMNode(this).querySelector(".flexbox-color-value").textContent;
+    const color = this.colorValueEl.current.textContent;
     this.props.onSetFlexboxOverlayColor(color);
   }
 
   onFlexboxCheckboxClick(e) {
     // If the click was on the svg icon to select the node in the inspector, bail out.
     const originalTarget = e.nativeEvent && e.nativeEvent.explicitOriginalTarget;
     if (originalTarget && originalTarget.namespaceURI === "http://www.w3.org/2000/svg") {
       // We should be able to cancel the click event propagation after the following reps
@@ -127,26 +127,33 @@ class FlexContainer extends PureComponen
               onDOMNodeMouseOut: () => onHideBoxModelHighlighter(),
               onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront),
               onInspectIconClick: () => this.onFlexboxInspectIconClick(nodeFront),
             }
           )
         ),
         dom.div(
           {
-            className: "flexbox-color-swatch",
+            className: "layout-color-swatch",
+            ref: this.swatchEl,
             style: {
               backgroundColor: color,
             },
             title: color,
           }
         ),
         // The SwatchColorPicker relies on the nextSibling of the swatch element to apply
         // the selected color. This is why we use a span in display: none for now.
         // Ideally we should modify the SwatchColorPickerTooltip to bypass this
         // requirement. See https://bugzilla.mozilla.org/show_bug.cgi?id=1341578
-        dom.span({ className: "flexbox-color-value" }, color)
+        dom.span(
+          {
+            className: "layout-color-value",
+            ref: this.colorValueEl,
+          },
+          color
+        )
       )
     );
   }
 }
 
 module.exports = FlexContainer;
--- a/devtools/client/inspector/grids/components/GridItem.js
+++ b/devtools/client/inspector/grids/components/GridItem.js
@@ -1,18 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { PureComponent } = require("devtools/client/shared/vendor/react");
+const { createRef, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 const { translateNodeFrontToGrip } = require("devtools/client/inspector/shared/utils");
 
 // Reps
 const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
 const { Rep } = REPS;
 const ElementNode = REPS.ElementNode;
 
 const Types = require("../types");
@@ -28,46 +27,47 @@ class GridItem extends PureComponent {
       onToggleGridHighlighter: PropTypes.func.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
+    this.colorValueEl = createRef();
+    this.swatchEl = createRef();
+
     this.onGridCheckboxClick = this.onGridCheckboxClick.bind(this);
     this.onGridInspectIconClick = this.onGridInspectIconClick.bind(this);
     this.setGridColor = this.setGridColor.bind(this);
   }
 
   componentDidMount() {
-    const swatchEl = findDOMNode(this).querySelector(".grid-color-swatch");
     const tooltip = this.props.getSwatchColorPickerTooltip();
 
     let previousColor;
-    tooltip.addSwatch(swatchEl, {
+    tooltip.addSwatch(this.swatchEl.current, {
       onCommit: this.setGridColor,
       onPreview: this.setGridColor,
       onRevert: () => {
         this.props.onSetGridOverlayColor(this.props.grid.nodeFront, previousColor);
       },
       onShow: () => {
         previousColor = this.props.grid.color;
       },
     });
   }
 
   componentWillUnmount() {
-    const swatchEl = findDOMNode(this).querySelector(".grid-color-swatch");
     const tooltip = this.props.getSwatchColorPickerTooltip();
-    tooltip.removeSwatch(swatchEl);
+    tooltip.removeSwatch(this.swatchEl.current);
   }
 
   setGridColor() {
-    const color = findDOMNode(this).querySelector(".grid-color-value").textContent;
+    const color = this.colorValueEl.current.textContent;
     this.props.onSetGridOverlayColor(this.props.grid.nodeFront, color);
   }
 
   onGridCheckboxClick(e) {
     // If the click was on the svg icon to select the node in the inspector, bail out.
     const originalTarget = e.nativeEvent && e.nativeEvent.explicitOriginalTarget;
     if (originalTarget && originalTarget.namespaceURI === "http://www.w3.org/2000/svg") {
       // We should be able to cancel the click event propagation after the following reps
@@ -115,26 +115,33 @@ class GridItem extends PureComponent {
             object: translateNodeFrontToGrip(nodeFront),
             onDOMNodeMouseOut: () => onHideBoxModelHighlighter(),
             onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront),
             onInspectIconClick: () => this.onGridInspectIconClick(nodeFront),
           })
         ),
         dom.div(
           {
-            className: "grid-color-swatch",
+            className: "layout-color-swatch",
+            ref: this.swatchEl,
             style: {
               backgroundColor: grid.color,
             },
             title: grid.color,
           }
         ),
         // The SwatchColorPicker relies on the nextSibling of the swatch element to apply
         // the selected color. This is why we use a span in display: none for now.
         // Ideally we should modify the SwatchColorPickerTooltip to bypass this
         // requirement. See https://bugzilla.mozilla.org/show_bug.cgi?id=1341578
-        dom.span({ className: "grid-color-value" }, grid.color)
+        dom.span(
+          {
+            className: "layout-color-value",
+            ref: this.colorValueEl,
+          },
+          grid.color
+        )
       )
     );
   }
 }
 
 module.exports = GridItem;
--- a/devtools/client/inspector/grids/test/browser_grids_color-in-rules-grid-toggle.js
+++ b/devtools/client/inspector/grids/test/browser_grids_color-in-rules-grid-toggle.js
@@ -20,17 +20,17 @@ const TEST_URI = `
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector, gridInspector, layoutView } = await openLayoutView();
   const { document: doc } = gridInspector;
   const { store } = inspector;
   const cPicker = layoutView.swatchColorPickerTooltip;
   const spectrum = cPicker.spectrum;
-  const swatch = doc.querySelector(".grid-color-swatch");
+  const swatch = doc.querySelector("#layout-grid-container .layout-color-swatch");
 
   info("Scrolling into view of the #grid color swatch.");
   swatch.scrollIntoView();
 
   info("Opening the color picker by clicking on the #grid color swatch.");
   const onColorPickerReady = cPicker.once("ready");
   swatch.click();
   await onColorPickerReady;
--- a/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-ESC.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-ESC.js
@@ -20,17 +20,17 @@ const TEST_URI = `
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector, gridInspector, layoutView } = await openLayoutView();
   const { document: doc } = gridInspector;
   const { store } = inspector;
   const cPicker = layoutView.swatchColorPickerTooltip;
   const spectrum = cPicker.spectrum;
-  const swatch = doc.querySelector(".grid-color-swatch");
+  const swatch = doc.querySelector("#layout-grid-container .layout-color-swatch");
 
   info("Checking the initial state of the Grid Inspector.");
   is(swatch.style.backgroundColor, "rgb(148, 0, 255)",
     "The color swatch's background is correct.");
   is(store.getState().grids[0].color, "#9400FF", "The grid color state is correct.");
 
   info("Scrolling into view of the #grid color swatch.");
   swatch.scrollIntoView();
--- a/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-RETURN.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-color-picker-on-RETURN.js
@@ -20,17 +20,17 @@ const TEST_URI = `
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector, gridInspector, layoutView } = await openLayoutView();
   const { document: doc } = gridInspector;
   const { store } = inspector;
   const cPicker = layoutView.swatchColorPickerTooltip;
   const spectrum = cPicker.spectrum;
-  const swatch = doc.querySelector(".grid-color-swatch");
+  const swatch = doc.querySelector("#layout-grid-container .layout-color-swatch");
 
   info("Checking the initial state of the Grid Inspector.");
   is(swatch.style.backgroundColor, "rgb(148, 0, 255)",
     "The color swatch's background is correct.");
   is(store.getState().grids[0].color, "#9400FF", "The grid color state is correct.");
 
   info("Scrolling into view of the #grid color swatch.");
   swatch.scrollIntoView();
--- a/devtools/client/inspector/grids/test/browser_grids_persist-color-palette.js
+++ b/devtools/client/inspector/grids/test/browser_grids_persist-color-palette.js
@@ -19,17 +19,17 @@ const TEST_URI = `
 `;
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector, gridInspector, layoutView, toolbox } = await openLayoutView();
   const { document: doc } = gridInspector;
   const { store } = inspector;
   const cPicker = layoutView.swatchColorPickerTooltip;
-  const swatch = doc.querySelector(".grid-color-swatch");
+  const swatch = doc.querySelector("#layout-grid-container .layout-color-swatch");
 
   info("Scrolling into view of the #grid color swatch.");
   swatch.scrollIntoView();
 
   info("Opening the color picker by clicking on the #grid color swatch.");
   const onColorPickerReady = cPicker.once("ready");
   swatch.click();
   await onColorPickerReady;
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -9,59 +9,48 @@
   min-width: 200px;
 }
 
 #layout-container .accordion ._content {
   padding: 0;
 }
 
 /**
- * Common styles for shared components
+ * Common styles for the layout container
  */
 
-.grid-container {
-  display: flex;
-  flex-direction: column;
-  flex: 1 auto;
-  min-width: 140px;
-  margin-inline-start: 16px;
-}
-
-.grid-container:first-child {
-  margin-bottom: 10px;
-}
-
-.grid-container > span {
-  font-weight: 600;
-  margin-bottom: 5px;
-  pointer-events: none;
-}
-
-.grid-container > ul {
-  list-style: none;
-  margin: 0;
-  padding: 0;
-}
-
 #layout-container li {
   padding: 3px 0;
   -moz-user-select: none;
 }
 
-.flex-container input,
-.grid-container input {
+#layout-container input {
   margin-inline-end: 7px;
   vertical-align: middle;
 }
 
-.flex-container label,
-.grid-container label {
+#layout-container label {
   margin-inline-start: -3px;
 }
 
+.layout-color-swatch {
+  width: 12px;
+  height: 12px;
+  margin-inline-start: -1px;
+  border: 1px solid var(--theme-highlight-gray);
+  border-radius: 50%;
+  cursor: pointer;
+  display: inline-block;
+  vertical-align: middle;
+}
+
+.layout-color-value {
+  display: none;
+}
+
 /* Layout Properties: Common styles used for the Box Model and Flexbox Properties */
 
 .layout-properties-header {
   font-size: 12px;
   padding: 2px 3px;
   -moz-user-select: none;
 }
 
@@ -86,25 +75,25 @@
   flex: 1;
 }
 
 .layout-properties-wrapper .computed-property-value-container {
   flex: 1;
   display: block;
 }
 
+/**
+ * Flex Container
+ */
+
 #layout-flexbox-container {
   display: flex;
   flex-direction: column;
 }
 
-/**
- * Flex Container
- */
-
 .flex-container {
   border-bottom: 1px solid var(--theme-splitter-color);
   padding: 5px 0;
   padding-inline-start: 20px;
 }
 
 /**
  * Flex Item List
@@ -149,16 +138,40 @@
  */
 
 #layout-grid-container {
   display: flex;
   flex-direction: column;
   padding: 5px;
 }
 
+.grid-container {
+  display: flex;
+  flex-direction: column;
+  flex: 1 auto;
+  min-width: 140px;
+  margin-inline-start: 16px;
+}
+
+.grid-container:first-child {
+  margin-bottom: 10px;
+}
+
+.grid-container > span {
+  font-weight: 600;
+  margin-bottom: 5px;
+  pointer-events: none;
+}
+
+.grid-container > ul {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+
 /**
  * Grid Content
  */
 
 .grid-content {
   display: flex;
   flex-wrap: wrap;
   flex: 1;
@@ -211,35 +224,14 @@
 .grid-outline-text-icon {
   background: url("chrome://devtools/skin/images/sad-face.svg");
   margin-inline-end: 5px;
   width: 16px;
   height: 16px;
 }
 
 /**
- * Flexbox and Grid Item
- */
-
-.flexbox-color-swatch,
-.grid-color-swatch {
-  width: 12px;
-  height: 12px;
-  margin-inline-start: -1px;
-  border: 1px solid var(--theme-highlight-gray);
-  border-radius: 50%;
-  cursor: pointer;
-  display: inline-block;
-  vertical-align: middle;
-}
-
-.flexbox-color-value,
-.grid-color-value {
-  display: none;
-}
-
-/**
  * Settings Item
  */
 
 .grid-settings-item label {
   line-height: 16px;
 }
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ChromeUtils.h"
 
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/SavedFrameAPI.h"
 #include "jsfriendapi.h"
 #include "WrapperFactory.h"
 
 #include "mozilla/Base64.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/PerformanceMetricsCollector.h"
@@ -462,21 +462,21 @@ namespace module_getter {
     JS::Rooted<JSObject*> thisObj(aCx);
     JS::Rooted<jsid> id(aCx);
     if (!ExtractArgs(aCx, args, &callee, &thisObj, &id)) {
       return false;
     }
 
     JS::Rooted<JSString*> moduleURI(
       aCx, js::GetFunctionNativeReserved(callee, SLOT_URI).toString());
-    JSAutoByteString bytes;
-    if (!bytes.encodeUtf8(aCx, moduleURI)) {
+    JS::UniqueChars bytes = JS_EncodeStringToUTF8(aCx, moduleURI);
+    if (!bytes) {
       return false;
     }
-    nsDependentCString uri(bytes.ptr());
+    nsDependentCString uri(bytes.get());
 
     RefPtr<mozJSComponentLoader> moduleloader = mozJSComponentLoader::Get();
     MOZ_ASSERT(moduleloader);
 
     JS::Rooted<JSObject*> moduleGlobal(aCx);
     JS::Rooted<JSObject*> moduleExports(aCx);
     nsresult rv = moduleloader->Import(aCx, uri, &moduleGlobal, &moduleExports);
     if (NS_FAILED(rv)) {
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -14,17 +14,16 @@
  * the generated code itself.
  */
 
 #include "mozilla/Assertions.h"
 
 #include "GeckoProfiler.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
-#include "js/AutoByteString.h"
 #include "js/Conversions.h"
 #include "js/StableStringChars.h"
 #include "nsString.h"
 
 class nsIScriptContext;
 class nsIScriptElement;
 class nsIScriptGlobalObject;
 class nsXBLPrototypeBinding;
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_BindingUtils_h__
 #define mozilla_dom_BindingUtils_h__
 
 #include "jsfriendapi.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/Wrapper.h"
 #include "js/Conversions.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Alignment.h"
 #include "mozilla/Array.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DeferredFinalize.h"
 #include "mozilla/dom/BindingDeclarations.h"
@@ -1308,22 +1308,22 @@ EnumValueNotFound<false>(JSContext* cx, 
   return true;
 }
 
 template<>
 inline bool
 EnumValueNotFound<true>(JSContext* cx, JS::HandleString str, const char* type,
                         const char* sourceDescription)
 {
-  JSAutoByteString deflated;
-  if (!deflated.encodeUtf8(cx, str)) {
+  JS::UniqueChars deflated = JS_EncodeStringToUTF8(cx, str);
+  if (!deflated) {
     return false;
   }
   return ThrowErrorMessage(cx, MSG_INVALID_ENUM_VALUE, sourceDescription,
-                           deflated.ptr(), type);
+                           deflated.get(), type);
 }
 
 template<typename CharT>
 inline int
 FindEnumStringIndexImpl(const CharT* chars, size_t length, const EnumEntry* values)
 {
   int i = 0;
   for (const EnumEntry* value = values; value->value; ++value, ++i) {
--- a/dom/bindings/CallbackInterface.cpp
+++ b/dom/bindings/CallbackInterface.cpp
@@ -1,35 +1,35 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/CallbackInterface.h"
 #include "jsapi.h"
+#include "js/CharacterEncoding.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 namespace dom {
 
 bool
 CallbackInterface::GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
                                        JS::MutableHandle<JS::Value> aCallable)
 {
   if (!JS_GetPropertyById(cx, CallbackKnownNotGray(), aPropId, aCallable)) {
     return false;
   }
   if (!aCallable.isObject() ||
       !JS::IsCallable(&aCallable.toObject())) {
-    char* propName =
-      JS_EncodeString(cx, JS_FORGET_STRING_FLATNESS(JSID_TO_FLAT_STRING(aPropId)));
-    nsPrintfCString description("Property '%s'", propName);
-    JS_free(cx, propName);
+    JS::RootedString propId(cx, JS_FORGET_STRING_FLATNESS(JSID_TO_FLAT_STRING(aPropId)));
+    JS::UniqueChars propName = JS_EncodeStringToUTF8(cx, propId);
+    nsPrintfCString description("Property '%s'", propName.get());
     ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get());
     return false;
   }
 
   return true;
 }
 
 } // namespace dom
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -63,28 +63,28 @@ typedef struct {
     AutoSwap_PRUint32 startGlyphId;
 } Format12Group;
 
 #pragma pack()
 
 void
 gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const
 {
-    NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad");
-    uint32_t b, numBlocks = mBlocks.Length();
+    uint32_t numBlocks = mBlockIndex.Length();
 
-    for (b = 0; b < numBlocks; b++) {
-        Block *block = mBlocks[b].get();
-        if (!block) {
+    for (uint32_t b = 0; b < numBlocks; b++) {
+        if (mBlockIndex[b] == NO_BLOCK) {
             continue;
         }
+        const Block* block = &mBlocks[mBlockIndex[b]];
         const int BUFSIZE = 256;
         char outStr[BUFSIZE];
         int index = 0;
-        index += snprintf(&outStr[index], BUFSIZE - index, "%s u+%6.6x [", aPrefix, (b << BLOCK_INDEX_SHIFT));
+        index += snprintf(&outStr[index], BUFSIZE - index, "%s u+%6.6x [",
+                          aPrefix, (b * BLOCK_SIZE_BITS));
         for (int i = 0; i < 32; i += 4) {
             for (int j = i; j < i + 4; j++) {
                 uint8_t bits = block->mBits[j];
                 uint8_t flip1 = ((bits & 0xaa) >> 1) | ((bits & 0x55) << 1);
                 uint8_t flip2 = ((flip1 & 0xcc) >> 2) | ((flip1 & 0x33) << 2);
                 uint8_t flipped = ((flip2 & 0xf0) >> 4) | ((flip2 & 0x0f) << 4);
 
                 index += snprintf(&outStr[index], BUFSIZE - index, "%2.2x", flipped);
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -26,306 +26,290 @@
 #endif
 
 typedef struct hb_blob_t hb_blob_t;
 
 class gfxSparseBitSet {
 private:
     enum { BLOCK_SIZE = 32 };   // ==> 256 codepoints per block
     enum { BLOCK_SIZE_BITS = BLOCK_SIZE * 8 };
-    enum { BLOCK_INDEX_SHIFT = 8 };
+    enum { NO_BLOCK = 0xffff }; // index value indicating missing (empty) block
 
     struct Block {
         Block(const Block& aBlock) { memcpy(mBits, aBlock.mBits, sizeof(mBits)); }
         explicit Block(unsigned char memsetValue = 0) { memset(mBits, memsetValue, BLOCK_SIZE); }
         uint8_t mBits[BLOCK_SIZE];
     };
 
 public:
     gfxSparseBitSet() { }
     gfxSparseBitSet(const gfxSparseBitSet& aBitset) {
-        uint32_t len = aBitset.mBlocks.Length();
-        mBlocks.AppendElements(len);
-        for (uint32_t i = 0; i < len; ++i) {
-            Block *block = aBitset.mBlocks[i].get();
-            if (block) {
-                mBlocks[i] = mozilla::MakeUnique<Block>(*block);
-            }
-        }
+        mBlockIndex.AppendElements(aBitset.mBlockIndex);
+        mBlocks.AppendElements(aBitset.mBlocks);
     }
 
-    bool Equals(const gfxSparseBitSet *aOther) const {
-        if (mBlocks.Length() != aOther->mBlocks.Length()) {
+    bool Equals(const gfxSparseBitSet* aOther) const {
+        if (mBlockIndex.Length() != aOther->mBlockIndex.Length()) {
             return false;
         }
-        size_t n = mBlocks.Length();
+        size_t n = mBlockIndex.Length();
         for (size_t i = 0; i < n; ++i) {
-            const Block *b1 = mBlocks[i].get();
-            const Block *b2 = aOther->mBlocks[i].get();
-            if (!b1 != !b2) {
+            uint32_t b1 = mBlockIndex[i];
+            uint32_t b2 = aOther->mBlockIndex[i];
+            if ((b1 == NO_BLOCK) != (b2 == NO_BLOCK)) {
                 return false;
             }
-            if (!b1) {
+            if (b1 == NO_BLOCK) {
                 continue;
             }
-            if (memcmp(&b1->mBits, &b2->mBits, BLOCK_SIZE) != 0) {
+            if (memcmp(&mBlocks[b1].mBits, &aOther->mBlocks[b2].mBits,
+                       BLOCK_SIZE) != 0) {
                 return false;
             }
         }
         return true;
     }
 
     bool test(uint32_t aIndex) const {
-        NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad");
-        uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS;
-        if (blockIndex >= mBlocks.Length()) {
+        uint32_t i = aIndex / BLOCK_SIZE_BITS;
+        if (i >= mBlockIndex.Length() || mBlockIndex[i] == NO_BLOCK) {
             return false;
         }
-        const Block *block = mBlocks[blockIndex].get();
-        if (!block) {
-            return false;
-        }
-        return ((block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)]) & (1 << (aIndex & 0x7))) != 0;
+        const Block& block = mBlocks[mBlockIndex[i]];
+        return ((block.mBits[(aIndex>>3) & (BLOCK_SIZE - 1)]) & (1 << (aIndex & 0x7))) != 0;
     }
 
     // dump out contents of bitmap
     void Dump(const char* aPrefix, eGfxLog aWhichLog) const;
 
     bool TestRange(uint32_t aStart, uint32_t aEnd) {
-        uint32_t startBlock, endBlock, blockLen;
-        
         // start point is beyond the end of the block array? return false immediately
-        startBlock = aStart >> BLOCK_INDEX_SHIFT;
-        blockLen = mBlocks.Length();
-        if (startBlock >= blockLen) return false;
-        
+        uint32_t startBlock = aStart / BLOCK_SIZE_BITS;
+        uint32_t blockLen = mBlockIndex.Length();
+        if (startBlock >= blockLen) {
+            return false;
+        }
+
         // check for blocks in range, if none, return false
-        uint32_t blockIndex;
         bool hasBlocksInRange = false;
-
-        endBlock = aEnd >> BLOCK_INDEX_SHIFT;
-        for (blockIndex = startBlock; blockIndex <= endBlock; blockIndex++) {
-            if (blockIndex < blockLen && mBlocks[blockIndex]) {
+        uint32_t endBlock = aEnd / BLOCK_SIZE_BITS;
+        for (uint32_t bi = startBlock; bi <= endBlock; bi++) {
+            if (bi < blockLen && mBlockIndex[bi] != NO_BLOCK) {
                 hasBlocksInRange = true;
+                break;
             }
         }
         if (!hasBlocksInRange) {
             return false;
         }
 
-        Block *block;
-        uint32_t i, start, end;
-        
         // first block, check bits
-        if ((block = mBlocks[startBlock].get())) {
-            start = aStart;
-            end = std::min(aEnd, ((startBlock+1) << BLOCK_INDEX_SHIFT) - 1);
-            for (i = start; i <= end; i++) {
-                if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7))) {
+        if (mBlockIndex[startBlock] != NO_BLOCK) {
+            const Block& block = mBlocks[mBlockIndex[startBlock]];
+            uint32_t start = aStart;
+            uint32_t end = std::min(aEnd, ((startBlock + 1) * BLOCK_SIZE_BITS) - 1);
+            for (uint32_t i = start; i <= end; i++) {
+                if ((block.mBits[(i >> 3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7))) {
                     return true;
                 }
             }
         }
         if (endBlock == startBlock) {
             return false;
         }
 
         // [2..n-1] blocks check bytes
-        for (blockIndex = startBlock + 1; blockIndex < endBlock; blockIndex++) {
-            uint32_t index;
-            
-            if (blockIndex >= blockLen ||
-                !(block = mBlocks[blockIndex].get())) {
+        for (uint32_t i = startBlock + 1; i < endBlock; i++) {
+            if (i >= blockLen || mBlockIndex[i] == NO_BLOCK) {
                 continue;
             }
-            for (index = 0; index < BLOCK_SIZE; index++) {
-                if (block->mBits[index]) {
+            const Block& block = mBlocks[mBlockIndex[i]];
+            for (uint32_t index = 0; index < BLOCK_SIZE; index++) {
+                if (block.mBits[index]) {
                     return true;
                 }
             }
         }
-        
+
         // last block, check bits
-        if (endBlock < blockLen && (block = mBlocks[endBlock].get())) {
-            start = endBlock << BLOCK_INDEX_SHIFT;
-            end = aEnd;
-            for (i = start; i <= end; i++) {
-                if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7))) {
+        if (endBlock < blockLen && mBlockIndex[endBlock] != NO_BLOCK) {
+            const Block& block = mBlocks[mBlockIndex[endBlock]];
+            uint32_t start = endBlock * BLOCK_SIZE_BITS;
+            uint32_t end = aEnd;
+            for (uint32_t i = start; i <= end; i++) {
+                if ((block.mBits[(i >> 3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7))) {
                     return true;
                 }
             }
         }
-        
+
         return false;
     }
     
     void set(uint32_t aIndex) {
-        uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS;
-        if (blockIndex >= mBlocks.Length()) {
-            mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
+        uint32_t i = aIndex / BLOCK_SIZE_BITS;
+        while (i >= mBlockIndex.Length()) {
+            mBlockIndex.AppendElement(NO_BLOCK);
         }
-        Block *block = mBlocks[blockIndex].get();
-        if (!block) {
-            block = new Block;
-            mBlocks[blockIndex].reset(block);
+        if (mBlockIndex[i] == NO_BLOCK) {
+            mBlocks.AppendElement();
+            MOZ_ASSERT(mBlocks.Length() < 0xffff, "block index overflow!");
+            mBlockIndex[i] = mBlocks.Length() - 1;
         }
-        block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
+        Block& block = mBlocks[mBlockIndex[i]];
+        block.mBits[(aIndex >> 3) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
     }
 
     void set(uint32_t aIndex, bool aValue) {
-        if (aValue)
+        if (aValue) {
             set(aIndex);
-        else
+        } else {
             clear(aIndex);
+        }
     }
 
     void SetRange(uint32_t aStart, uint32_t aEnd) {
-        const uint32_t startIndex = aStart/BLOCK_SIZE_BITS;
-        const uint32_t endIndex = aEnd/BLOCK_SIZE_BITS;
+        const uint32_t startIndex = aStart / BLOCK_SIZE_BITS;
+        const uint32_t endIndex = aEnd / BLOCK_SIZE_BITS;
 
-        if (endIndex >= mBlocks.Length()) {
-            uint32_t numNewBlocks = endIndex + 1 - mBlocks.Length();
-            mBlocks.AppendElements(numNewBlocks);
+        while (endIndex >= mBlockIndex.Length()) {
+            mBlockIndex.AppendElement(NO_BLOCK);
         }
 
         for (uint32_t i = startIndex; i <= endIndex; ++i) {
             const uint32_t blockFirstBit = i * BLOCK_SIZE_BITS;
             const uint32_t blockLastBit = blockFirstBit + BLOCK_SIZE_BITS - 1;
 
-            Block *block = mBlocks[i].get();
-            if (!block) {
+            if (mBlockIndex[i] == NO_BLOCK) {
                 bool fullBlock =
                     (aStart <= blockFirstBit && aEnd >= blockLastBit);
-
-                block = new Block(fullBlock ? 0xFF : 0);
-                mBlocks[i].reset(block);
-
+                mBlocks.AppendElement(Block(fullBlock ? 0xFF : 0));
+                MOZ_ASSERT(mBlocks.Length() < 0xffff, "block index overflow!");
+                mBlockIndex[i] = mBlocks.Length() - 1;
                 if (fullBlock) {
                     continue;
                 }
             }
 
+            Block& block = mBlocks[mBlockIndex[i]];
             const uint32_t start = aStart > blockFirstBit ? aStart - blockFirstBit : 0;
             const uint32_t end = std::min<uint32_t>(aEnd - blockFirstBit, BLOCK_SIZE_BITS - 1);
 
             for (uint32_t bit = start; bit <= end; ++bit) {
-                block->mBits[bit>>3] |= 1 << (bit & 0x7);
+                block.mBits[bit >> 3] |= 1 << (bit & 0x7);
             }
         }
     }
 
     void clear(uint32_t aIndex) {
-        uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS;
-        if (blockIndex >= mBlocks.Length()) {
-            mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
-        }
-        Block *block = mBlocks[blockIndex].get();
-        if (!block) {
+        uint32_t i = aIndex / BLOCK_SIZE_BITS;
+        if (i >= mBlockIndex.Length()) {
             return;
         }
-        block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] &= ~(1 << (aIndex & 0x7));
+        if (mBlockIndex[i] == NO_BLOCK) {
+            mBlocks.AppendElement();
+            MOZ_ASSERT(mBlocks.Length() < 0xffff, "block index overflow!");
+            mBlockIndex[i] = mBlocks.Length() - 1;
+        }
+        Block& block = mBlocks[mBlockIndex[i]];
+        block.mBits[(aIndex >> 3) & (BLOCK_SIZE - 1)] &= ~(1 << (aIndex & 0x7));
     }
 
     void ClearRange(uint32_t aStart, uint32_t aEnd) {
-        const uint32_t startIndex = aStart/BLOCK_SIZE_BITS;
-        const uint32_t endIndex = aEnd/BLOCK_SIZE_BITS;
-
-        if (endIndex >= mBlocks.Length()) {
-            uint32_t numNewBlocks = endIndex + 1 - mBlocks.Length();
-            mBlocks.AppendElements(numNewBlocks);
-        }
+        const uint32_t startIndex = aStart / BLOCK_SIZE_BITS;
+        const uint32_t endIndex = aEnd / BLOCK_SIZE_BITS;
 
         for (uint32_t i = startIndex; i <= endIndex; ++i) {
-            const uint32_t blockFirstBit = i * BLOCK_SIZE_BITS;
-
-            Block *block = mBlocks[i].get();
-            if (!block) {
-                // any nonexistent block is implicitly all clear,
-                // so there's no need to even create it
+            if (i >= mBlockIndex.Length()) {
+                return;
+            }
+            if (mBlockIndex[i] == NO_BLOCK) {
                 continue;
             }
 
+            const uint32_t blockFirstBit = i * BLOCK_SIZE_BITS;
+            Block& block = mBlocks[mBlockIndex[i]];
+
             const uint32_t start = aStart > blockFirstBit ? aStart - blockFirstBit : 0;
             const uint32_t end = std::min<uint32_t>(aEnd - blockFirstBit, BLOCK_SIZE_BITS - 1);
 
             for (uint32_t bit = start; bit <= end; ++bit) {
-                block->mBits[bit>>3] &= ~(1 << (bit & 0x7));
+                block.mBits[bit >> 3] &= ~(1 << (bit & 0x7));
             }
         }
     }
 
     size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
-        size_t total = mBlocks.ShallowSizeOfExcludingThis(aMallocSizeOf);
-        for (uint32_t i = 0; i < mBlocks.Length(); i++) {
-            if (mBlocks[i]) {
-                total += aMallocSizeOf(mBlocks[i].get());
-            }
-        }
-        return total;
+        return mBlocks.ShallowSizeOfExcludingThis(aMallocSizeOf) +
+               mBlockIndex.ShallowSizeOfExcludingThis(aMallocSizeOf);
     }
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
         return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     }
 
     // clear out all blocks in the array
     void reset() {
-        uint32_t i;
-        for (i = 0; i < mBlocks.Length(); i++) {
-            mBlocks[i] = nullptr;
-        }
+        mBlocks.Clear();
+        mBlockIndex.Clear();
     }
 
     // set this bitset to the union of its current contents and another
     void Union(const gfxSparseBitSet& aBitset) {
         // ensure mBlocks is large enough
-        uint32_t blockCount = aBitset.mBlocks.Length();
-        if (blockCount > mBlocks.Length()) {
-            uint32_t needed = blockCount - mBlocks.Length();
-            mBlocks.AppendElements(needed);
+        uint32_t blockCount = aBitset.mBlockIndex.Length();
+        while (blockCount > mBlockIndex.Length()) {
+            mBlockIndex.AppendElement(NO_BLOCK);
         }
         // for each block that may be present in aBitset...
         for (uint32_t i = 0; i < blockCount; ++i) {
             // if it is missing (implicitly empty), just skip
-            if (!aBitset.mBlocks[i]) {
+            if (aBitset.mBlockIndex[i] == NO_BLOCK) {
                 continue;
             }
             // if the block is missing in this set, just copy the other
-            if (!mBlocks[i]) {
-                mBlocks[i] = mozilla::MakeUnique<Block>(*aBitset.mBlocks[i]);
+            if (mBlockIndex[i] == NO_BLOCK) {
+                mBlocks.AppendElement(aBitset.mBlocks[aBitset.mBlockIndex[i]]);
+                MOZ_ASSERT(mBlocks.Length() < 0xffff, "block index overflow!");
+                mBlockIndex[i] = mBlocks.Length() - 1;
                 continue;
             }
             // else set existing block to the union of both
-            uint32_t *dst = reinterpret_cast<uint32_t*>(mBlocks[i]->mBits);
-            const uint32_t *src =
-                reinterpret_cast<const uint32_t*>(aBitset.mBlocks[i]->mBits);
+            uint32_t* dst = reinterpret_cast<uint32_t*>(
+                &mBlocks[mBlockIndex[i]].mBits);
+            const uint32_t* src = reinterpret_cast<const uint32_t*>(
+                &aBitset.mBlocks[aBitset.mBlockIndex[i]].mBits);
             for (uint32_t j = 0; j < BLOCK_SIZE / 4; ++j) {
                 dst[j] |= src[j];
             }
         }
     }
 
     void Compact() {
+        // TODO: Discard any empty blocks, and adjust index accordingly.
+        // (May not be worth doing, though, because we so rarely clear bits
+        // that were previously set.)
         mBlocks.Compact();
+        mBlockIndex.Compact();
     }
 
     uint32_t GetChecksum() const {
-        uint32_t check = adler32(0, Z_NULL, 0);
-        for (uint32_t i = 0; i < mBlocks.Length(); i++) {
-            if (mBlocks[i]) {
-                const Block *block = mBlocks[i].get();
-                check = adler32(check, (uint8_t*) (&i), 4);
-                check = adler32(check, (uint8_t*) block, sizeof(Block));
-            }
-        }
+        uint32_t check =
+            adler32(0,
+                    reinterpret_cast<const uint8_t*>(mBlockIndex.Elements()),
+                    mBlockIndex.Length() * sizeof(uint16_t));
+        check = adler32(check,
+                        reinterpret_cast<const uint8_t*>(mBlocks.Elements()),
+                        mBlocks.Length() * sizeof(Block));
         return check;
     }
 
 private:
-    nsTArray<mozilla::UniquePtr<Block>> mBlocks;
+    nsTArray<uint16_t> mBlockIndex;
+    nsTArray<Block>    mBlocks;
 };
 
 #define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
 
 namespace mozilla {
 
 // Byte-swapping types and name table structure definitions moved from
 // gfxFontUtils.cpp to .h file so that gfxFont.cpp can also refer to them
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -11,17 +11,17 @@
 #endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>     /* for isatty() */
 #endif
 
 #include "base/basictypes.h"
 
 #include "jsapi.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/SourceBufferHolder.h"
 
 #include "xpcpublic.h"
 
 #include "XPCShellEnvironment.h"
 
 #include "mozilla/XPCOM.h"
@@ -78,20 +78,20 @@ static bool
 Print(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
     for (unsigned i = 0; i < args.length(); i++) {
         JSString *str = JS::ToString(cx, args[i]);
         if (!str)
             return false;
-        JSAutoByteString bytes(cx, str);
+        JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, str);
         if (!bytes)
             return false;
-        fprintf(stdout, "%s%s", i ? " " : "", bytes.ptr());
+        fprintf(stdout, "%s%s", i ? " " : "", bytes.get());
         fflush(stdout);
     }
     fputc('\n', stdout);
     args.rval().setUndefined();
     return true;
 }
 
 static bool
@@ -114,21 +114,21 @@ Dump(JSContext *cx, unsigned argc, JS::V
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
     if (!args.length())
         return true;
 
     JSString *str = JS::ToString(cx, args[0]);
     if (!str)
         return false;
-    JSAutoByteString bytes(cx, str);
+    JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, str);
     if (!bytes)
       return false;
 
-    fputs(bytes.ptr(), stdout);
+    fputs(bytes.get(), stdout);
     fflush(stdout);
     return true;
 }
 
 static bool
 Load(JSContext *cx,
      unsigned argc,
      JS::Value *vp)
@@ -142,30 +142,30 @@ Load(JSContext *cx,
         JS_ReportErrorASCII(cx, "Trying to load() into a non-global object");
         return false;
     }
 
     for (unsigned i = 0; i < args.length(); i++) {
         JS::Rooted<JSString*> str(cx, JS::ToString(cx, args[i]));
         if (!str)
             return false;
-        JSAutoByteString filename(cx, str);
+        JS::UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
         if (!filename)
             return false;
-        FILE *file = fopen(filename.ptr(), "r");
+        FILE *file = fopen(filename.get(), "r");
         if (!file) {
-            filename.clear();
-            if (!filename.encodeUtf8(cx, str))
+            filename = JS_EncodeStringToUTF8(cx, str);
+            if (!filename)
                 return false;
-            JS_ReportErrorUTF8(cx, "cannot open file '%s' for reading", filename.ptr());
+            JS_ReportErrorUTF8(cx, "cannot open file '%s' for reading", filename.get());
             return false;
         }
         JS::CompileOptions options(cx);
         options.setUTF8(true)
-               .setFileAndLine(filename.ptr(), 1);
+               .setFileAndLine(filename.get(), 1);
         JS::Rooted<JSScript*> script(cx);
         bool ok = JS::Compile(cx, options, file, &script);
         fclose(file);
         if (!ok)
             return false;
 
         if (!JS_ExecuteScript(cx, script)) {
             return false;
@@ -341,23 +341,23 @@ XPCShellEnvironment::ProcessFile(JSConte
         if (JS_CompileScript(cx, buffer, strlen(buffer), options, &script)) {
             JS::WarningReporter older;
 
             ok = JS_ExecuteScript(cx, script, &result);
             if (ok && !result.isUndefined()) {
                 /* Suppress warnings from JS::ToString(). */
                 older = JS::SetWarningReporter(cx, nullptr);
                 str = JS::ToString(cx, result);
-                JSAutoByteString bytes;
+                JS::UniqueChars bytes;
                 if (str)
-                    bytes.encodeLatin1(cx, str);
+                    bytes = JS_EncodeStringToLatin1(cx, str);
                 JS::SetWarningReporter(cx, older);
 
                 if (!!bytes)
-                    fprintf(stdout, "%s\n", bytes.ptr());
+                    fprintf(stdout, "%s\n", bytes.get());
                 else
                     ok = false;
             }
         }
     } while (!hitEOF && !env->IsQuitting());
 
     fprintf(stdout, "\n");
 }
deleted file mode 100644
--- a/js/public/AutoByteString.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/*
- * DEPRECATED functions and classes for heap-allocating copies of a JSString's
- * data.
- */
-
-#ifndef js_AutoByteString_h
-#define js_AutoByteString_h
-
-#include "mozilla/Assertions.h" // MOZ_ASSERT
-#include "mozilla/Attributes.h" // MOZ_RAII, MOZ_GUARD*
-
-#include <string.h> // strlen
-
-#include "jstypes.h" // JS_PUBLIC_API
-
-#include "js/MemoryFunctions.h" // JS_free
-#include "js/RootingAPI.h" // JS::Handle
-#include "js/TypeDecls.h" // JSContext, JSString
-#include "js/Utility.h" // js_free, JS::UniqueChars
-
-/**
- * DEPRECATED
- *
- * Allocate memory sufficient to contain the characters of |str| truncated to
- * Latin-1 and a trailing null terminator, fill the memory with the characters
- * interpreted in that manner plus the null terminator, and return a pointer to
- * the memory.  The memory must be freed using JS_free to avoid leaking.
- *
- * This function *loses information* when it copies the characters of |str| if
- * |str| contains code units greater than 0xFF.  Additionally, users that
- * depend on null-termination will misinterpret the copied characters if |str|
- * contains any nulls.  Avoid using this function if possible, because it will
- * eventually be removed.
- */
-extern JS_PUBLIC_API(char*)
-JS_EncodeString(JSContext* cx, JSString* str);
-
-/**
- * DEPRECATED
- *
- * Same behavior as JS_EncodeString(), but encode into a UTF-8 string.
- *
- * This function *loses information* when it copies the characters of |str| if
- * |str| contains invalid UTF-16: U+FFFD REPLACEMENT CHARACTER will be copied
- * instead.
- *
- * The returned string is also subject to misinterpretation if |str| contains
- * any nulls (which are faithfully transcribed into the returned string, but
- * which will implicitly truncate the string if it's passed to functions that
- * expect null-terminated strings).
- *
- * Avoid using this function if possible, because we'll remove it once we can
- * devise a better API for the task.
- */
-extern JS_PUBLIC_API(char*)
-JS_EncodeStringToUTF8(JSContext* cx, JS::Handle<JSString*> str);
-
-/**
- * DEPRECATED
- *
- * A lightweight RAII helper class around the various JS_Encode* functions
- * above, subject to the same pitfalls noted above.  Avoid using this class if
- * possible, because as with the functions above, it too needs to be replaced
- * with a better, safer API.
- */
-class MOZ_RAII JSAutoByteString final
-{
-  private:
-    char* mBytes;
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-
-  private:
-    JSAutoByteString(const JSAutoByteString& another) = delete;
-    void operator=(const JSAutoByteString& another) = delete;
-
-  public:
-    JSAutoByteString(JSContext* cx, JSString* str
-                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : mBytes(JS_EncodeString(cx, str))
-    {
-        MOZ_ASSERT(cx);
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    explicit JSAutoByteString(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
-      : mBytes(nullptr)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    ~JSAutoByteString() {
-        JS_free(nullptr, mBytes);
-    }
-
-    /* Take ownership of the given byte array. */
-    void initBytes(JS::UniqueChars&& bytes) {
-        MOZ_ASSERT(!mBytes);
-        mBytes = bytes.release();
-    }
-
-    char* encodeLatin1(JSContext* cx, JSString* str) {
-        MOZ_ASSERT(!mBytes);
-        MOZ_ASSERT(cx);
-        mBytes = JS_EncodeString(cx, str);
-        return mBytes;
-    }
-
-    char* encodeUtf8(JSContext* cx, JS::Handle<JSString*> str) {
-        MOZ_ASSERT(!mBytes);
-        MOZ_ASSERT(cx);
-        mBytes = JS_EncodeStringToUTF8(cx, str);
-        return mBytes;
-    }
-
-    void clear() {
-        js_free(mBytes);
-        mBytes = nullptr;
-    }
-
-    char* ptr() const {
-        return mBytes;
-    }
-
-    bool operator!() const {
-        return !mBytes;
-    }
-
-    size_t length() const {
-        if (!mBytes)
-            return 0;
-        return strlen(mBytes);
-    }
-};
-
-#endif /* js_AutoByteString_h */
--- a/js/public/CharacterEncoding.h
+++ b/js/public/CharacterEncoding.h
@@ -112,17 +112,17 @@ class UTF8CharsZ : public mozilla::Range
 
     char* c_str() { return reinterpret_cast<char*>(get()); }
 };
 
 /*
  * A wrapper for a "const char*" that is encoded using UTF-8.
  * This class does not manage ownership of the data; that is left
  * to others.  This differs from UTF8CharsZ in that the chars are
- * const and it allows assignment.
+ * const and it disallows assignment.
  */
 class JS_PUBLIC_API(ConstUTF8CharsZ)
 {
     const char* data_;
 
   public:
     using CharT = unsigned char;
 
@@ -326,9 +326,46 @@ LossyUTF8CharsToNewLatin1CharsZ(JSContex
 extern JS_PUBLIC_API(bool)
 StringIsASCII(const char* s);
 
 } // namespace JS
 
 inline void JS_free(JS::Latin1CharsZ& ptr) { js_free((void*)ptr.get()); }
 inline void JS_free(JS::UTF8CharsZ& ptr) { js_free((void*)ptr.get()); }
 
+/**
+ * DEPRECATED
+ *
+ * Allocate memory sufficient to contain the characters of |str| truncated to
+ * Latin-1 and a trailing null terminator, fill the memory with the characters
+ * interpreted in that manner plus the null terminator, and return a pointer to
+ * the memory.
+ *
+ * This function *loses information* when it copies the characters of |str| if
+ * |str| contains code units greater than 0xFF.  Additionally, users that
+ * depend on null-termination will misinterpret the copied characters if |str|
+ * contains any nulls.  Avoid using this function if possible, because it will
+ * eventually be removed.
+ */
+extern JS_PUBLIC_API(JS::UniqueChars)
+JS_EncodeStringToLatin1(JSContext* cx, JSString* str);
+
+/**
+ * DEPRECATED
+ *
+ * Same behavior as JS_EncodeStringToLatin1(), but encode into a UTF-8 string.
+ *
+ * This function *loses information* when it copies the characters of |str| if
+ * |str| contains invalid UTF-16: U+FFFD REPLACEMENT CHARACTER will be copied
+ * instead.
+ *
+ * The returned string is also subject to misinterpretation if |str| contains
+ * any nulls (which are faithfully transcribed into the returned string, but
+ * which will implicitly truncate the string if it's passed to functions that
+ * expect null-terminated strings).
+ *
+ * Avoid using this function if possible, because we'll remove it once we can
+ * devise a better API for the task.
+ */
+extern JS_PUBLIC_API(JS::UniqueChars)
+JS_EncodeStringToUTF8(JSContext* cx, JS::Handle<JSString*> str);
+
 #endif /* js_CharacterEncoding_h */
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -620,17 +620,17 @@ class JS_FRIEND_API(AutoEnterPolicy)
   protected:
     // no-op constructor for subclass
     AutoEnterPolicy()
 #ifdef JS_DEBUG
         : context(nullptr)
         , enteredAction(BaseProxyHandler::NONE)
 #endif
         {}
-    void reportErrorIfExceptionIsNotPending(JSContext* cx, jsid id);
+    void reportErrorIfExceptionIsNotPending(JSContext* cx, HandleId id);
     bool allow;
     bool rv;
 
 #ifdef JS_DEBUG
     JSContext* context;
     mozilla::Maybe<HandleObject> enteredProxy;
     mozilla::Maybe<HandleId> enteredId;
     Action                   enteredAction;
--- a/js/rust/build.rs
+++ b/js/rust/build.rs
@@ -314,17 +314,16 @@ const WHITELIST_FUNCTIONS: &'static [&'s
     "JS_DefineProperties",
     "JS_DefineProperty",
     "JS_DefinePropertyById",
     "JS_DefineUCProperty",
     "JS::detail::InitWithFailureDiagnostic",
     "JS_DestroyContext",
     "JS::DisableIncrementalGC",
     "js::Dump.*",
-    "JS_EncodeStringToUTF8",
     "JS::EnterRealm",
     "JS_EnumerateStandardClasses",
     "JS_ErrorFromException",
     "JS_FireOnNewGlobalObject",
     "JS_free",
     "JS_GC",
     "JS_GetArrayBufferData",
     "JS_GetArrayBufferViewType",
--- a/js/rust/src/glue.rs
+++ b/js/rust/src/glue.rs
@@ -335,15 +335,19 @@ extern "C" {
     pub fn DeleteJSAutoStructuredCloneBuffer(buf: *mut JSAutoStructuredCloneBuffer);
     pub fn GetLengthOfJSStructuredCloneData(data: *mut JSStructuredCloneData) -> usize;
     pub fn CopyJSStructuredCloneData(src: *mut JSStructuredCloneData, dest: *mut u8);
     pub fn WriteBytesToJSStructuredCloneData(src: *const u8,
                                              len: usize,
                                              dest: *mut JSStructuredCloneData)
                                              -> bool;
 
+    pub fn JSEncodeStringToUTF8(cx: *mut JSContext,
+                                string: JS::HandleString)
+                                -> *mut ::libc::c_char;
+
     pub fn IsDebugBuild() -> bool;
 }
 
 #[test]
 fn jsglue_cpp_configured_correctly() {
     assert_eq!(cfg!(feature = "debugmozjs"), unsafe { IsDebugBuild() });
 }
--- a/js/rust/src/jsglue.cpp
+++ b/js/rust/src/jsglue.cpp
@@ -10,16 +10,17 @@
 #ifdef JS_DEBUG
 // A hack for MFBT. Guard objects need this to work.
 #define DEBUG 1
 #endif
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/Proxy.h"
+#include "js/CharacterEncoding.h"
 #include "js/Class.h"
 #include "js/MemoryMetrics.h"
 #include "js/Principals.h"
 #include "js/StructuredClone.h"
 #include "js/Wrapper.h"
 #include "assert.h"
 
 struct ProxyTraps {
@@ -915,9 +916,15 @@ bool
 WriteBytesToJSStructuredCloneData(const uint8_t* src, size_t len, JSStructuredCloneData* dest)
 {
     assert(src != nullptr);
     assert(dest != nullptr);
 
     return dest->AppendBytes(reinterpret_cast<const char*>(src), len);
 }
 
+char*
+JSEncodeStringToUTF8(JSContext* cx, JS::HandleString string)
+{
+    return JS_EncodeStringToUTF8(cx, string).release();
+}
+
 } // extern "C"
--- a/js/rust/tests/callback.rs
+++ b/js/rust/tests/callback.rs
@@ -2,21 +2,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #[macro_use]
 extern crate js;
 extern crate libc;
 
 use js::ar::AutoRealm;
+use js::glue::JSEncodeStringToUTF8;
 use js::jsapi::root::JS::CallArgs;
 use js::jsapi::root::JS::RealmOptions;
 use js::jsapi::root::JSContext;
 use js::jsapi::root::JS_DefineFunction;
-use js::jsapi::root::JS_EncodeStringToUTF8;
 use js::jsapi::root::JS_NewGlobalObject;
 use js::jsapi::root::JS_ReportErrorASCII;
 use js::jsapi::root::JS::OnNewGlobalHookOption;
 use js::jsapi::root::JS::Value;
 use js::jsval::UndefinedValue;
 use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
 
 use std::ffi::CStr;
@@ -50,15 +50,15 @@ unsafe extern "C" fn puts(context: *mut 
     if args._base.argc_ != 1 {
         JS_ReportErrorASCII(context, b"puts() requires exactly 1 argument\0".as_ptr() as *const libc::c_char);
         return false;
     }
 
     let arg = args.get(0);
     let js = js::rust::ToString(context, arg);
     rooted!(in(context) let message_root = js);
-    let message = JS_EncodeStringToUTF8(context, message_root.handle());
+    let message = JSEncodeStringToUTF8(context, message_root.handle());
     let message = CStr::from_ptr(message);
     println!("{}", str::from_utf8(message.to_bytes()).unwrap());
 
     args.rval().set(UndefinedValue());
     return true;
 }
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -266,17 +266,20 @@ js::ObjectToSource(JSContext* cx, Handle
             /*
              * If id is a string that's not an identifier, or if it's a
              * negative integer, then it must be quoted.
              */
             if (JSID_IS_ATOM(id)
                 ? !IsIdentifier(JSID_TO_ATOM(id))
                 : JSID_TO_INT(id) < 0)
             {
-                idstr = QuoteString(cx, idstr, char16_t('\''));
+                UniqueChars quotedId = QuoteString(cx, idstr, '\'');
+                if (!quotedId)
+                    return false;
+                idstr = NewStringCopyZ<CanGC>(cx, quotedId.get());
                 if (!idstr)
                     return false;
             }
         }
 
         RootedString valsource(cx, ValueToSource(cx, val));
         if (!valsource)
             return false;
@@ -1045,23 +1048,22 @@ js::obj_create(JSContext* cx, unsigned a
     // Step 1.
     if (args.length() == 0) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
                                   "Object.create", "0", "s");
         return false;
     }
 
     if (!args[0].isObjectOrNull()) {
-        RootedValue v(cx, args[0]);
-        UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
+        UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args[0], nullptr);
         if (!bytes)
             return false;
 
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                                   bytes.get(), "not an object or null");
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
+                                 bytes.get(), "not an object or null");
         return false;
     }
 
     // Step 2.
     RootedObject proto(cx, args[0].toObjectOrNull());
     RootedPlainObject obj(cx, ObjectCreateImpl(cx, proto));
     if (!obj)
         return false;
--- a/js/src/builtin/Profilers.cpp
+++ b/js/src/builtin/Profilers.cpp
@@ -23,16 +23,17 @@
 #endif
 #endif
 
 #ifdef XP_WIN
 # include <process.h>
 # define getpid _getpid
 #endif
 
+#include "js/CharacterEncoding.h"
 #include "js/Utility.h"
 #include "util/Text.h"
 #include "vm/Probes.h"
 
 #include "vm/JSContext-inl.h"
 
 using namespace js;
 
@@ -198,17 +199,17 @@ RequiredStringArg(JSContext* cx, const C
         return nullptr;
     }
 
     if (!args[argi].isString()) {
         JS_ReportErrorASCII(cx, "%s: invalid arguments (string expected)", caller);
         return nullptr;
     }
 
-    return UniqueChars(JS_EncodeString(cx, args[argi].toString()));
+    return JS_EncodeStringToLatin1(cx, args[argi].toString());
 }
 
 static bool
 StartProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
         args.rval().setBoolean(JS_StartProfiling(nullptr, getpid()));
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3389,17 +3389,17 @@ reflect_parse(JSContext* cx, uint32_t ar
             if (!GetPropertyDefault(cx, config, sourceId, nullVal, &prop))
                 return false;
 
             if (!prop.isNullOrUndefined()) {
                 RootedString str(cx, ToString<CanGC>(cx, prop));
                 if (!str)
                     return false;
 
-                filename.reset(JS_EncodeString(cx, str));
+                filename = EncodeLatin1(cx, str);
                 if (!filename)
                     return false;
             }
 
             /* config.line */
             RootedId lineId(cx, NameToId(cx->names().line));
             RootedValue oneValue(cx, Int32Value(1));
             if (!GetPropertyDefault(cx, config, lineId, oneValue, &prop) ||
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -272,18 +272,18 @@ PromiseRejectedWithPendingError(JSContex
 
 static void
 ReportArgTypeError(JSContext* cx, const char* funName, const char* expectedType, HandleValue arg)
 {
     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, nullptr);
     if (!bytes)
         return;
 
-    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, funName,
-                               expectedType, bytes.get());
+    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, funName,
+                             expectedType, bytes.get());
 }
 
 static MOZ_MUST_USE bool
 RejectWithPendingError(JSContext* cx, Handle<PromiseObject*> promise) {
     // Not much we can do about uncatchable exceptions, just bail.
     RootedValue exn(cx);
     if (!GetAndClearException(cx, &exn))
         return false;
--- a/js/src/builtin/String.cpp
+++ b/js/src/builtin/String.cpp
@@ -609,32 +609,36 @@ IsString(HandleValue v)
     return v.isString() || (v.isObject() && v.toObject().is<StringObject>());
 }
 
 MOZ_ALWAYS_INLINE bool
 str_toSource_impl(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(IsString(args.thisv()));
 
-    Rooted<JSString*> str(cx, ToString<CanGC>(cx, args.thisv()));
+    JSString* str = ToString<CanGC>(cx, args.thisv());
     if (!str)
         return false;
 
-    str = QuoteString(cx, str, '"');
-    if (!str)
+    UniqueChars quoted = QuoteString(cx, str, '"');
+    if (!quoted)
         return false;
 
     StringBuffer sb(cx);
-    if (!sb.append("(new String(") || !sb.append(str) || !sb.append("))"))
+    if (!sb.append("(new String(") ||
+        !sb.append(quoted.get(), strlen(quoted.get())) ||
+        !sb.append("))"))
+    {
         return false;
-
-    str = sb.finishString();
-    if (!str)
+    }
+
+    JSString* result = sb.finishString();
+    if (!result)
         return false;
-    args.rval().setString(str);
+    args.rval().setString(result);
     return true;
 }
 
 static bool
 str_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsString, str_toSource_impl>(cx, args);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -36,17 +36,17 @@
 #include "irregexp/RegExpAST.h"
 #include "irregexp/RegExpEngine.h"
 #include "irregexp/RegExpParser.h"
 #endif
 #include "gc/Heap.h"
 #include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitRealm.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/LocaleSensitive.h"
 #include "js/SourceBufferHolder.h"
 #include "js/StableStringChars.h"
 #include "js/StructuredClone.h"
@@ -1409,23 +1409,23 @@ CallFunctionWithAsyncStack(JSContext* cx
     if (!args[2].isString() || args[2].toString()->empty()) {
         JS_ReportErrorASCII(cx, "The third argument should be a non-empty string.");
         return false;
     }
 
     RootedObject function(cx, &args[0].toObject());
     RootedObject stack(cx, &args[1].toObject());
     RootedString asyncCause(cx, args[2].toString());
-    JSAutoByteString utf8Cause;
-    if (!utf8Cause.encodeUtf8(cx, asyncCause)) {
+    UniqueChars utf8Cause = JS_EncodeStringToUTF8(cx, asyncCause);
+    if (!utf8Cause) {
         MOZ_ASSERT(cx->isExceptionPending());
         return false;
     }
 
-    JS::AutoSetAsyncStackForNewCalls sas(cx, stack, utf8Cause.ptr(),
+    JS::AutoSetAsyncStackForNewCalls sas(cx, stack, utf8Cause.get(),
                                          JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
     return Call(cx, UndefinedHandleValue, function,
                 JS::HandleValueArray::empty(), args.rval());
 }
 
 static bool
 EnableTrackAllocations(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2189,26 +2189,25 @@ DumpHeap(JSContext* cx, unsigned argc, V
         }
     }
 
     if (args.length() > i) {
         Value v = args[i];
         if (v.isString()) {
             if (!fuzzingSafe) {
                 RootedString str(cx, v.toString());
-                JSAutoByteString fileNameBytes;
-                if (!fileNameBytes.encodeLatin1(cx, str))
+                UniqueChars fileNameBytes = JS_EncodeStringToLatin1(cx, str);
+                if (!fileNameBytes)
                     return false;
-                const char* fileName = fileNameBytes.ptr();
-                dumpFile = fopen(fileName, "w");
+                dumpFile = fopen(fileNameBytes.get(), "w");
                 if (!dumpFile) {
-                    fileNameBytes.clear();
-                    if (!fileNameBytes.encodeUtf8(cx, str))
+                    fileNameBytes = JS_EncodeStringToUTF8(cx, str);
+                    if (!fileNameBytes)
                         return false;
-                    JS_ReportErrorUTF8(cx, "can't open %s", fileNameBytes.ptr());
+                    JS_ReportErrorUTF8(cx, "can't open %s", fileNameBytes.get());
                     return false;
                 }
             }
             ++i;
         }
     }
 
     if (i != args.length()) {
@@ -2741,48 +2740,50 @@ class CloneBufferObject : public NativeO
         js_delete(data());
         setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
     }
 
     static bool
     setCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
         Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
 
-        uint8_t* data = nullptr;
-        UniquePtr<uint8_t[], JS::FreePolicy> dataOwner;
+        const char* data = nullptr;
+        UniqueChars dataOwner;
         uint32_t nbytes;
 
         if (args.get(0).isObject() && args[0].toObject().is<ArrayBufferObject>()) {
             ArrayBufferObject* buffer = &args[0].toObject().as<ArrayBufferObject>();
             bool isSharedMemory;
-            js::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory, &data);
+            uint8_t* dataBytes = nullptr;
+            js::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory, &dataBytes);
             MOZ_ASSERT(!isSharedMemory);
+            data = reinterpret_cast<char*>(dataBytes);
         } else {
             JSString* str = JS::ToString(cx, args.get(0));
             if (!str)
                 return false;
-            data = reinterpret_cast<uint8_t*>(JS_EncodeString(cx, str));
-            if (!data)
+            dataOwner = JS_EncodeStringToLatin1(cx, str);
+            if (!dataOwner)
                 return false;
-            dataOwner.reset(data);
+            data = dataOwner.get();
             nbytes = JS_GetStringLength(str);
         }
 
         if (nbytes == 0 || (nbytes % sizeof(uint64_t) != 0)) {
             JS_ReportErrorASCII(cx, "Invalid length for clonebuffer data");
             return false;
         }
 
         auto buf = js::MakeUnique<JSStructuredCloneData>(JS::StructuredCloneScope::DifferentProcess);
         if (!buf || !buf->Init(nbytes)) {
             ReportOutOfMemory(cx);
             return false;
         }
 
-        MOZ_ALWAYS_TRUE(buf->AppendBytes((const char*)data, nbytes));
+        MOZ_ALWAYS_TRUE(buf->AppendBytes(data, nbytes));
         obj->discard();
         obj->setData(buf.release(), true);
 
         args.rval().setUndefined();
         return true;
     }
 
     static bool
@@ -2909,27 +2910,27 @@ const JSPropertySpec CloneBufferObject::
     JS_PS_END
 };
 
 static mozilla::Maybe<JS::StructuredCloneScope>
 ParseCloneScope(JSContext* cx, HandleString str)
 {
     mozilla::Maybe<JS::StructuredCloneScope> scope;
 
-    JSAutoByteString scopeStr(cx, str);
+    JSLinearString* scopeStr = str->ensureLinear(cx);
     if (!scopeStr)
         return scope;
 
-    if (strcmp(scopeStr.ptr(), "SameProcessSameThread") == 0)
+    if (StringEqualsAscii(scopeStr, "SameProcessSameThread"))
         scope.emplace(JS::StructuredCloneScope::SameProcessSameThread);
-    else if (strcmp(scopeStr.ptr(), "SameProcessDifferentThread") == 0)
+    else if (StringEqualsAscii(scopeStr, "SameProcessDifferentThread"))
         scope.emplace(JS::StructuredCloneScope::SameProcessDifferentThread);
-    else if (strcmp(scopeStr.ptr(), "DifferentProcess") == 0)
+    else if (StringEqualsAscii(scopeStr, "DifferentProcess"))
         scope.emplace(JS::StructuredCloneScope::DifferentProcess);
-    else if (strcmp(scopeStr.ptr(), "DifferentProcessForIndexedDB") == 0)
+    else if (StringEqualsAscii(scopeStr, "DifferentProcessForIndexedDB"))
         scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB);
 
     return scope;
 }
 
 static bool
 Serialize(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2946,23 +2947,23 @@ Serialize(JSContext* cx, unsigned argc, 
         RootedValue v(cx);
         if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v))
             return false;
 
         if (!v.isUndefined()) {
             JSString* str = JS::ToString(cx, v);
             if (!str)
                 return false;
-            JSAutoByteString poli(cx, str);
+            JSLinearString* poli = str->ensureLinear(cx);
             if (!poli)
                 return false;
 
-            if (strcmp(poli.ptr(), "allow") == 0) {
+            if (StringEqualsAscii(poli, "allow")) {
                 // default
-            } else if (strcmp(poli.ptr(), "deny") == 0) {
+            } else if (StringEqualsAscii(poli, "deny")) {
                 policy.denySharedArrayBuffer();
             } else {
                 JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
                 return false;
             }
         }
 
         if (!JS_GetProperty(cx, opts, "scope", &v))
@@ -3299,21 +3300,27 @@ GetBacktrace(JSContext* cx, unsigned arg
             return false;
         showLocals = ToBoolean(v);
 
         if (!JS_GetProperty(cx, cfg, "thisprops", &v))
             return false;
         showThisProps = ToBoolean(v);
     }
 
-    JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, showArgs, showLocals, showThisProps);
+    JS::UniqueChars buf = JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
     if (!buf)
         return false;
 
-    return ReturnStringCopy(cx, args, buf.get());
+    JS::ConstUTF8CharsZ utf8chars(buf.get(), strlen(buf.get()));
+    JSString* str = NewStringCopyUTF8Z<CanGC>(cx, utf8chars);
+    if (!str)
+        return false;
+
+    args.rval().setString(str);
+    return true;
 }
 
 static bool
 ReportOutOfMemory(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ReportOutOfMemory(cx);
     cx->clearPendingException();
@@ -4116,41 +4123,41 @@ SetGCCallback(JSContext* cx, unsigned ar
 
     RootedValue v(cx);
     if (!JS_GetProperty(cx, opts, "action", &v))
         return false;
 
     JSString* str = JS::ToString(cx, v);
     if (!str)
         return false;
-    JSAutoByteString action(cx, str);
+    RootedLinearString action(cx, str->ensureLinear(cx));
     if (!action)
         return false;
 
     int32_t phases = 0;
-    if ((strcmp(action.ptr(), "minorGC") == 0) || (strcmp(action.ptr(), "majorGC") == 0)) {
+    if (StringEqualsAscii(action, "minorGC") || StringEqualsAscii(action, "majorGC")) {
         if (!JS_GetProperty(cx, opts, "phases", &v))
             return false;
         if (v.isUndefined()) {
             phases = (1 << JSGC_END);
         } else {
             JSString* str = JS::ToString(cx, v);
             if (!str)
                 return false;
-            JSAutoByteString phasesStr(cx, str);
+            JSLinearString* phasesStr = str->ensureLinear(cx);
             if (!phasesStr)
                 return false;
 
-            if (strcmp(phasesStr.ptr(), "begin") == 0)
+            if (StringEqualsAscii(phasesStr, "begin")) {
                 phases = (1 << JSGC_BEGIN);
-            else if (strcmp(phasesStr.ptr(), "end") == 0)
+            } else if (StringEqualsAscii(phasesStr, "end")) {
                 phases = (1 << JSGC_END);
-            else if (strcmp(phasesStr.ptr(), "both") == 0)
+            } else if (StringEqualsAscii(phasesStr, "both")) {
                 phases = (1 << JSGC_BEGIN) | (1 << JSGC_END);
-            else {
+            } else {
                 JS_ReportErrorASCII(cx, "Invalid callback phase");
                 return false;
             }
         }
     }
 
     if (gcCallback::prevMajorGC) {
         JS_SetGCCallback(cx, nullptr, nullptr);
@@ -4159,27 +4166,27 @@ SetGCCallback(JSContext* cx, unsigned ar
     }
 
     if (gcCallback::prevMinorGC) {
         JS_SetGCCallback(cx, nullptr, nullptr);
         js_delete<gcCallback::MinorGC>(gcCallback::prevMinorGC);
         gcCallback::prevMinorGC = nullptr;
     }
 
-    if (strcmp(action.ptr(), "minorGC") == 0) {
+    if (StringEqualsAscii(action, "minorGC")) {
         auto info = js_new<gcCallback::MinorGC>();
         if (!info) {
             ReportOutOfMemory(cx);
             return false;
         }
 
         info->phases = phases;
         info->active = true;
         JS_SetGCCallback(cx, gcCallback::minorGC, info);
-    } else if (strcmp(action.ptr(), "majorGC") == 0) {
+    } else if (StringEqualsAscii(action, "majorGC")) {
         if (!JS_GetProperty(cx, opts, "depth", &v))
             return false;
         int32_t depth = 1;
         if (!v.isUndefined()) {
             if (!ToInt32(cx, v, &depth))
                 return false;
         }
         if (depth < 0) {
@@ -4729,21 +4736,21 @@ SetTimeZone(JSContext* cx, unsigned argc
 #if defined(_WIN32)
         return _putenv_s("TZ", "") == 0;
 #else
         return unsetenv("TZ") == 0;
 #endif /* _WIN32 */
     };
 
     if (args[0].isString() && !args[0].toString()->empty()) {
-        JSAutoByteString timeZone;
-        if (!timeZone.encodeLatin1(cx, args[0].toString()))
+        UniqueChars timeZone = JS_EncodeStringToLatin1(cx, args[0].toString());
+        if (!timeZone)
             return false;
 
-        if (!setTimeZone(timeZone.ptr())) {
+        if (!setTimeZone(timeZone.get())) {
             JS_ReportErrorASCII(cx, "Failed to set 'TZ' environment variable");
             return false;
         }
     } else {
         if (!unsetTimeZone()) {
             JS_ReportErrorASCII(cx, "Failed to unset 'TZ' environment variable");
             return false;
         }
@@ -4819,21 +4826,21 @@ SetDefaultLocale(JSContext* cx, unsigned
                             : containsOnlyValidBCP47Characters(str->twoByteChars(nogc), length);
         }
 
         if (!hasValidChars) {
             ReportUsageErrorASCII(cx, callee, "First argument should be BCP47 language tag");
             return false;
         }
 
-        JSAutoByteString locale;
-        if (!locale.encodeLatin1(cx, str))
+        UniqueChars locale = JS_EncodeStringToLatin1(cx, str);
+        if (!locale)
             return false;
 
-        if (!JS_SetDefaultLocale(cx->runtime(), locale.ptr())) {
+        if (!JS_SetDefaultLocale(cx->runtime(), locale.get())) {
             ReportOutOfMemory(cx);
             return false;
         }
     } else {
         JS_ResetDefaultLocale(cx->runtime());
     }
 
     args.rval().setUndefined();
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -7,16 +7,17 @@
 #include "builtin/TypedObject-inl.h"
 
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 
 #include "jsutil.h"
 
 #include "gc/Marking.h"
+#include "js/CharacterEncoding.h"
 #include "js/Vector.h"
 #include "util/StringBuffer.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSFunction.h"
 #include "vm/Realm.h"
 #include "vm/SelfHosting.h"
 #include "vm/StringType.h"
 #include "vm/TypedArrayObject.h"
@@ -1586,31 +1587,16 @@ TypedObject::createZeroed(JSContext* cx,
     buffer = ArrayBufferObject::create(cx, totalSize);
     if (!buffer)
         return nullptr;
     descr->initInstances(cx->runtime(), buffer->dataPointer(), 1);
     obj->attach(cx, *buffer, 0);
     return obj;
 }
 
-static bool
-ReportTypedObjTypeError(JSContext* cx,
-                        const unsigned errorNumber,
-                        HandleTypedObject obj)
-{
-    // Serialize type string of obj
-    RootedAtom typeReprAtom(cx, &obj->typeDescr().stringRepr());
-    UniqueChars typeReprStr(JS_EncodeStringToUTF8(cx, typeReprAtom));
-    if (!typeReprStr)
-        return false;
-
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, typeReprStr.get());
-    return false;
-}
-
 /* static */ void
 OutlineTypedObject::obj_trace(JSTracer* trc, JSObject* object)
 {
     OutlineTypedObject& typedObj = object->as<OutlineTypedObject>();
 
     TraceEdge(trc, typedObj.shapePtr(), "OutlineTypedObject_shape");
 
     if (!typedObj.owner_)
@@ -1690,41 +1676,30 @@ TypedObject::obj_lookupProperty(JSContex
         objp.set(nullptr);
         propp.setNotFound();
         return true;
     }
 
     return LookupProperty(cx, proto, id, objp, propp);
 }
 
-static bool
-ReportPropertyError(JSContext* cx,
-                    const unsigned errorNumber,
-                    HandleId id)
-{
-    RootedValue idVal(cx, IdToValue(id));
-    RootedString str(cx, ValueToSource(cx, idVal));
-    if (!str)
-        return false;
-
-    UniqueChars propName(JS_EncodeStringToUTF8(cx, str));
-    if (!propName)
-        return false;
-
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, propName.get());
-    return false;
-}
-
 bool
 TypedObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                 Handle<PropertyDescriptor> desc,
                                 ObjectOpResult& result)
 {
-    Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
-    return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
+    // Serialize the type string of |obj|.
+    RootedAtom typeReprAtom(cx, &obj->as<TypedObject>().typeDescr().stringRepr());
+    UniqueChars typeReprStr = StringToNewUTF8CharsZ(cx, *typeReprAtom);
+    if (!typeReprStr)
+        return false;
+
+    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_NOT_EXTENSIBLE,
+                             typeReprStr.get());
+    return false;
 }
 
 bool
 TypedObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
     switch (typedObj->typeDescr().kind()) {
       case type::Scalar:
@@ -2009,18 +1984,24 @@ IsOwnId(JSContext* cx, HandleObject obj,
     }
 
     return false;
 }
 
 bool
 TypedObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
 {
-    if (IsOwnId(cx, obj, id))
-        return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
+    if (IsOwnId(cx, obj, id)) {
+        UniqueChars propName = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
+        if (!propName)
+            return false;
+
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_CANT_DELETE, propName.get());
+        return false;
+    }
 
     RootedObject proto(cx, obj->staticPrototype());
     if (!proto)
         return result.succeed();
 
     return DeleteProperty(cx, proto, id, result);
 }
 
--- a/js/src/builtin/intl/Collator.cpp
+++ b/js/src/builtin/intl/Collator.cpp
@@ -12,17 +12,17 @@
 
 #include "jsapi.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "gc/FreeOp.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/StableStringChars.h"
 #include "js/TypeDecls.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Runtime.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
@@ -195,21 +195,21 @@ js::intl_Collator_availableLocales(JSCon
 
 bool
 js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isString());
 
-    JSAutoByteString locale(cx, args[0].toString());
+    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
     if (!locale)
         return false;
     UErrorCode status = U_ZERO_ERROR;
-    UEnumeration* values = ucol_getKeywordValuesForLocale("co", locale.ptr(), false, &status);
+    UEnumeration* values = ucol_getKeywordValuesForLocale("co", locale.get(), false, &status);
     if (U_FAILURE(status)) {
         ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UEnumeration, uenum_close> toClose(values);
 
     uint32_t count = uenum_count(values, &status);
     if (U_FAILURE(status)) {
@@ -272,17 +272,17 @@ NewUCollator(JSContext* cx, Handle<Colla
     RootedValue value(cx);
 
     RootedObject internals(cx, intl::GetInternalsObject(cx, collator));
     if (!internals)
         return nullptr;
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    JSAutoByteString locale(cx, value.toString());
+    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     // UCollator options with default values.
     UColAttributeValue uStrength = UCOL_DEFAULT;
     UColAttributeValue uCaseLevel = UCOL_OFF;
     UColAttributeValue uAlternate = UCOL_DEFAULT;
     UColAttributeValue uNumeric = UCOL_OFF;
@@ -295,17 +295,17 @@ NewUCollator(JSContext* cx, Handle<Colla
 
     {
         JSLinearString* usage = value.toString()->ensureLinear(cx);
         if (!usage)
             return nullptr;
         if (StringEqualsAscii(usage, "search")) {
             // ICU expects search as a Unicode locale extension on locale.
             // Unicode locale extensions must occur before private use extensions.
-            const char* oldLocale = locale.ptr();
+            const char* oldLocale = locale.get();
             const char* p;
             size_t index;
             size_t localeLen = strlen(oldLocale);
             if ((p = strstr(oldLocale, "-x-")))
                 index = p - oldLocale;
             else
                 index = localeLen;
 
@@ -318,18 +318,17 @@ NewUCollator(JSContext* cx, Handle<Colla
             }
             size_t insertLen = strlen(insert);
             char* newLocale = cx->pod_malloc<char>(localeLen + insertLen + 1);
             if (!newLocale)
                 return nullptr;
             memcpy(newLocale, oldLocale, index);
             memcpy(newLocale + index, insert, insertLen);
             memcpy(newLocale + index + insertLen, oldLocale + index, localeLen - index + 1); // '\0'
-            locale.clear();
-            locale.initBytes(JS::UniqueChars(newLocale));
+            locale = JS::UniqueChars(newLocale);
         } else {
             MOZ_ASSERT(StringEqualsAscii(usage, "sort"));
         }
     }
 
     // We don't need to look at the collation property - it can only be set
     // via the Unicode locale extension and is therefore already set on
     // locale.
@@ -381,17 +380,17 @@ NewUCollator(JSContext* cx, Handle<Colla
             uCaseFirst = UCOL_LOWER_FIRST;
         } else {
             MOZ_ASSERT(StringEqualsAscii(caseFirst, "false"));
             uCaseFirst = UCOL_OFF;
         }
     }
 
     UErrorCode status = U_ZERO_ERROR;
-    UCollator* coll = ucol_open(IcuLocale(locale.ptr()), &status);
+    UCollator* coll = ucol_open(IcuLocale(locale.get()), &status);
     if (U_FAILURE(status)) {
         ReportInternalError(cx);
         return nullptr;
     }
 
     ucol_setAttribute(coll, UCOL_STRENGTH, uStrength, &status);
     ucol_setAttribute(coll, UCOL_CASE_LEVEL, uCaseLevel, &status);
     ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, uAlternate, &status);
--- a/js/src/builtin/intl/CommonFunctions.cpp
+++ b/js/src/builtin/intl/CommonFunctions.cpp
@@ -4,16 +4,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Operations used to implement multiple Intl.* classes. */
 
 #include "builtin/intl/CommonFunctions.h"
 
 #include "mozilla/Assertions.h"
+#include "mozilla/TextUtils.h"
+
+#include <algorithm>
 
 #include "jsfriendapi.h" // for GetErrorMessage, JSMSG_INTERNAL_INTL_ERROR
 
 #include "js/Value.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/Stack.h"
@@ -77,16 +80,42 @@ js::intl::GetInternalsObject(JSContext* 
 }
 
 void
 js::intl::ReportInternalError(JSContext* cx)
 {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
 }
 
+js::UniqueChars
+js::intl::EncodeLocale(JSContext* cx, JSString* locale)
+{
+#ifdef DEBUG
+    auto containsOnlyValidBCP47Chars = [](auto* chars, size_t length) {
+        return length > 0 &&
+               mozilla::IsAsciiAlpha(chars[0]) &&
+               std::all_of(chars, chars + length, [](auto c) {
+                   return mozilla::IsAsciiAlphanumeric(c) || c == '-';
+               });
+    };
+
+    if (JSLinearString* linear = locale->ensureLinear(cx)) {
+        JS::AutoCheckCannotGC nogc;
+        MOZ_ASSERT(linear->hasLatin1Chars()
+                   ? containsOnlyValidBCP47Chars(linear->latin1Chars(nogc), linear->length())
+                   : containsOnlyValidBCP47Chars(linear->twoByteChars(nogc), linear->length()));
+    } else {
+        // Ignore OOM when only performing a debug assertion.
+        cx->recoverFromOutOfMemory();
+    }
+#endif
+
+    return EncodeLatin1(cx, locale);
+}
+
 bool
 js::intl::GetAvailableLocales(JSContext* cx, CountAvailable countAvailable,
                               GetAvailable getAvailable, JS::MutableHandle<JS::Value> result)
 {
     RootedObject locales(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
     if (!locales)
         return false;
 
--- a/js/src/builtin/intl/CommonFunctions.h
+++ b/js/src/builtin/intl/CommonFunctions.h
@@ -67,16 +67,19 @@ static inline const char*
 IcuLocale(const char* locale)
 {
     if (StringsAreEqual(locale, "und"))
         return ""; // ICU root locale
 
     return locale;
 }
 
+extern UniqueChars
+EncodeLocale(JSContext* cx, JSString* locale);
+
 // Starting with ICU 59, UChar defaults to char16_t.
 static_assert(mozilla::IsSame<UChar, char16_t>::value,
               "SpiderMonkey doesn't support redefining UChar to a different type");
 
 // The inline capacity we use for a Vector<char16_t>.  Use this to ensure that
 // our uses of ICU string functions, below and elsewhere, will try to fill the
 // buffer's entire inline capacity before growing it and heap-allocating.
 constexpr size_t INITIAL_CHAR_BUFFER_SIZE = 32;
--- a/js/src/builtin/intl/DateTimeFormat.cpp
+++ b/js/src/builtin/intl/DateTimeFormat.cpp
@@ -14,17 +14,17 @@
 #include "jsfriendapi.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "builtin/intl/TimeZoneDataGenerated.h"
 #include "gc/FreeOp.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/StableStringChars.h"
 #include "vm/DateTime.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Runtime.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
@@ -231,20 +231,20 @@ js::intl_DateTimeFormat_availableLocales
     RootedValue result(cx);
     if (!GetAvailableLocales(cx, udat_countAvailable, udat_getAvailable, &result))
         return false;
     args.rval().set(result);
     return true;
 }
 
 static bool
-DefaultCalendar(JSContext* cx, const JSAutoByteString& locale, MutableHandleValue rval)
+DefaultCalendar(JSContext* cx, const UniqueChars& locale, MutableHandleValue rval)
 {
     UErrorCode status = U_ZERO_ERROR;
-    UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
+    UCalendar* cal = ucal_open(nullptr, 0, locale.get(), UCAL_DEFAULT, &status);
 
     // This correctly handles nullptr |cal| when opening failed.
     ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
 
     const char* calendar = ucal_getType(cal, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
@@ -278,17 +278,17 @@ const CalendarAlias calendarAliases[] = 
 
 bool
 js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isString());
 
-    JSAutoByteString locale(cx, args[0].toString());
+    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
     if (!locale)
         return false;
 
     RootedObject calendars(cx, NewDenseEmptyArray(cx));
     if (!calendars)
         return false;
     uint32_t index = 0;
 
@@ -297,17 +297,17 @@ js::intl_availableCalendars(JSContext* c
     if (!DefaultCalendar(cx, locale, &element))
         return false;
 
     if (!DefineDataElement(cx, calendars, index++, element))
         return false;
 
     // Now get the calendars that "would make a difference", i.e., not the default.
     UErrorCode status = U_ZERO_ERROR;
-    UEnumeration* values = ucal_getKeywordValuesForLocale("ca", locale.ptr(), false, &status);
+    UEnumeration* values = ucal_getKeywordValuesForLocale("ca", locale.get(), false, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UEnumeration, uenum_close> toClose(values);
 
     uint32_t count = uenum_count(values, &status);
     if (U_FAILURE(status)) {
@@ -355,17 +355,17 @@ js::intl_availableCalendars(JSContext* c
 
 bool
 js::intl_defaultCalendar(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isString());
 
-    JSAutoByteString locale(cx, args[0].toString());
+    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
     if (!locale)
         return false;
 
     return DefaultCalendar(cx, locale, args.rval());
 }
 
 bool
 js::intl_IsValidTimeZoneName(JSContext* cx, unsigned argc, Value* vp)
@@ -522,28 +522,28 @@ js::intl_isDefaultTimeZone(JSContext* cx
 bool
 js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(args[0].isString());
     MOZ_ASSERT(args[1].isString());
 
-    JSAutoByteString locale(cx, args[0].toString());
+    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
     if (!locale)
         return false;
 
     AutoStableStringChars skeleton(cx);
     if (!skeleton.initTwoByte(cx, args[1].toString()))
         return false;
 
     mozilla::Range<const char16_t> skelChars = skeleton.twoByteRange();
 
     UErrorCode status = U_ZERO_ERROR;
-    UDateTimePatternGenerator* gen = udatpg_open(IcuLocale(locale.ptr()), &status);
+    UDateTimePatternGenerator* gen = udatpg_open(IcuLocale(locale.get()), &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UDateTimePatternGenerator, udatpg_close> toClose(gen);
 
     JSString* str =
         CallICU(cx, [gen, &skelChars](UChar* chars, uint32_t size, UErrorCode* status) {
@@ -559,17 +559,17 @@ js::intl_patternForSkeleton(JSContext* c
 
 bool
 js::intl_patternForStyle(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 4);
     MOZ_ASSERT(args[0].isString());
 
-    JSAutoByteString locale(cx, args[0].toString());
+    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
     if (!locale)
         return false;
 
     UDateFormatStyle dateStyle = UDAT_NONE;
     UDateFormatStyle timeStyle = UDAT_NONE;
 
     if (args[1].isString()) {
         JSLinearString* dateStyleStr = args[1].toString()->ensureLinear(cx);
@@ -607,17 +607,17 @@ js::intl_patternForStyle(JSContext* cx, 
 
     AutoStableStringChars timeZone(cx);
     if (!timeZone.initTwoByte(cx, args[3].toString()))
         return false;
 
     mozilla::Range<const char16_t> timeZoneChars = timeZone.twoByteRange();
 
     UErrorCode status = U_ZERO_ERROR;
-    UDateFormat* df = udat_open(timeStyle, dateStyle, IcuLocale(locale.ptr()),
+    UDateFormat* df = udat_open(timeStyle, dateStyle, IcuLocale(locale.get()),
                                 timeZoneChars.begin().get(), timeZoneChars.length(),
                                 nullptr, -1, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UDateFormat, udat_close> toClose(df);
 
@@ -640,17 +640,17 @@ NewUDateFormat(JSContext* cx, Handle<Dat
     RootedValue value(cx);
 
     RootedObject internals(cx, intl::GetInternalsObject(cx, dateTimeFormat));
     if (!internals)
        return nullptr;
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    JSAutoByteString locale(cx, value.toString());
+    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     // We don't need to look at calendar and numberingSystem - they can only be
     // set via the Unicode locale extension and are therefore already set on
     // locale.
 
     if (!GetProperty(cx, internals, internals, cx->names().timeZone, &value))
@@ -668,17 +668,17 @@ NewUDateFormat(JSContext* cx, Handle<Dat
     AutoStableStringChars pattern(cx);
     if (!pattern.initTwoByte(cx, value.toString()))
         return nullptr;
 
     mozilla::Range<const char16_t> patternChars = pattern.twoByteRange();
 
     UErrorCode status = U_ZERO_ERROR;
     UDateFormat* df =
-        udat_open(UDAT_PATTERN, UDAT_PATTERN, IcuLocale(locale.ptr()),
+        udat_open(UDAT_PATTERN, UDAT_PATTERN, IcuLocale(locale.get()),
                   timeZoneChars.begin().get(), timeZoneChars.length(),
                   patternChars.begin().get(), patternChars.length(), &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return nullptr;
     }
 
     // ECMAScript requires the Gregorian calendar to be used from the beginning
--- a/js/src/builtin/intl/IntlObject.cpp
+++ b/js/src/builtin/intl/IntlObject.cpp
@@ -16,17 +16,17 @@
 
 #include "builtin/intl/Collator.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/DateTimeFormat.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/PluralRules.h"
 #include "builtin/intl/ScopedICUObject.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/Class.h"
 #include "js/StableStringChars.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
@@ -45,24 +45,24 @@ using js::intl::IcuLocale;
 /******************** Intl ********************/
 
 bool
 js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
-    JSAutoByteString locale(cx, args[0].toString());
+    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
     if (!locale)
         return false;
 
     UErrorCode status = U_ZERO_ERROR;
     const UChar* uTimeZone = nullptr;
     int32_t uTimeZoneLength = 0;
-    UCalendar* cal = ucal_open(uTimeZone, uTimeZoneLength, locale.ptr(), UCAL_DEFAULT, &status);
+    UCalendar* cal = ucal_open(uTimeZone, uTimeZoneLength, locale.get(), UCAL_DEFAULT, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UCalendar, ucal_close> toClose(cal);
 
     RootedObject info(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!info)
@@ -365,19 +365,18 @@ ComputeSingleDisplayName(JSContext* cx, 
 
 bool
 js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 3);
 
     // 1. Assert: locale is a string.
-    RootedString str(cx, args[0].toString());
-    JSAutoByteString locale;
-    if (!locale.encodeUtf8(cx, str))
+    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
+    if (!locale)
         return false;
 
     // 2. Assert: style is a string.
     DisplayNameStyle dnStyle;
     {
         JSLinearString* style = args[1].toString()->ensureLinear(cx);
         if (!style)
             return false;
@@ -400,27 +399,27 @@ js::intl_ComputeDisplayNames(JSContext* 
     // 4. Let result be ArrayCreate(0).
     RootedArrayObject result(cx, NewDenseUnallocatedArray(cx, keys->length()));
     if (!result)
         return false;
 
     UErrorCode status = U_ZERO_ERROR;
 
     UDateFormat* fmt =
-        udat_open(UDAT_DEFAULT, UDAT_DEFAULT, IcuLocale(locale.ptr()),
+        udat_open(UDAT_DEFAULT, UDAT_DEFAULT, IcuLocale(locale.get()),
         nullptr, 0, nullptr, 0, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UDateFormat, udat_close> datToClose(fmt);
 
     // UDateTimePatternGenerator will be needed for translations of date and
     // time fields like "month", "week", "day" etc.
-    UDateTimePatternGenerator* dtpg = udatpg_open(IcuLocale(locale.ptr()), &status);
+    UDateTimePatternGenerator* dtpg = udatpg_open(IcuLocale(locale.get()), &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UDateTimePatternGenerator, udatpg_close> datPgToClose(dtpg);
 
     // 5. For each element of keys,
     RootedString keyValStr(cx);
@@ -456,28 +455,28 @@ js::intl_ComputeDisplayNames(JSContext* 
 }
 
 bool
 js::intl_GetLocaleInfo(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
-    JSAutoByteString locale(cx, args[0].toString());
+    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
     if (!locale)
         return false;
 
     RootedObject info(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!info)
         return false;
 
     if (!DefineDataProperty(cx, info, cx->names().locale, args[0]))
         return false;
 
-    bool rtl = uloc_isRightToLeft(IcuLocale(locale.ptr()));
+    bool rtl = uloc_isRightToLeft(IcuLocale(locale.get()));
 
     RootedValue dir(cx, StringValue(rtl ? cx->names().rtl : cx->names().ltr));
 
     if (!DefineDataProperty(cx, info, cx->names().direction, dir))
         return false;
 
     args.rval().setObject(*info);
     return true;
--- a/js/src/builtin/intl/NumberFormat.cpp
+++ b/js/src/builtin/intl/NumberFormat.cpp
@@ -15,16 +15,17 @@
 #include <stddef.h>
 #include <stdint.h>
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
+#include "js/CharacterEncoding.h"
 #include "js/RootingAPI.h"
 #include "js/StableStringChars.h"
 #include "js/TypeDecls.h"
 #include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 #include "vm/Stack.h"
 
 #include "vm/JSObject-inl.h"
@@ -208,22 +209,22 @@ js::intl_NumberFormat_availableLocales(J
 
 bool
 js::intl_numberingSystem(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isString());
 
-    JSAutoByteString locale(cx, args[0].toString());
+    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
     if (!locale)
         return false;
 
     UErrorCode status = U_ZERO_ERROR;
-    UNumberingSystem* numbers = unumsys_open(IcuLocale(locale.ptr()), &status);
+    UNumberingSystem* numbers = unumsys_open(IcuLocale(locale.get()), &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
 
     ScopedICUObject<UNumberingSystem, unumsys_close> toClose(numbers);
 
     const char* name = unumsys_getName(numbers);
@@ -250,17 +251,17 @@ NewUNumberFormat(JSContext* cx, Handle<N
     RootedValue value(cx);
 
     RootedObject internals(cx, intl::GetInternalsObject(cx, numberFormat));
     if (!internals)
        return nullptr;
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    JSAutoByteString locale(cx, value.toString());
+    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     // UNumberFormat options with default values
     UNumberFormatStyle uStyle = UNUM_DECIMAL;
     const UChar* uCurrency = nullptr;
     uint32_t uMinimumIntegerDigits = 1;
     uint32_t uMinimumFractionDigits = 0;
@@ -342,17 +343,17 @@ NewUNumberFormat(JSContext* cx, Handle<N
         uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
     }
 
     if (!GetProperty(cx, internals, internals, cx->names().useGrouping, &value))
         return nullptr;
     uUseGrouping = value.toBoolean();
 
     UErrorCode status = U_ZERO_ERROR;
-    UNumberFormat* nf = unum_open(uStyle, nullptr, 0, IcuLocale(locale.ptr()), nullptr, &status);
+    UNumberFormat* nf = unum_open(uStyle, nullptr, 0, IcuLocale(locale.get()), nullptr, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return nullptr;
     }
     ScopedICUObject<UNumberFormat, unum_close> toClose(nf);
 
     if (uCurrency) {
         unum_setTextAttribute(nf, UNUM_CURRENCY_CODE, uCurrency, 3, &status);
--- a/js/src/builtin/intl/PluralRules.cpp
+++ b/js/src/builtin/intl/PluralRules.cpp
@@ -10,17 +10,17 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
@@ -191,17 +191,17 @@ NewUNumberFormatForPluralRules(JSContext
     RootedObject internals(cx, intl::GetInternalsObject(cx, pluralRules));
     if (!internals)
        return nullptr;
 
     RootedValue value(cx);
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    JSAutoByteString locale(cx, value.toString());
+    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     uint32_t uMinimumIntegerDigits = 1;
     uint32_t uMinimumFractionDigits = 0;
     uint32_t uMaximumFractionDigits = 3;
     int32_t uMinimumSignificantDigits = -1;
     int32_t uMaximumSignificantDigits = -1;
@@ -229,17 +229,17 @@ NewUNumberFormatForPluralRules(JSContext
 
         if (!GetProperty(cx, internals, internals, cx->names().maximumFractionDigits, &value))
             return nullptr;
         uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
     }
 
     UErrorCode status = U_ZERO_ERROR;
     UNumberFormat* nf =
-        unum_open(UNUM_DECIMAL, nullptr, 0, IcuLocale(locale.ptr()), nullptr, &status);
+        unum_open(UNUM_DECIMAL, nullptr, 0, IcuLocale(locale.get()), nullptr, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return nullptr;
     }
     ScopedICUObject<UNumberFormat, unum_close> toClose(nf);
 
     if (uMinimumSignificantDigits != -1) {
         unum_setAttribute(nf, UNUM_SIGNIFICANT_DIGITS_USED, true);
@@ -264,17 +264,17 @@ NewUPluralRules(JSContext* cx, Handle<Pl
     RootedObject internals(cx, intl::GetInternalsObject(cx, pluralRules));
     if (!internals)
         return nullptr;
 
     RootedValue value(cx);
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    JSAutoByteString locale(cx, value.toString());
+    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     if (!GetProperty(cx, internals, internals, cx->names().type, &value))
         return nullptr;
 
     UPluralType category;
     {
@@ -286,17 +286,17 @@ NewUPluralRules(JSContext* cx, Handle<Pl
             category = UPLURAL_TYPE_CARDINAL;
         } else {
             MOZ_ASSERT(StringEqualsAscii(type, "ordinal"));
             category = UPLURAL_TYPE_ORDINAL;
         }
     }
 
     UErrorCode status = U_ZERO_ERROR;
-    UPluralRules* pr = uplrules_openForType(IcuLocale(locale.ptr()), category, &status);
+    UPluralRules* pr = uplrules_openForType(IcuLocale(locale.get()), category, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return nullptr;
     }
     return pr;
 }
 
 bool
--- a/js/src/builtin/intl/RelativeTimeFormat.cpp
+++ b/js/src/builtin/intl/RelativeTimeFormat.cpp
@@ -10,17 +10,17 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/FloatingPoint.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::IsNegativeZero;
@@ -219,17 +219,17 @@ NewURelativeDateTimeFormatter(JSContext*
     RootedObject internals(cx, intl::GetInternalsObject(cx, relativeTimeFormat));
     if (!internals)
         return nullptr;
 
     RootedValue value(cx);
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    JSAutoByteString locale(cx, value.toString());
+    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     if (!GetProperty(cx, internals, internals, cx->names().style, &value))
         return nullptr;
 
     UDateRelativeDateTimeFormatterStyle relDateTimeStyle;
     {
@@ -244,17 +244,17 @@ NewURelativeDateTimeFormatter(JSContext*
         } else {
             MOZ_ASSERT(StringEqualsAscii(style, "long"));
             relDateTimeStyle = UDAT_STYLE_LONG;
         }
     }
 
     UErrorCode status = U_ZERO_ERROR;
     URelativeDateTimeFormatter* rtf =
-        ureldatefmt_open(IcuLocale(locale.ptr()), nullptr, relDateTimeStyle,
+        ureldatefmt_open(IcuLocale(locale.get()), nullptr, relDateTimeStyle,
                          UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return nullptr;
     }
     return rtf;
 }
 
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -34,19 +34,20 @@
 #include "jsexn.h"
 #include "jsnum.h"
 
 #include "builtin/TypedObject.h"
 #include "ctypes/Library.h"
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/StableStringChars.h"
 #include "js/UniquePtr.h"
+#include "js/Utility.h"
 #include "js/Vector.h"
 #include "util/Windows.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace std;
@@ -976,31 +977,32 @@ static const JSErrorFormatString ErrorFo
 static const JSErrorFormatString*
 GetErrorMessage(void* userRef, const unsigned errorNumber)
 {
   if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
     return &ErrorFormatString[errorNumber];
   return nullptr;
 }
 
-static const char*
-EncodeLatin1(JSContext* cx, AutoString& str, JSAutoByteString& bytes)
-{
-  return bytes.encodeLatin1(cx, NewUCString(cx, str.finish()));
+static JS::UniqueChars
+EncodeLatin1(JSContext* cx, AutoString& str)
+{
+  return JS_EncodeStringToLatin1(cx, NewUCString(cx, str.finish()));
 }
 
 static const char*
-CTypesToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& bytes)
+CTypesToSourceForError(JSContext* cx, HandleValue val, JS::UniqueChars& bytes)
 {
   if (val.isObject()) {
       RootedObject obj(cx, &val.toObject());
       if (CType::IsCType(obj) || CData::IsCDataMaybeUnwrap(&obj)) {
           RootedValue v(cx, ObjectValue(*obj));
           RootedString str(cx, JS_ValueToSource(cx, v));
-          return bytes.encodeLatin1(cx, str);
+          bytes = JS_EncodeStringToLatin1(cx, str);
+          return bytes.get();
       }
   }
   return ValueToSourceForError(cx, val, bytes);
 }
 
 static void
 BuildCStyleFunctionTypeSource(JSContext* cx, HandleObject typeObj,
                               HandleString nameStr, unsigned ptrCount,
@@ -1174,138 +1176,130 @@ BuildTypeSource(JSContext* cx, JSObject*
                 AutoString& result);
 
 static bool
 ConvError(JSContext* cx, const char* expectedStr, HandleValue actual,
           ConversionType convType,
           HandleObject funObj = nullptr, unsigned argIndex = 0,
           HandleObject arrObj = nullptr, unsigned arrIndex = 0)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   if (arrObj) {
     MOZ_ASSERT(CType::IsCType(arrObj));
 
     switch (CType::GetTypeCode(arrObj)) {
     case TYPE_array: {
       MOZ_ASSERT(!funObj);
 
       char indexStr[16];
       SprintfLiteral(indexStr, "%u", arrIndex);
 
       AutoString arrSource;
-      JSAutoByteString arrBytes;
       BuildTypeSource(cx, arrObj, true, arrSource);
       if (!arrSource)
           return false;
-      const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
+      JS::UniqueChars arrStr = EncodeLatin1(cx, arrSource);
       if (!arrStr)
         return false;
 
       JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                                  CTYPESMSG_CONV_ERROR_ARRAY,
-                                 valStr, indexStr, arrStr);
+                                 valStr, indexStr, arrStr.get());
       break;
     }
     case TYPE_struct: {
       JSFlatString* name = GetFieldName(arrObj, arrIndex);
       MOZ_ASSERT(name);
-      JSAutoByteString nameBytes;
-      const char* nameStr = nameBytes.encodeLatin1(cx, name);
+      JS::UniqueChars nameStr = JS_EncodeStringToLatin1(cx, name);
       if (!nameStr)
         return false;
 
       AutoString structSource;
-      JSAutoByteString structBytes;
       BuildTypeSource(cx, arrObj, true, structSource);
       if (!structSource)
           return false;
-      const char* structStr = EncodeLatin1(cx, structSource, structBytes);
+      JS::UniqueChars structStr = EncodeLatin1(cx, structSource);
       if (!structStr)
         return false;
 
-      JSAutoByteString posBytes;
-      const char* posStr;
+      JS::UniqueChars posStr;
       if (funObj) {
         AutoString posSource;
         BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
         if (!posSource)
             return false;
-        posStr = EncodeLatin1(cx, posSource, posBytes);
+        posStr = EncodeLatin1(cx, posSource);
         if (!posStr)
           return false;
-      } else {
-        posStr = "";
       }
 
       JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                                  CTYPESMSG_CONV_ERROR_STRUCT,
-                                 valStr, nameStr, expectedStr, structStr, posStr);
+                                 valStr, nameStr.get(), expectedStr, structStr.get(),
+                                 (posStr ? posStr.get() : ""));
       break;
     }
     default:
       MOZ_CRASH("invalid arrObj value");
     }
     return false;
   }
 
   switch (convType) {
   case ConversionType::Argument: {
     MOZ_ASSERT(funObj);
 
     char indexStr[16];
     SprintfLiteral(indexStr, "%u", argIndex + 1);
 
     AutoString funSource;
-    JSAutoByteString funBytes;
     BuildFunctionTypeSource(cx, funObj, funSource);
     if (!funSource)
         return false;
-    const char* funStr = EncodeLatin1(cx, funSource, funBytes);
+    JS::UniqueChars funStr = EncodeLatin1(cx, funSource);
     if (!funStr)
       return false;
 
     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                                CTYPESMSG_CONV_ERROR_ARG,
-                               valStr, indexStr, funStr);
+                               valStr, indexStr, funStr.get());
     break;
   }
   case ConversionType::Finalizer: {
     MOZ_ASSERT(funObj);
 
     AutoString funSource;
-    JSAutoByteString funBytes;
     BuildFunctionTypeSource(cx, funObj, funSource);
     if (!funSource)
         return false;
-    const char* funStr = EncodeLatin1(cx, funSource, funBytes);
+    JS::UniqueChars funStr = EncodeLatin1(cx, funSource);
     if (!funStr)
       return false;
 
     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                               CTYPESMSG_CONV_ERROR_FIN, valStr, funStr);
+                               CTYPESMSG_CONV_ERROR_FIN, valStr, funStr.get());
     break;
   }
   case ConversionType::Return: {
     MOZ_ASSERT(funObj);
 
     AutoString funSource;
-    JSAutoByteString funBytes;
     BuildFunctionTypeSource(cx, funObj, funSource);
     if (!funSource)
         return false;
-    const char* funStr = EncodeLatin1(cx, funSource, funBytes);
+    JS::UniqueChars funStr = EncodeLatin1(cx, funSource);
     if (!funStr)
       return false;
 
     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                               CTYPESMSG_CONV_ERROR_RET, valStr, funStr);
+                               CTYPESMSG_CONV_ERROR_RET, valStr, funStr.get());
     break;
   }
   case ConversionType::Setter:
   case ConversionType::Construct:
     MOZ_ASSERT(!funObj);
 
     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                                CTYPESMSG_CONV_ERROR_SET, valStr, expectedStr);
@@ -1319,33 +1313,32 @@ static bool
 ConvError(JSContext* cx, HandleObject expectedType, HandleValue actual,
           ConversionType convType,
           HandleObject funObj = nullptr, unsigned argIndex = 0,
           HandleObject arrObj = nullptr, unsigned arrIndex = 0)
 {
   MOZ_ASSERT(CType::IsCType(expectedType));
 
   AutoString expectedSource;
-  JSAutoByteString expectedBytes;
   BuildTypeSource(cx, expectedType, true, expectedSource);
   if (!expectedSource)
       return false;
-  const char* expectedStr = EncodeLatin1(cx, expectedSource, expectedBytes);
+  JS::UniqueChars expectedStr = EncodeLatin1(cx, expectedSource);
   if (!expectedStr)
     return false;
 
-  return ConvError(cx, expectedStr, actual, convType, funObj, argIndex,
+  return ConvError(cx, expectedStr.get(), actual, convType, funObj, argIndex,
                    arrObj, arrIndex);
 }
 
 static bool
 ArgumentConvError(JSContext* cx, HandleValue actual, const char* funStr,
                   unsigned argIndex)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   char indexStr[16];
   SprintfLiteral(indexStr, "%u", argIndex + 1);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
@@ -1364,70 +1357,68 @@ ArgumentLengthError(JSContext* cx, const
 
 static bool
 ArrayLengthMismatch(JSContext* cx, unsigned expectedLength, HandleObject arrObj,
                     unsigned actualLength, HandleValue actual,
                     ConversionType convType)
 {
   MOZ_ASSERT(arrObj && CType::IsCType(arrObj));
 
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   char expectedLengthStr[16];
   SprintfLiteral(expectedLengthStr, "%u", expectedLength);
   char actualLengthStr[16];
   SprintfLiteral(actualLengthStr, "%u", actualLength);
 
   AutoString arrSource;
-  JSAutoByteString arrBytes;
   BuildTypeSource(cx, arrObj, true, arrSource);
   if (!arrSource)
       return false;
-  const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
+  JS::UniqueChars arrStr = EncodeLatin1(cx, arrSource);
   if (!arrStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_ARRAY_MISMATCH,
-                             valStr, arrStr, expectedLengthStr, actualLengthStr);
+                             valStr, arrStr.get(), expectedLengthStr, actualLengthStr);
   return false;
 }
 
 static bool
 ArrayLengthOverflow(JSContext* cx, unsigned expectedLength, HandleObject arrObj,
                     unsigned actualLength, HandleValue actual,
                     ConversionType convType)
 {
   MOZ_ASSERT(arrObj && CType::IsCType(arrObj));
 
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   char expectedLengthStr[16];
   SprintfLiteral(expectedLengthStr, "%u", expectedLength);
   char actualLengthStr[16];
   SprintfLiteral(actualLengthStr, "%u", actualLength);
 
   AutoString arrSource;
-  JSAutoByteString arrBytes;
   BuildTypeSource(cx, arrObj, true, arrSource);
   if (!arrSource)
       return false;
-  const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
+  JS::UniqueChars arrStr = EncodeLatin1(cx, arrSource);
   if (!arrStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_ARRAY_OVERFLOW,
-                             valStr, arrStr, expectedLengthStr, actualLengthStr);
+                             valStr, arrStr.get(), expectedLengthStr, actualLengthStr);
   return false;
 }
 
 static bool
 ArgumentRangeMismatch(JSContext* cx, const char* func, const char* range)
 {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             CTYPESMSG_ARG_RANGE_MISMATCH, func, range);
@@ -1449,301 +1440,288 @@ CannotConstructError(JSContext* cx, cons
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             CTYPESMSG_CANNOT_CONSTRUCT, type);
   return false;
 }
 
 static bool
 DuplicateFieldError(JSContext* cx, Handle<JSFlatString*> name)
 {
-  JSAutoByteString nameBytes;
-  const char* nameStr = nameBytes.encodeLatin1(cx, name);
+  JS::UniqueChars nameStr = JS_EncodeStringToLatin1(cx, name);
   if (!nameStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_DUPLICATE_FIELD, nameStr);
+                             CTYPESMSG_DUPLICATE_FIELD, nameStr.get());
   return false;
 }
 
 static bool
 EmptyFinalizerCallError(JSContext* cx, const char* funName)
 {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             CTYPESMSG_EMPTY_FIN_CALL, funName);
   return false;
 }
 
 static bool
 EmptyFinalizerError(JSContext* cx, ConversionType convType,
                     HandleObject funObj = nullptr, unsigned argIndex = 0)
 {
-  JSAutoByteString posBytes;
-  const char* posStr;
+  JS::UniqueChars posStr;
   if (funObj) {
     AutoString posSource;
     BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
     if (!posSource)
         return false;
-    posStr = EncodeLatin1(cx, posSource, posBytes);
+    posStr = EncodeLatin1(cx, posSource);
     if (!posStr)
       return false;
-  } else {
-    posStr = "";
   }
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_EMPTY_FIN, posStr);
+                             CTYPESMSG_EMPTY_FIN, (posStr ? posStr.get() : ""));
   return false;
 }
 
 static bool
 FieldCountMismatch(JSContext* cx,
                    unsigned expectedCount, HandleObject structObj,
                    unsigned actualCount, HandleValue actual,
                    ConversionType convType,
                    HandleObject funObj = nullptr, unsigned argIndex = 0)
 {
   MOZ_ASSERT(structObj && CType::IsCType(structObj));
 
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   AutoString structSource;
-  JSAutoByteString structBytes;
   BuildTypeSource(cx, structObj, true, structSource);
   if (!structSource)
       return false;
-  const char* structStr = EncodeLatin1(cx, structSource, structBytes);
+  JS::UniqueChars structStr = EncodeLatin1(cx, structSource);
   if (!structStr)
     return false;
 
   char expectedCountStr[16];
   SprintfLiteral(expectedCountStr, "%u", expectedCount);
   char actualCountStr[16];
   SprintfLiteral(actualCountStr, "%u", actualCount);
 
-  JSAutoByteString posBytes;
-  const char* posStr;
+  JS::UniqueChars posStr;
   if (funObj) {
     AutoString posSource;
     BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
     if (!posSource)
         return false;
-    posStr = EncodeLatin1(cx, posSource, posBytes);
+    posStr = EncodeLatin1(cx, posSource);
     if (!posStr)
       return false;
-  } else {
-    posStr = "";
   }
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_FIELD_MISMATCH,
-                             valStr, structStr, expectedCountStr, actualCountStr,
-                             posStr);
+                             valStr, structStr.get(), expectedCountStr, actualCountStr,
+                             (posStr ? posStr.get() : ""));
   return false;
 }
 
 static bool
 FieldDescriptorCountError(JSContext* cx, HandleValue typeVal, size_t length)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   if (!valStr)
     return false;
 
   char lengthStr[16];
   SprintfLiteral(lengthStr, "%zu", length);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_FIELD_DESC_COUNT, valStr, lengthStr);
   return false;
 }
 
 static bool
 FieldDescriptorNameError(JSContext* cx, HandleId id)
 {
-  JSAutoByteString idBytes;
+  JS::UniqueChars idBytes;
   RootedValue idVal(cx, IdToValue(id));
   const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
   if (!propStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_FIELD_DESC_NAME, propStr);
   return false;
 }
 
 static bool
 FieldDescriptorSizeError(JSContext* cx, HandleObject typeObj, HandleId id)
 {
   RootedValue typeVal(cx, ObjectValue(*typeObj));
-  JSAutoByteString typeBytes;
+  JS::UniqueChars typeBytes;
   const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
   if (!typeStr)
     return false;
 
   RootedString idStr(cx, IdToString(cx, id));
-  JSAutoByteString idBytes;
-  const char* propStr = idBytes.encodeLatin1(cx, idStr);
+  JS::UniqueChars propStr = JS_EncodeStringToLatin1(cx, idStr);
   if (!propStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_FIELD_DESC_SIZE, typeStr, propStr);
+                             CTYPESMSG_FIELD_DESC_SIZE, typeStr, propStr.get());
   return false;
 }
 
 static bool
 FieldDescriptorNameTypeError(JSContext* cx, HandleValue typeVal)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_FIELD_DESC_NAMETYPE, valStr);
   return false;
 }
 
 static bool
 FieldDescriptorTypeError(JSContext* cx, HandleValue poroVal, HandleId id)
 {
-  JSAutoByteString typeBytes;
+  JS::UniqueChars typeBytes;
   const char* typeStr = CTypesToSourceForError(cx, poroVal, typeBytes);
   if (!typeStr)
     return false;
 
   RootedString idStr(cx, IdToString(cx, id));
-  JSAutoByteString idBytes;
-  const char* propStr = idBytes.encodeLatin1(cx, idStr);
+  JS::UniqueChars propStr = JS_EncodeStringToLatin1(cx, idStr);
   if (!propStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_FIELD_DESC_TYPE, typeStr, propStr);
+                             CTYPESMSG_FIELD_DESC_TYPE, typeStr, propStr.get());
   return false;
 }
 
 static bool
 FieldMissingError(JSContext* cx, JSObject* typeObj, JSFlatString* name_)
 {
-  JSAutoByteString typeBytes;
+  JS::UniqueChars typeBytes;
   RootedString name(cx, name_);
   RootedValue typeVal(cx, ObjectValue(*typeObj));
   const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
   if (!typeStr)
     return false;
 
-  JSAutoByteString nameBytes;
-  const char* nameStr = nameBytes.encodeLatin1(cx, name);
+  JS::UniqueChars nameStr = JS_EncodeStringToLatin1(cx, name);
   if (!nameStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_FIELD_MISSING, typeStr, nameStr);
+                             CTYPESMSG_FIELD_MISSING, typeStr, nameStr.get());
   return false;
 }
 
 static bool
 FinalizerSizeError(JSContext* cx, HandleObject funObj, HandleValue actual)
 {
   MOZ_ASSERT(CType::IsCType(funObj));
 
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   AutoString funSource;
-  JSAutoByteString funBytes;
   BuildFunctionTypeSource(cx, funObj, funSource);
   if (!funSource)
       return false;
-  const char* funStr = EncodeLatin1(cx, funSource, funBytes);
+  JS::UniqueChars funStr = EncodeLatin1(cx, funSource);
   if (!funStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_FIN_SIZE_ERROR, funStr, valStr);
+                             CTYPESMSG_FIN_SIZE_ERROR, funStr.get(), valStr);
   return false;
 }
 
 static bool
 FunctionArgumentLengthMismatch(JSContext* cx,
                                unsigned expectedCount, unsigned actualCount,
                                HandleObject funObj, HandleObject typeObj,
                                bool isVariadic)
 {
   AutoString funSource;
-  JSAutoByteString funBytes;
   Value slot = JS_GetReservedSlot(funObj, SLOT_REFERENT);
   if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
     BuildFunctionTypeSource(cx, funObj, funSource);
   } else {
     BuildFunctionTypeSource(cx, typeObj, funSource);
   }
   if (!funSource)
       return false;
-  const char* funStr = EncodeLatin1(cx, funSource, funBytes);
+  JS::UniqueChars funStr = EncodeLatin1(cx, funSource);
   if (!funStr)
     return false;
 
   char expectedCountStr[16];
   SprintfLiteral(expectedCountStr, "%u", expectedCount);
   char actualCountStr[16];
   SprintfLiteral(actualCountStr, "%u", actualCount);
 
   const char* variadicStr = isVariadic ? " or more": "";
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_ARG_COUNT_MISMATCH,
-                             funStr, expectedCountStr, variadicStr,
+                             funStr.get(), expectedCountStr, variadicStr,
                              actualCountStr);
   return false;
 }
 
 static bool
 FunctionArgumentTypeError(JSContext* cx,
                           uint32_t index, HandleValue typeVal, const char* reason)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   if (!valStr)
     return false;
 
   char indexStr[16];
   SprintfLiteral(indexStr, "%u", index + 1);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_ARG_TYPE_ERROR,
                              indexStr, reason, valStr);
   return false;
 }
 
 static bool
 FunctionReturnTypeError(JSContext* cx, HandleValue type, const char* reason)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, type, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_RET_TYPE_ERROR, reason, valStr);
   return false;
 }
 
 static bool
 IncompatibleCallee(JSContext* cx, const char* funName, HandleObject actualObj)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   RootedValue val(cx, ObjectValue(*actualObj));
   const char* valStr = CTypesToSourceForError(cx, val, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_INCOMPATIBLE_CALLEE, funName, valStr);
   return false;
@@ -1757,17 +1735,17 @@ IncompatibleThisProto(JSContext* cx, con
                              CTYPESMSG_INCOMPATIBLE_THIS,
                              funName, actualType);
   return false;
 }
 
 static bool
 IncompatibleThisProto(JSContext* cx, const char* funName, HandleValue actualVal)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_INCOMPATIBLE_THIS_VAL,
                              funName, "incompatible object", valStr);
   return false;
@@ -1781,31 +1759,31 @@ IncompatibleThisType(JSContext* cx, cons
                             funName, actualType);
   return false;
 }
 
 static bool
 IncompatibleThisType(JSContext* cx, const char* funName, const char* actualType,
                      HandleValue actualVal)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_INCOMPATIBLE_THIS_VAL,
                              funName, actualType, valStr);
   return false;
 }
 
 static bool
 InvalidIndexError(JSContext* cx, HandleValue val)
 {
-  JSAutoByteString idBytes;
+  JS::UniqueChars idBytes;
   const char* indexStr = CTypesToSourceForError(cx, val, idBytes);
   if (!indexStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_INVALID_INDEX, indexStr);
   return false;
 }
@@ -1832,195 +1810,189 @@ InvalidIndexRangeError(JSContext* cx, si
 }
 
 static bool
 NonPrimitiveError(JSContext* cx, HandleObject typeObj)
 {
   MOZ_ASSERT(CType::IsCType(typeObj));
 
   AutoString typeSource;
-  JSAutoByteString typeBytes;
   BuildTypeSource(cx, typeObj, true, typeSource);
   if (!typeSource)
       return false;
-  const char* typeStr = EncodeLatin1(cx, typeSource, typeBytes);
+  JS::UniqueChars typeStr = EncodeLatin1(cx, typeSource);
   if (!typeStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_NON_PRIMITIVE, typeStr);
+                             CTYPESMSG_NON_PRIMITIVE, typeStr.get());
   return false;
 }
 
 static bool
 NonStringBaseError(JSContext* cx, HandleValue thisVal)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, thisVal, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_NON_STRING_BASE, valStr);
   return false;
 }
 
 static bool
 NullPointerError(JSContext* cx, const char* action, HandleObject obj)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   RootedValue val(cx, ObjectValue(*obj));
   const char* valStr = CTypesToSourceForError(cx, val, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_NULL_POINTER, action, valStr);
   return false;
 }
 
 static bool
 PropNameNonStringError(JSContext* cx, HandleId id, HandleValue actual,
                        ConversionType convType,
                        HandleObject funObj = nullptr, unsigned argIndex = 0)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
-  JSAutoByteString idBytes;
+  JS::UniqueChars idBytes;
   RootedValue idVal(cx, IdToValue(id));
   const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
   if (!propStr)
     return false;
 
-  JSAutoByteString posBytes;
-  const char* posStr;
+  JS::UniqueChars posStr;
   if (funObj) {
     AutoString posSource;
     BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
     if (!posSource)
         return false;
-    posStr = EncodeLatin1(cx, posSource, posBytes);
+    posStr = EncodeLatin1(cx, posSource);
     if (!posStr)
       return false;
-  } else {
-    posStr = "";
   }
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_PROP_NONSTRING, propStr, valStr, posStr);
+                             CTYPESMSG_PROP_NONSTRING, propStr, valStr,
+                             (posStr ? posStr.get() : ""));
   return false;
 }
 
 static bool
 SizeOverflow(JSContext* cx, const char* name, const char* limit)
 {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             CTYPESMSG_SIZE_OVERFLOW, name, limit);
   return false;
 }
 
 static bool
 TypeError(JSContext* cx, const char* expected, HandleValue actual)
 {
-  JSAutoByteString bytes;
+  JS::UniqueChars bytes;
   const char* src = CTypesToSourceForError(cx, actual, bytes);
   if (!src)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_TYPE_ERROR, expected, src);
   return false;
 }
 
 static bool
 TypeOverflow(JSContext* cx, const char* expected, HandleValue actual)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_TYPE_OVERFLOW, valStr, expected);
   return false;
 }
 
 static bool
 UndefinedSizeCastError(JSContext* cx, HandleObject targetTypeObj)
 {
   AutoString targetTypeSource;
-  JSAutoByteString targetTypeBytes;
   BuildTypeSource(cx, targetTypeObj, true, targetTypeSource);
   if (!targetTypeSource)
       return false;
-  const char* targetTypeStr = EncodeLatin1(cx, targetTypeSource, targetTypeBytes);
+  JS::UniqueChars targetTypeStr = EncodeLatin1(cx, targetTypeSource);
   if (!targetTypeStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_UNDEFINED_SIZE_CAST, targetTypeStr);
+                             CTYPESMSG_UNDEFINED_SIZE_CAST, targetTypeStr.get());
   return false;
 }
 
 static bool
 SizeMismatchCastError(JSContext* cx,
                       HandleObject sourceTypeObj, HandleObject targetTypeObj,
                       size_t sourceSize, size_t targetSize)
 {
   AutoString sourceTypeSource;
-  JSAutoByteString sourceTypeBytes;
   BuildTypeSource(cx, sourceTypeObj, true, sourceTypeSource);
   if (!sourceTypeSource)
       return false;
-  const char* sourceTypeStr = EncodeLatin1(cx, sourceTypeSource, sourceTypeBytes);
+  JS::UniqueChars sourceTypeStr = EncodeLatin1(cx, sourceTypeSource);
   if (!sourceTypeStr)
     return false;
 
   AutoString targetTypeSource;
-  JSAutoByteString targetTypeBytes;
   BuildTypeSource(cx, targetTypeObj, true, targetTypeSource);
   if (!targetTypeSource)
       return false;
-  const char* targetTypeStr = EncodeLatin1(cx, targetTypeSource, targetTypeBytes);
+  JS::UniqueChars targetTypeStr = EncodeLatin1(cx, targetTypeSource);
   if (!targetTypeStr)
     return false;
 
   char sourceSizeStr[16];
   char targetSizeStr[16];
   SprintfLiteral(sourceSizeStr, "%zu", sourceSize);
   SprintfLiteral(targetSizeStr, "%zu", targetSize);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_SIZE_MISMATCH_CAST,
-                             targetTypeStr, sourceTypeStr,
+                             targetTypeStr.get(), sourceTypeStr.get(),
                              targetSizeStr, sourceSizeStr);
   return false;
 }
 
 static bool
 UndefinedSizePointerError(JSContext* cx, const char* action, HandleObject obj)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   RootedValue val(cx, ObjectValue(*obj));
   const char* valStr = CTypesToSourceForError(cx, val, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_UNDEFINED_SIZE, action, valStr);
   return false;
 }
 
 static bool
 VariadicArgumentTypeError(JSContext* cx, uint32_t index, HandleValue actual)
 {
-  JSAutoByteString valBytes;
+  JS::UniqueChars valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   char indexStr[16];
   SprintfLiteral(indexStr, "%u", index + 1);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ctypes/Library.h"
 
 #include "prerror.h"
 #include "prlink.h"
 
 #include "ctypes/CTypes.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/StableStringChars.h"
 
 using JS::AutoStableStringChars;
 
 namespace js {
 namespace ctypes {
 
 /*******************************************************************************
@@ -164,23 +164,21 @@ Library::Create(JSContext* cx, HandleVal
 #define MAX_ERROR_LEN 1024
     char error[MAX_ERROR_LEN] = "Cannot get error from NSPR.";
     uint32_t errorLen = PR_GetErrorTextLength();
     if (errorLen && errorLen < MAX_ERROR_LEN)
       PR_GetErrorText(error);
 #undef MAX_ERROR_LEN
 
     if (JS::StringIsASCII(error)) {
-      JSAutoByteString pathCharsUTF8;
-      if (pathCharsUTF8.encodeUtf8(cx, pathStr))
-        JS_ReportErrorUTF8(cx, "couldn't open library %s: %s", pathCharsUTF8.ptr(), error);
+      if (JS::UniqueChars pathCharsUTF8 = JS_EncodeStringToUTF8(cx, pathStr))
+        JS_ReportErrorUTF8(cx, "couldn't open library %s: %s", pathCharsUTF8.get(), error);
     } else {
-      JSAutoByteString pathCharsLatin1;
-      if (pathCharsLatin1.encodeLatin1(cx, pathStr))
-        JS_ReportErrorLatin1(cx, "couldn't open library %s: %s", pathCharsLatin1.ptr(), error);
+      if (JS::UniqueChars pathCharsLatin1 = JS_EncodeStringToLatin1(cx, pathStr))
+        JS_ReportErrorLatin1(cx, "couldn't open library %s: %s", pathCharsLatin1.get(), error);
     }
     return nullptr;
   }
 
   // stash the library
   JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PrivateValue(library));
 
   return libraryObj;
--- a/js/src/frontend/EmitterScope.cpp
+++ b/js/src/frontend/EmitterScope.cpp
@@ -3,17 +3,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "frontend/EmitterScope.h"
 
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/TDZCheckCache.h"
-#include "js/AutoByteString.h"
 
 #include "vm/GlobalObject.h"
 
 using namespace js;
 using namespace js::frontend;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
@@ -395,23 +394,23 @@ EmitterScope::deadZoneFrameSlotRange(Byt
 void
 EmitterScope::dump(BytecodeEmitter* bce)
 {
     fprintf(stdout, "EmitterScope [%s] %p\n", ScopeKindString(scope(bce)->kind()), this);
 
     for (NameLocationMap::Range r = nameCache_->all(); !r.empty(); r.popFront()) {
         const NameLocation& l = r.front().value();
 
-        JSAutoByteString bytes;
-        if (!AtomToPrintableString(bce->cx, r.front().key(), &bytes))
+        UniqueChars bytes = AtomToPrintableString(bce->cx, r.front().key());
+        if (!bytes)
             return;
         if (l.kind() != NameLocation::Kind::Dynamic)
-            fprintf(stdout, "  %s %s ", BindingKindString(l.bindingKind()), bytes.ptr());
+            fprintf(stdout, "  %s %s ", BindingKindString(l.bindingKind()), bytes.get());
         else
-            fprintf(stdout, "  %s ", bytes.ptr());
+            fprintf(stdout, "  %s ", bytes.get());
 
         switch (l.kind()) {
           case NameLocation::Kind::Dynamic:
             fprintf(stdout, "dynamic\n");
             break;
           case NameLocation::Kind::Global:
             fprintf(stdout, "global\n");
             break;
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -45,18 +45,21 @@ class NameResolver
      * given code like a["b c"], the front end will produce a ParseNodeKind::Dot
      * with a ParseNodeKind::Name child whose name contains spaces.
      */
     bool appendPropertyReference(JSAtom* name) {
         if (IsIdentifier(name))
             return buf->append('.') && buf->append(name);
 
         /* Quote the string as needed. */
-        JSString* source = QuoteString(cx, name, '"');
-        return source && buf->append('[') && buf->append(source) && buf->append(']');
+        UniqueChars source = QuoteString(cx, name, '"');
+        return source &&
+               buf->append('[') &&
+               buf->append(source.get(), strlen(source.get())) &&
+               buf->append(']');
     }
 
     /* Append a number to buf. */
     bool appendNumber(double n) {
         char number[30];
         int digits = SprintfLiteral(number, "%g", n);
         return buf->append(number, digits);
     }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -32,17 +32,16 @@
 #include "jstypes.h"
 
 #include "builtin/ModuleObject.h"
 #include "builtin/SelfHostingDefines.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
-#include "js/AutoByteString.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSScript.h"
 #include "vm/RegExpObject.h"
 #include "vm/StringType.h"
 #include "wasm/AsmJS.h"
@@ -166,23 +165,23 @@ void
 ParseContext::Scope::dump(ParseContext* pc)
 {
     JSContext* cx = pc->sc()->context;
 
     fprintf(stdout, "ParseScope %p", this);
 
     fprintf(stdout, "\n  decls:\n");
     for (DeclaredNameMap::Range r = declared_->all(); !r.empty(); r.popFront()) {
-        JSAutoByteString bytes;
-        if (!AtomToPrintableString(cx, r.front().key(), &bytes))
+        UniqueChars bytes = AtomToPrintableString(cx, r.front().key());
+        if (!bytes)
             return;
         DeclaredNameInfo& info = r.front().value().wrapped;
         fprintf(stdout, "    %s %s%s\n",
                 DeclarationKindString(info.kind()),
-                bytes.ptr(),
+                bytes.get(),
                 info.closedOver() ? " (closed over)" : "");
     }
 
     fprintf(stdout, "\n");
 }
 
 bool
 ParseContext::Scope::addPossibleAnnexBFunctionBox(ParseContext* pc, FunctionBox* funbox)
@@ -1222,22 +1221,22 @@ GeneralParser<ParseHandler, CharT>::repo
 }
 
 template <class ParseHandler, typename CharT>
 void
 GeneralParser<ParseHandler, CharT>::reportRedeclaration(HandlePropertyName name,
                                                         DeclarationKind prevKind,
                                                         TokenPos pos, uint32_t prevPos)
 {
-    JSAutoByteString bytes;
-    if (!AtomToPrintableString(context, name, &bytes))
+    UniqueChars bytes = AtomToPrintableString(context, name);
+    if (!bytes)
         return;
 
     if (prevPos == DeclaredNameInfo::npos) {
-        errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.ptr());
+        errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.get());
         return;
     }
 
     auto notes = MakeUnique<JSErrorNotes>();
     if (!notes) {
         ReportOutOfMemory(pc->sc()->context);
         return;
     }
@@ -1256,17 +1255,17 @@ GeneralParser<ParseHandler, CharT>::repo
                              GetErrorMessage, nullptr,
                              JSMSG_REDECLARED_PREV,
                              lineNumber, columnNumber))
     {
         return;
     }
 
     errorWithNotesAt(std::move(notes), pos.begin, JSMSG_REDECLARED_VAR,
-                     DeclarationKindString(prevKind), bytes.ptr());
+                     DeclarationKindString(prevKind), bytes.get());
 }
 
 // notePositionalFormalParameter is called for both the arguments of a regular
 // function definition and the arguments specified by the Function
 // constructor.
 //
 // The 'disallowDuplicateParams' bool indicates whether the use of another
 // feature (destructuring or default arguments) disables duplicate arguments.
@@ -1286,20 +1285,20 @@ GeneralParser<ParseHandler, CharT>::note
             return false;
         }
 
         // Strict-mode disallows duplicate args. We may not know whether we are
         // in strict mode or not (since the function body hasn't been parsed).
         // In such cases, report will queue up the potential error and return
         // 'true'.
         if (pc->sc()->needStrictChecks()) {
-            JSAutoByteString bytes;
-            if (!AtomToPrintableString(context, name, &bytes))
+            UniqueChars bytes = AtomToPrintableString(context, name);
+            if (!bytes)
                 return false;
-            if (!strictModeError(JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
+            if (!strictModeError(JSMSG_DUPLICATE_FORMAL, bytes.get()))
                 return false;
         }
 
         *duplicatedParam = true;
     } else {
         DeclarationKind kind = DeclarationKind::PositionalFormalParameter;
         if (!pc->functionScope().addDeclaredName(pc, p, name, kind, beginPos))
             return false;
@@ -2421,21 +2420,21 @@ Parser<FullParseHandler, CharT>::moduleB
 
     // Check exported local bindings exist and mark them as closed over.
     for (auto entry : modulesc->builder.localExportEntries()) {
         JSAtom* name = entry->localName();
         MOZ_ASSERT(name);
 
         DeclaredNamePtr p = modulepc.varScope().lookupDeclaredName(name);
         if (!p) {
-            JSAutoByteString str;
-            if (!AtomToPrintableString(context, name, &str))
+            UniqueChars str = AtomToPrintableString(context, name);
+            if (!str)
                 return null();
 
-            errorAt(TokenStream::NoOffset, JSMSG_MISSING_EXPORT, str.ptr());
+            errorAt(TokenStream::NoOffset, JSMSG_MISSING_EXPORT, str.get());
             return null();
         }
 
         p->value()->setClosedOver();
     }
 
     if (!FoldConstants(context, &pn, this))
         return null();
@@ -5459,21 +5458,21 @@ GeneralParser<ParseHandler, CharT>::impo
 
 template<typename CharT>
 bool
 Parser<FullParseHandler, CharT>::checkExportedName(JSAtom* exportName)
 {
     if (!pc->sc()->asModuleContext()->builder.hasExportedName(exportName))
         return true;
 
-    JSAutoByteString str;
-    if (!AtomToPrintableString(context, exportName, &str))
+    UniqueChars str = AtomToPrintableString(context, exportName);
+    if (!str)
         return false;
 
-    error(JSMSG_DUPLICATE_EXPORT_NAME, str.ptr());
+    error(JSMSG_DUPLICATE_EXPORT_NAME, str.get());
     return false;
 }
 
 template<typename CharT>
 inline bool
 Parser<SyntaxParseHandler, CharT>::checkExportedName(JSAtom* exportName)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -568,28 +568,28 @@ UniqueChars
 Statistics::renderJsonSlice(size_t sliceNum) const
 {
     Sprinter printer(nullptr, false);
     if (!printer.init())
         return UniqueChars(nullptr);
     JSONPrinter json(printer);
 
     formatJsonSlice(sliceNum, json);
-    return UniqueChars(printer.release());
+    return printer.release();
 }
 
 UniqueChars
 Statistics::renderNurseryJson(JSRuntime* rt) const
 {
     Sprinter printer(nullptr, false);
     if (!printer.init())
         return UniqueChars(nullptr);
     JSONPrinter json(printer);
     rt->gc.nursery().renderProfileJSON(json);
-    return UniqueChars(printer.release());
+    return printer.release();
 }
 
 #ifdef DEBUG
 void
 Statistics::writeLogMessage(const char* fmt, ...)
 {
     va_list args;
     va_start(args, fmt);
@@ -636,17 +636,17 @@ Statistics::renderJsonMessage(uint64_t t
     }
 
     json.beginObjectProperty("totals"); // #24
     formatJsonPhaseTimes(phaseTimes, json);
     json.endObject();
 
     json.endObject();
 
-    return UniqueChars(printer.release());
+    return printer.release();
 }
 
 void
 Statistics::formatJsonDescription(uint64_t timestamp, JSONPrinter& json) const
 {
     // If you change JSON properties here, please update:
     // Telemetry ping code:
     //   toolkit/components/telemetry/GCTelemetry.jsm
--- a/js/src/jit-test/tests/self-test/readlineBuf.js
+++ b/js/src/jit-test/tests/self-test/readlineBuf.js
@@ -4,31 +4,33 @@ assertThrowsInstanceOf(function () { rea
 
 var testBuffers = [
     "foo\nbar\nbaz\n",
     "foo\nbar\nbaz",
     "foo\n\nbar\nbaz",
     "f",
     "\n",
     "\nf",
-    ""
+    "",
+    "Ää\n\u{10ffff}",
 ];
 
 var expected = [
     [ "foo", "bar", "baz" ],
     [ "foo", "bar", "baz" ],
     [ "foo", "", "bar", "baz" ],
     [ "f" ],
     [ "" ],
     [ "", "f" ],
-    []
+    [],
+    ["Ää", "\u{10ffff}"],
 ];
 
-for (var idx in testBuffers) {
-    readlineBuf(testBuffers[idx]);
+for (var [idx, testValue] of testBuffers.entries()) {
+    readlineBuf(testValue);
     var result = [];
 
     while ((line = readlineBuf()) != null) {
         result.push(line);
     }
 
     assertDeepEq(result, expected[idx]);
 }
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -12,17 +12,17 @@
 
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "gc/GC.h"
 #include "js/AllocPolicy.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/Vector.h"
 #include "vm/JSContext.h"
 
 /* Note: Aborts on OOM. */
 class JSAPITestString {
     js::Vector<char, 0, js::SystemAllocPolicy> chars;
 
   public:
@@ -98,21 +98,19 @@ class JSAPITest
     // Like exec(), but doesn't call fail() if JS::Evaluate returns false.
     bool execDontReport(const char* bytes, const char* filename, int lineno);
 
 #define EVAL(s, vp) do { if (!evaluate(s, __FILE__, __LINE__, vp)) return false; } while (false)
 
     bool evaluate(const char* bytes, const char* filename, int lineno, JS::MutableHandleValue vp);
 
     JSAPITestString jsvalToSource(JS::HandleValue v) {
-        JSString* str = JS_ValueToSource(cx, v);
-        if (str) {
-            JSAutoByteString bytes(cx, str);
-            if (!!bytes)
-                return JSAPITestString(bytes.ptr());
+        if (JSString* str = JS_ValueToSource(cx, v)) {
+            if (JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, str))
+                return JSAPITestString(bytes.get());
         }
         JS_ClearPendingException(cx);
         return JSAPITestString("<<error converting value to string>>");
     }
 
     JSAPITestString toSource(long v) {
         char buf[40];
         sprintf(buf, "%ld", v);
@@ -228,19 +226,18 @@ class JSAPITest
 
         if (JS_IsExceptionPending(cx)) {
             js::gc::AutoSuppressGC gcoff(cx);
             JS::RootedValue v(cx);
             JS_GetPendingException(cx, &v);
             JS_ClearPendingException(cx);
             JSString* s = JS::ToString(cx, v);
             if (s) {
-                JSAutoByteString bytes(cx, s);
-                if (!!bytes)
-                    message += bytes.ptr();
+                if (JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, s))
+                    message += bytes.get();
             }
         }
 
         fprintf(stderr, "%.*s\n", int(message.length()), message.begin());
 
         if (msgs.length() != 0)
             msgs += " | ";
         msgs += message;
@@ -268,21 +265,20 @@ class JSAPITest
     print(JSContext* cx, unsigned argc, JS::Value* vp)
     {
         JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
         for (unsigned i = 0; i < args.length(); i++) {
             JSString* str = JS::ToString(cx, args[i]);
             if (!str)
                 return false;
-            char* bytes = JS_EncodeString(cx, str);
+            JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, str);
             if (!bytes)
                 return false;
-            printf("%s%s", i ? " " : "", bytes);
-            JS_free(cx, bytes);
+            printf("%s%s", i ? " " : "", bytes.get());
         }
 
         putchar('\n');
         fflush(stdout);
         args.rval().setUndefined();
         return true;
     }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -50,17 +50,16 @@
 #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "gc/WeakMap.h"
 #include "jit/JitCommon.h"
 #include "jit/JitSpewer.h"
-#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/Initialization.h"
 #include "js/JSON.h"
 #include "js/LocaleSensitive.h"
@@ -171,43 +170,38 @@ JS::ObjectOpResult::reportStrictErrorOrW
     unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_WARNING | JSREPORT_STRICT);
     if (code_ == JSMSG_OBJECT_NOT_EXTENSIBLE) {
         RootedValue val(cx, ObjectValue(*obj));
         return ReportValueErrorFlags(cx, flags, code_, JSDVG_IGNORE_STACK, val,
                                      nullptr, nullptr, nullptr);
     }
 
     if (ErrorTakesArguments(code_)) {
-        RootedValue idv(cx, IdToValue(id));
-        RootedString str(cx, ValueToSource(cx, idv));
-        if (!str)
-            return false;
-
-        JSAutoByteString propName;
-        if (!propName.encodeUtf8(cx, str))
+        UniqueChars propName = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
+        if (!propName)
             return false;
 
         if (code_ == JSMSG_SET_NON_OBJECT_RECEIVER) {
             // We know that the original receiver was a primitive, so unbox it.
             RootedValue val(cx, ObjectValue(*obj));
             if (!obj->is<ProxyObject>()) {
                 if (!Unbox(cx, obj, &val))
                     return false;
             }
             return ReportValueErrorFlags(cx, flags, code_, JSDVG_IGNORE_STACK, val,
-                                         nullptr, propName.ptr(), nullptr);
+                                         nullptr, propName.get(), nullptr);
         }
 
         if (ErrorTakesObjectArgument(code_)) {
             return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr, code_,
-                                                    obj->getClass()->name, propName.ptr());
+                                                    obj->getClass()->name, propName.get());
         }
 
         return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr, code_,
-                                                propName.ptr());
+                                                propName.get());
     }
     return JS_ReportErrorFlagsAndNumberASCII(cx, flags, GetErrorMessage, nullptr, code_);
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, bool strict)
 {
     MOZ_ASSERT(code_ != Uninitialized);
@@ -1644,17 +1638,17 @@ JS::GetFirstArgumentAsTypeHint(JSContext
 
     if (!EqualStrings(cx, str, cx->names().number, &match))
         return false;
     if (match) {
         *result = JSTYPE_NUMBER;
         return true;
     }
 
-    JSAutoByteString bytes;
+    UniqueChars bytes;
     const char* source = ValueToSourceForError(cx, args.get(0), bytes);
     if (!source) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
                                "Symbol.toPrimitive",
@@ -5061,30 +5055,28 @@ JS::GetPromiseResolutionSite(JS::HandleO
 }
 
 #ifdef DEBUG
 JS_PUBLIC_API(void)
 JS::DumpPromiseAllocationSite(JSContext* cx, JS::HandleObject promise)
 {
     RootedObject stack(cx, promise->as<PromiseObject>().allocationSite());
     JSPrincipals* principals = cx->realm()->principals();
-    UniqueChars stackStr(
-        reinterpret_cast<char*>(BuildUTF8StackString(cx, principals, stack).get()));
-    if (stackStr.get())
+    UniqueChars stackStr = BuildUTF8StackString(cx, principals, stack);
+    if (stackStr)
         fputs(stackStr.get(), stderr);
 }
 
 JS_PUBLIC_API(void)
 JS::DumpPromiseResolutionSite(JSContext* cx, JS::HandleObject promise)
 {
     RootedObject stack(cx, promise->as<PromiseObject>().resolutionSite());
     JSPrincipals* principals = cx->realm()->principals();
-    UniqueChars stackStr(
-        reinterpret_cast<char*>(BuildUTF8StackString(cx, principals, stack).get()));
-    if (stackStr.get())
+    UniqueChars stackStr = BuildUTF8StackString(cx, principals, stack);
+    if (stackStr)
         fputs(stackStr.get(), stderr);
 }
 #endif
 
 JS_PUBLIC_API(JSObject*)
 JS::CallOriginalPromiseResolve(JSContext* cx, JS::HandleValue resolutionValue)
 {
     AssertHeapIsIdle();
@@ -6050,32 +6042,32 @@ JS_DecodeBytes(JSContext* cx, const char
         return false;
     }
 
     CopyAndInflateChars(dst, src, srclen);
     *dstlenp = srclen;
     return true;
 }
 
-JS_PUBLIC_API(char*)
-JS_EncodeString(JSContext* cx, JSString* str)
-{
-    AssertHeapIsIdle();
-    CHECK_THREAD(cx);
-
-    return js::EncodeLatin1(cx, str).release();
-}
-
-JS_PUBLIC_API(char*)
+JS_PUBLIC_API(JS::UniqueChars)
+JS_EncodeStringToLatin1(JSContext* cx, JSString* str)
+{
+    AssertHeapIsIdle();
+    CHECK_THREAD(cx);
+
+    return js::EncodeLatin1(cx, str);
+}
+
+JS_PUBLIC_API(JS::UniqueChars)
 JS_EncodeStringToUTF8(JSContext* cx, HandleString str)
 {
     AssertHeapIsIdle();
     CHECK_THREAD(cx);
 
-    return StringToNewUTF8CharsZ(cx, *str).release();
+    return StringToNewUTF8CharsZ(cx, *str);
 }
 
 JS_PUBLIC_API(size_t)
 JS_GetStringEncodingLength(JSContext* cx, JSString* str)
 {
     AssertHeapIsIdle();
     CHECK_THREAD(cx);
 
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -18,17 +18,16 @@
 
 #include "jsapi.h"
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
-#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/UniquePtr.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "vm/ErrorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
@@ -879,17 +878,17 @@ ErrorReport::init(JSContext* cx, HandleV
             str = name;
         } else if (msg) {
             str = msg;
         }
 
         if (JS_GetProperty(cx, exnObject, filename_str, &val)) {
             RootedString tmp(cx, ToString<CanGC>(cx, val));
             if (tmp)
-                filename.encodeUtf8(cx, tmp);
+                filename = JS_EncodeStringToUTF8(cx, tmp);
             else
                 cx->clearPendingException();
         } else {
             cx->clearPendingException();
         }
 
         uint32_t lineno;
         if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &val) ||
@@ -904,17 +903,17 @@ ErrorReport::init(JSContext* cx, HandleV
             !ToUint32(cx, val, &column))
         {
             cx->clearPendingException();
             column = 0;
         }
 
         reportp = &ownedReport;
         new (reportp) JSErrorReport();
-        ownedReport.filename = filename.ptr();
+        ownedReport.filename = filename.get();
         ownedReport.lineno = lineno;
         ownedReport.exnType = JSEXN_INTERNALERR;
         ownedReport.column = column;
         if (str) {
             // Note that using |str| for |message_| here is kind of wrong,
             // because |str| is supposed to be of the format
             // |ErrorName: ErrorMessage|, and |message_| is supposed to
             // correspond to |ErrorMessage|. But this is what we've
@@ -930,18 +929,20 @@ ErrorReport::init(JSContext* cx, HandleV
             } else {
                 cx->clearPendingException();
                 str = nullptr;
             }
         }
     }
 
     const char* utf8Message = nullptr;
-    if (str)
-        utf8Message = toStringResultBytesStorage.encodeUtf8(cx, str);
+    if (str) {
+        toStringResultBytesStorage = JS_EncodeStringToUTF8(cx, str);
+        utf8Message = toStringResultBytesStorage.get();
+    }
     if (!utf8Message)
         utf8Message = "unknown (can't convert to string)";
 
     if (!reportp) {
         // This is basically an inlined version of
         //
         //   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
         //                            JSMSG_UNCAUGHT_EXCEPTION, utf8Message);
@@ -1049,17 +1050,17 @@ JS::CreateError(JSContext* cx, JSExnType
     if (!obj)
         return false;
 
     rval.setObject(*obj);
     return true;
 }
 
 const char*
-js::ValueToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& bytes)
+js::ValueToSourceForError(JSContext* cx, HandleValue val, UniqueChars& bytes)
 {
     if (val.isUndefined())
         return "undefined";
 
     if (val.isNull())
         return "null";
 
     AutoClearPendingException acpe(cx);
@@ -1088,24 +1089,26 @@ js::ValueToSourceForError(JSContext* cx,
     } else if (val.isNumber()) {
         if (!sb.append("the number "))
             return "<<error converting value to string>>";
     } else if (val.isString()) {
         if (!sb.append("the string "))
             return "<<error converting value to string>>";
     } else {
         MOZ_ASSERT(val.isBoolean() || val.isSymbol());
-        return bytes.encodeLatin1(cx, str);
+        bytes = EncodeLatin1(cx, str);
+        return bytes.get();
     }
     if (!sb.append(str))
         return "<<error converting value to string>>";
     str = sb.finishString();
     if (!str)
         return "<<error converting value to string>>";
-    return bytes.encodeLatin1(cx, str);
+    bytes = EncodeLatin1(cx, str);
+    return bytes.get();
 }
 
 bool
 js::GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error)
 {
     FixedInvokeArgs<1> args(cx);
     args[0].set(Int32Value(errorNumber));
     return CallSelfHostedFunction(cx, cx->names().GetInternalError, NullHandleValue, args, error);
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -12,18 +12,16 @@
 #define jsexn_h
 
 #include "jsapi.h"
 #include "NamespaceImports.h"
 
 #include "js/UniquePtr.h"
 #include "vm/JSContext.h"
 
-class JSAutoByteString;
-
 namespace js {
 class ErrorObject;
 
 UniquePtr<JSErrorNotes::Note>
 CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note);
 
 UniquePtr<JSErrorReport>
 CopyErrorReport(JSContext* cx, JSErrorReport* report);
@@ -129,17 +127,17 @@ class AutoAssertNoPendingException
     { }
 
     ~AutoAssertNoPendingException() {
         MOZ_ASSERT(!JS_IsExceptionPending(cx));
     }
 };
 
 extern const char*
-ValueToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& bytes);
+ValueToSourceForError(JSContext* cx, HandleValue val, JS::UniqueChars& bytes);
 
 bool
 GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error);
 bool
 GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error);
 
 } // namespace js
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -2,30 +2,31 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsfriendapi.h"
 
 #include "mozilla/Atomics.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TimeStamp.h"
 
 #include <stdint.h>
 
 #ifdef ENABLE_BIGINT
 #include "builtin/BigInt.h"
 #endif
 #include "builtin/Promise.h"
 #include "builtin/TestingFunctions.h"
 #include "gc/GCInternals.h"
 #include "gc/PublicIterators.h"
 #include "gc/WeakMap.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/Printf.h"
 #include "js/Proxy.h"
 #include "js/Wrapper.h"
 #include "proxy/DeadObjectProxy.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/Realm.h"
@@ -803,71 +804,44 @@ JS_FRIEND_API(bool)
 js::DumpScript(JSContext* cx, JSScript* scriptArg)
 {
     return DumpScript(cx, scriptArg, stdout);
 }
 
 #endif
 
 static const char*
-FormatValue(JSContext* cx, const Value& vArg, JSAutoByteString& bytes)
+FormatValue(JSContext* cx, HandleValue v, UniqueChars& bytes)
 {
-    RootedValue v(cx, vArg);
-
     if (v.isMagic(JS_OPTIMIZED_OUT))
         return "[unavailable]";
 
-    /*
-     * We could use Maybe<AutoRealm> here, but G++ can't quite follow
-     * that, and warns about uninitialized members being used in the
-     * destructor.
-     */
-    RootedString str(cx);
-    if (v.isObject()) {
-        if (IsCrossCompartmentWrapper(&v.toObject()))
-            return "[cross-compartment wrapper]";
-        AutoRealm ar(cx, &v.toObject());
+    if (IsCallable(v))
+        return "[function]";
+
+    if (v.isObject() && IsCrossCompartmentWrapper(&v.toObject()))
+        return "[cross-compartment wrapper]";
+
+    JSString* str;
+    {
+        mozilla::Maybe<AutoRealm> ar;
+        if (v.isObject())
+            ar.emplace(cx, &v.toObject());
+
         str = ToString<CanGC>(cx, v);
-    } else {
-        str = ToString<CanGC>(cx, v);
+        if (!str)
+            return nullptr;
     }
 
-    if (!str)
-        return nullptr;
-    const char* buf = bytes.encodeLatin1(cx, str);
-    if (!buf)
-        return nullptr;
-    const char* found = strstr(buf, "function ");
-    if (found && (found - buf <= 2))
-        return "[function]";
-    return buf;
+    bytes = StringToNewUTF8CharsZ(cx, *str);
+    return bytes.get();
 }
 
-// Wrapper for JS_sprintf_append() that reports allocation failure to the
-// context.
-static JS::UniqueChars
-MOZ_FORMAT_PRINTF(3, 4)
-sprintf_append(JSContext* cx, JS::UniqueChars&& buf, const char* fmt, ...)
-{
-    va_list ap;
-
-    va_start(ap, fmt);
-    JS::UniqueChars result = JS_vsprintf_append(std::move(buf), fmt, ap);
-    va_end(ap);
-
-    if (!result) {
-        ReportOutOfMemory(cx);
-        return nullptr;
-    }
-
-    return result;
-}
-
-static JS::UniqueChars
-FormatFrame(JSContext* cx, const FrameIter& iter, JS::UniqueChars&& inBuf, int num,
+static bool
+FormatFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp, int num,
             bool showArgs, bool showLocals, bool showThisProps)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     RootedScript script(cx, iter.script());
     jsbytecode* pc = iter.pc();
 
     RootedObject envChain(cx, iter.environmentChain(cx));
     JSAutoRealm ar(cx, envChain);
@@ -881,34 +855,33 @@ FormatFrame(JSContext* cx, const FrameIt
 
     RootedValue thisVal(cx);
     if (iter.hasUsableAbstractFramePtr() &&
         iter.isFunctionFrame() &&
         fun && !fun->isArrow() && !fun->isDerivedClassConstructor() &&
         !(fun->isBoundFunction() && iter.isConstructing()))
     {
         if (!GetFunctionThis(cx, iter.abstractFramePtr(), &thisVal))
-            return nullptr;
+            return false;
     }
 
     // print the frame number and function name
-    JS::UniqueChars buf(std::move(inBuf));
     if (funname) {
-        JSAutoByteString funbytes;
-        char* str = funbytes.encodeLatin1(cx, funname);
-        if (!str)
-            return nullptr;
-        buf = sprintf_append(cx, std::move(buf), "%d %s(", num, str);
+        UniqueChars funbytes = StringToNewUTF8CharsZ(cx, *funname);
+        if (!funbytes)
+            return false;
+        if (!sp.printf("%d %s(", num, funbytes.get()))
+            return false;
     } else if (fun) {
-        buf = sprintf_append(cx, std::move(buf), "%d anonymous(", num);
+        if (!sp.printf("%d anonymous(", num))
+            return false;
     } else {
-        buf = sprintf_append(cx, std::move(buf), "%d <TOP LEVEL>", num);
+        if (!sp.printf("%d <TOP LEVEL>", num))
+            return false;
     }
-    if (!buf)
-        return nullptr;
 
     if (showArgs && iter.hasArgs()) {
         PositionalFormalParameterIter fi(script);
         bool first = true;
         for (unsigned i = 0; i < iter.numActualArgs(); i++) {
             RootedValue arg(cx);
             if (i < iter.numFormalArgs() && fi.closedOver()) {
                 arg = iter.callObj(cx).aliasedBinding(fi);
@@ -920,206 +893,204 @@ FormatFrame(JSContext* cx, const FrameIt
                     arg = iter.argsObj().arg(i);
                 } else {
                     arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
                 }
             } else {
                 arg = MagicValue(JS_OPTIMIZED_OUT);
             }
 
-            JSAutoByteString valueBytes;
+            UniqueChars valueBytes;
             const char* value = FormatValue(cx, arg, valueBytes);
             if (!value) {
                 if (cx->isThrowingOutOfMemory())
-                    return nullptr;
+                    return false;
                 cx->clearPendingException();
             }
 
-            JSAutoByteString nameBytes;
+            UniqueChars nameBytes;
             const char* name = nullptr;
 
             if (i < iter.numFormalArgs()) {
                 MOZ_ASSERT(fi.argumentSlot() == i);
                 if (!fi.isDestructured()) {
-                    name = nameBytes.encodeLatin1(cx, fi.name());
+                    nameBytes = StringToNewUTF8CharsZ(cx, *fi.name());
+                    name = nameBytes.get();
                     if (!name)
-                        return nullptr;
+                        return false;
                 } else {
                     name = "(destructured parameter)";
                 }
                 fi++;
             }
 
             if (value) {
-                buf = sprintf_append(cx, std::move(buf), "%s%s%s%s%s%s",
-                                     !first ? ", " : "",
-                                     name ? name :"",
-                                     name ? " = " : "",
-                                     arg.isString() ? "\"" : "",
-                                     value,
-                                     arg.isString() ? "\"" : "");
-                if (!buf)
-                    return nullptr;
+                if (!sp.printf("%s%s%s%s%s%s",
+                               !first ? ", " : "",
+                               name ? name :"",
+                               name ? " = " : "",
+                               arg.isString() ? "\"" : "",
+                               value,
+                               arg.isString() ? "\"" : ""))
+                {
+                    return false;
+                }
 
                 first = false;
             } else {
-                buf = sprintf_append(cx, std::move(buf),
-                                     "    <Failed to get argument while inspecting stack frame>\n");
-                if (!buf)
-                    return nullptr;
+                if (!sp.put("    <Failed to get argument while inspecting stack frame>\n"))
+                    return false;
 
             }
         }
     }
 
     // print filename and line number
-    buf = sprintf_append(cx, std::move(buf), "%s [\"%s\":%d]\n",
-                         fun ? ")" : "",
-                         filename ? filename : "<unknown>",
-                         lineno);
-    if (!buf)
-        return nullptr;
-
+    if (!sp.printf("%s [\"%s\":%d]\n",
+                   fun ? ")" : "",
+                   filename ? filename : "<unknown>",
+                   lineno))
+    {
+        return false;
+    }
 
     // Note: Right now we don't dump the local variables anymore, because
     // that is hard to support across all the JITs etc.
 
     // print the value of 'this'
     if (showLocals) {
         if (!thisVal.isUndefined()) {
-            JSAutoByteString thisValBytes;
             RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal));
             if (!thisValStr) {
                 if (cx->isThrowingOutOfMemory())
-                    return nullptr;
+                    return false;
                 cx->clearPendingException();
             }
             if (thisValStr) {
-                const char* str = thisValBytes.encodeLatin1(cx, thisValStr);
-                if (!str)
-                    return nullptr;
-                buf = sprintf_append(cx, std::move(buf), "    this = %s\n", str);
+                UniqueChars thisValBytes = StringToNewUTF8CharsZ(cx, *thisValStr);
+                if (!thisValBytes)
+                    return false;
+                if (!sp.printf("    this = %s\n", thisValBytes.get()))
+                    return false;
             } else {
-                buf = sprintf_append(cx, std::move(buf), "    <failed to get 'this' value>\n");
+                if (!sp.put("    <failed to get 'this' value>\n"))
+                    return false;
             }
-            if (!buf)
-                return nullptr;
         }
     }
 
     if (showThisProps && thisVal.isObject()) {
         RootedObject obj(cx, &thisVal.toObject());
 
         AutoIdVector keys(cx);
         if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &keys)) {
             if (cx->isThrowingOutOfMemory())
-                return nullptr;
+                return false;
             cx->clearPendingException();
         }
 
-        RootedId id(cx);
         for (size_t i = 0; i < keys.length(); i++) {
             RootedId id(cx, keys[i]);
             RootedValue key(cx, IdToValue(id));
             RootedValue v(cx);
 
             if (!GetProperty(cx, obj, obj, id, &v)) {
                 if (cx->isThrowingOutOfMemory())
-                    return nullptr;
+                    return false;
                 cx->clearPendingException();
-                buf = sprintf_append(cx, std::move(buf),
-                                     "    <Failed to fetch property while inspecting stack frame>\n");
-                if (!buf)
-                    return nullptr;
+                if (!sp.put("    <Failed to fetch property while inspecting stack frame>\n"))
+                    return false;
                 continue;
             }
 
-            JSAutoByteString nameBytes;
+            UniqueChars nameBytes;
             const char* name = FormatValue(cx, key, nameBytes);
             if (!name) {
                 if (cx->isThrowingOutOfMemory())
-                    return nullptr;
+                    return false;
                 cx->clearPendingException();
             }
 
-            JSAutoByteString valueBytes;
+            UniqueChars valueBytes;
             const char* value = FormatValue(cx, v, valueBytes);
             if (!value) {
                 if (cx->isThrowingOutOfMemory())
-                    return nullptr;
+                    return false;
                 cx->clearPendingException();
             }
 
             if (name && value) {
-                buf = sprintf_append(cx, std::move(buf), "    this.%s = %s%s%s\n",
-                                     name,
-                                     v.isString() ? "\"" : "",
-                                     value,
-                                     v.isString() ? "\"" : "");
+                if (!sp.printf("    this.%s = %s%s%s\n",
+                               name,
+                               v.isString() ? "\"" : "",
+                               value,
+                               v.isString() ? "\"" : ""))
+                {
+                    return false;
+                }
             } else {
-                buf = sprintf_append(cx, std::move(buf),
-                                     "    <Failed to format values while inspecting stack frame>\n");
+                if (!sp.put("    <Failed to format values while inspecting stack frame>\n"))
+                    return false;
             }
-            if (!buf)
-                return nullptr;
         }
     }
 
     MOZ_ASSERT(!cx->isExceptionPending());
-    return buf;
+    return true;
 }
 
-static JS::UniqueChars
-FormatWasmFrame(JSContext* cx, const FrameIter& iter, JS::UniqueChars&& inBuf, int num)
+static bool
+FormatWasmFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp, int num)
 {
     UniqueChars nameStr;
     if (JSAtom* functionDisplayAtom = iter.maybeFunctionDisplayAtom()) {
         nameStr = StringToNewUTF8CharsZ(cx, *functionDisplayAtom);
         if (!nameStr)
-            return nullptr;
+            return false;
     }
 
-    JS::UniqueChars buf = sprintf_append(cx, std::move(inBuf), "%d %s()",
-                                         num,
-                                         nameStr ? nameStr.get() : "<wasm-function>");
-    if (!buf)
-        return nullptr;
+    if (!sp.printf("%d %s()", num, nameStr ? nameStr.get() : "<wasm-function>"))
+        return false;
 
-    buf = sprintf_append(cx, std::move(buf), " [\"%s\":wasm-function[%d]:0x%x]\n",
-                         iter.filename() ? iter.filename() : "<unknown>",
-                         iter.wasmFuncIndex(),
-                         iter.wasmBytecodeOffset());
-    if (!buf)
-        return nullptr;
+    if (!sp.printf(" [\"%s\":wasm-function[%d]:0x%x]\n",
+                   iter.filename() ? iter.filename() : "<unknown>",
+                   iter.wasmFuncIndex(),
+                   iter.wasmBytecodeOffset()))
+    {
+        return false;
+    }
 
     MOZ_ASSERT(!cx->isExceptionPending());
-    return buf;
+    return true;
 }
 
 JS_FRIEND_API(JS::UniqueChars)
-JS::FormatStackDump(JSContext* cx, JS::UniqueChars&& inBuf, bool showArgs, bool showLocals,
-                    bool showThisProps)
+JS::FormatStackDump(JSContext* cx, bool showArgs, bool showLocals, bool showThisProps)
 {
     int num = 0;
 
-    JS::UniqueChars buf(std::move(inBuf));
+    Sprinter sp(cx);
+    if (!sp.init())
+        return nullptr;
+
     for (AllFramesIter i(cx); !i.done(); ++i) {
-        if (i.hasScript())
-            buf = FormatFrame(cx, i, std::move(buf), num, showArgs, showLocals, showThisProps);
-        else
-            buf = FormatWasmFrame(cx, i, std::move(buf), num);
-        if (!buf)
+        bool ok = i.hasScript()
+                  ? FormatFrame(cx, i, sp, num, showArgs, showLocals, showThisProps)
+                  : FormatWasmFrame(cx, i, sp, num);
+        if (!ok)
             return nullptr;
         num++;
     }
 
-    if (!num)
-        buf = JS_sprintf_append(std::move(buf), "JavaScript stack is empty\n");
+    if (num == 0) {
+        if (!sp.put("JavaScript stack is empty\n"))
+            return nullptr;
+    }
 
-    return buf;
+    return sp.release();
 }
 
 extern JS_FRIEND_API(bool)
 JS::ForceLexicalInitialization(JSContext *cx, HandleObject obj)
 {
     AssertHeapIsIdle();
     CHECK_THREAD(cx);
     cx->check(obj);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -10,17 +10,16 @@
 #include "mozilla/Atomics.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/UniquePtr.h"
 
 #include "jspubtd.h"
 
-#include "js/AutoByteString.h"
 #include "js/CallArgs.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/CharacterEncoding.h"
 #include "js/Class.h"
 #include "js/ErrorReport.h"
 #include "js/HeapAPI.h"
 #include "js/StableStringChars.h"
 #include "js/TypeDecls.h"
@@ -297,18 +296,17 @@ extern JS_FRIEND_API(void)
 DumpBacktrace(JSContext* cx);
 
 } // namespace js
 
 namespace JS {
 
 /** Exposed for DumpJSStack */
 extern JS_FRIEND_API(JS::UniqueChars)
-FormatStackDump(JSContext* cx, JS::UniqueChars&& buf, bool showArgs, bool showLocals,
-                bool showThisProps);
+FormatStackDump(JSContext* cx, bool showArgs, bool showLocals, bool showThisProps);
 
 /**
  * Set all of the uninitialized lexicals on an object to undefined. Return
  * true if any lexicals were initialized and false otherwise.
  * */
 extern JS_FRIEND_API(bool)
 ForceLexicalInitialization(JSContext *cx, HandleObject obj);
 
@@ -1459,23 +1457,23 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(Err
 
     // And keep its chars alive too.
     JS::AutoStableStringChars strChars;
 
     // And we need to root our exception value.
     JS::RootedObject exnObject;
 
     // And for our filename.
-    JSAutoByteString filename;
+    JS::UniqueChars filename;
 
     // We may have a result of error.toString().
     // FIXME: We should not call error.toString(), since it could have side
     //        effect (see bug 633623).
     JS::ConstUTF8CharsZ toStringResult_;
-    JSAutoByteString toStringResultBytesStorage;
+    JS::UniqueChars toStringResultBytesStorage;
 };
 
 /* Implemented in vm/StructuredClone.cpp. */
 extern JS_FRIEND_API(uint64_t)
 GetSCOffset(JSStructuredCloneWriter* writer);
 
 namespace Scalar {
 
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -22,16 +22,17 @@
 #endif
 #include <math.h>
 #include <string.h>
 
 #include "jstypes.h"
 
 #include "builtin/String.h"
 #include "double-conversion/double-conversion.h"
+#include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
 #if !EXPOSE_INTL_API
 #include "js/LocaleSensitive.h"
 #endif
 #include "util/DoubleToString.h"
 #include "util/StringBuffer.h"
 #ifdef ENABLE_BIGINT
 #include "vm/BigIntType.h"
@@ -791,20 +792,20 @@ num_toLocaleString_impl(JSContext* cx, c
         JS_ReportOutOfMemory(cx);
         return false;
     }
 
     /*
      * Create the string, move back to bytes to make string twiddling
      * a bit easier and so we can insert platform charset seperators.
      */
-    JSAutoByteString numBytes(cx, str);
+    UniqueChars numBytes = JS_EncodeStringToLatin1(cx, str);
     if (!numBytes)
         return false;
-    const char* num = numBytes.ptr();
+    const char* num = numBytes.get();
     if (!num)
         return false;
 
     /*
      * Find the first non-integer value, whether it be a letter as in
      * 'Infinity', a decimal point, or an 'e' from exponential notation.
      */
     const char* nint = num;
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -119,17 +119,16 @@ EXPORTS += [
     'jsfriendapi.h',
     'jspubtd.h',
     'jstypes.h',
     'perf/jsperf.h',
 ]
 
 EXPORTS.js += [
     '../public/AllocPolicy.h',
-    '../public/AutoByteString.h',
     '../public/CallArgs.h',
     '../public/CallNonGenericMethod.h',
     '../public/CharacterEncoding.h',
     '../public/Class.h',
     '../public/CompilationAndEvaluation.h',
     '../public/CompileOptions.h',
     '../public/Conversions.h',
     '../public/Date.h',
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -218,17 +218,17 @@ pm_finalize(JSFreeOp* fop, JSObject* obj
 
 static PerfMeasurement*
 GetPM(JSContext* cx, JS::HandleValue value, const char* fname)
 {
     if (!value.isObject()) {
         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr);
         if (!bytes)
             return nullptr;
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
         return nullptr;
     }
     RootedObject obj(cx, &value.toObject());
     PerfMeasurement* p = (PerfMeasurement*)
         JS_GetInstancePrivate(cx, obj, &pm_class, nullptr);
     if (p)
         return p;
 
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -23,39 +23,31 @@
 #include "gc/Marking-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
-using JS::AutoStableStringChars;
-
 void
-js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext* cx, jsid id)
+js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext* cx, HandleId id)
 {
     if (JS_IsExceptionPending(cx))
         return;
 
     if (JSID_IS_VOID(id)) {
         ReportAccessDenied(cx);
     } else {
-        RootedValue idVal(cx, IdToValue(id));
-        JSString* str = ValueToSource(cx, idVal);
-        if (!str) {
+        UniqueChars prop = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
+        if (!prop)
             return;
-        }
-        AutoStableStringChars chars(cx);
-        const char16_t* prop = nullptr;
-        if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
-            prop = chars.twoByteChars();
 
-        JS_ReportErrorNumberUC(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_ACCESS_DENIED,
-                               prop);
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_ACCESS_DENIED,
+                                 prop.get());
     }
 }
 
 #ifdef DEBUG
 void
 js::AutoEnterPolicy::recordEnter(JSContext* cx, HandleObject proxy, HandleId id, Action act)
 {
     if (allowed()) {
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -3,17 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "proxy/ScriptedProxyHandler.h"
 
 #include "jsapi.h"
 
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "vm/Interpreter.h" // For InstanceOfOperator
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using JS::IsArrayAnswer;
@@ -174,21 +174,21 @@ GetProxyTrap(JSContext* cx, HandleObject
 
     if (func.isNull()) {
         func.setUndefined();
         return true;
     }
 
     // Step 4.
     if (!IsCallable(func)) {
-        JSAutoByteString bytes(cx, name);
+        UniqueChars bytes = EncodeLatin1(cx, name);
         if (!bytes)
             return false;
 
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_BAD_TRAP, bytes.ptr());
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_BAD_TRAP, bytes.get());
         return false;
     }
 
     return true;
 }
 
 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.1 Proxy.[[GetPrototypeOf]].
 bool
@@ -826,18 +826,20 @@ ScriptedProxyHandler::ownPropertyKeys(JS
         if (!ptr)
             return js::Throw(cx, targetConfigurableKeys[i], JSMSG_CANT_REPORT_E_AS_NE);
 
         // Step 21.b.
         uncheckedResultKeys.remove(ptr);
     }
 
     // Step 22.
-    if (!uncheckedResultKeys.empty())
-        return js::Throw(cx, uncheckedResultKeys.all().front(), JSMSG_CANT_REPORT_NEW);
+    if (!uncheckedResultKeys.empty()) {
+        RootedId id(cx, uncheckedResultKeys.all().front());
+        return js::Throw(cx, id, JSMSG_CANT_REPORT_NEW);
+    }
 
     // Step 23.
     return props.appendAll(trapResult);
 }
 
 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.10 Proxy.[[Delete]](P)
 bool
 ScriptedProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id,
@@ -884,18 +886,21 @@ ScriptedProxyHandler::delete_(JSContext*
 
     // Step 10.
     Rooted<PropertyDescriptor> desc(cx);
     if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
         return false;
 
     // Step 12.
     if (desc.object() && !desc.configurable()) {
-        RootedValue v(cx, IdToValue(id));
-        ReportValueError(cx, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, v, nullptr);
+        UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
+        if (!bytes)
+            return false;
+
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_CANT_DELETE, bytes.get());
         return false;
     }
 
     // Steps 11,13.
     return result.succeed();
 }
 
 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.7 Proxy.[[HasProperty]](P)
--- a/js/src/proxy/SecurityWrapper.cpp
+++ b/js/src/proxy/SecurityWrapper.cpp
@@ -108,26 +108,22 @@ SecurityWrapper<Base>::boxedValue_unbox(
 
 template <class Base>
 bool
 SecurityWrapper<Base>::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
                                       Handle<PropertyDescriptor> desc,
                                       ObjectOpResult& result) const
 {
     if (desc.getter() || desc.setter()) {
-        RootedValue idVal(cx, IdToValue(id));
-        JSString* str = ValueToSource(cx, idVal);
-        if (!str)
+        UniqueChars prop = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
+        if (!prop)
             return false;
-        AutoStableStringChars chars(cx);
-        const char16_t* prop = nullptr;
-        if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
-            prop = chars.twoByteChars();
-        JS_ReportErrorNumberUC(cx, GetErrorMessage, nullptr,
-                               JSMSG_ACCESSOR_DEF_DENIED, prop);
+
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_ACCESSOR_DEF_DENIED,
+                                 prop.get());
         return false;
     }
 
     return Base::defineProperty(cx, wrapper, id, desc, result);
 }
 
 template class js::SecurityWrapper<Wrapper>;
 template class js::SecurityWrapper<CrossCompartmentWrapper>;
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -20,17 +20,17 @@
 #endif
 
 #include "jsapi.h"
 // For JSFunctionSpecWithHelp
 #include "jsfriendapi.h"
 
 #include "builtin/String.h"
 #include "gc/FreeOp.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
 #include "js/Wrapper.h"
 #include "shell/jsshell.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/JSObject.h"
 #include "vm/TypedArrayObject.h"
@@ -53,19 +53,19 @@ namespace shell {
 
 #ifdef XP_WIN
 const char PathSeparator = '\\';
 #else
 const char PathSeparator = '/';
 #endif
 
 static bool
-IsAbsolutePath(const JSAutoByteString& filename)
+IsAbsolutePath(const UniqueChars& filename)
 {
-    const char* pathname = filename.ptr();
+    const char* pathname = filename.get();
 
     if (pathname[0] == PathSeparator)
         return true;
 
 #ifdef XP_WIN
     // On Windows there are various forms of absolute paths (see
     // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
     // for details):
@@ -101,17 +101,17 @@ ResolvePath(JSContext* cx, HandleString 
     if (!filenameStr) {
 #ifdef XP_WIN
         return JS_NewStringCopyZ(cx, "nul");
 #else
         return JS_NewStringCopyZ(cx, "/dev/null");
 #endif
     }
 
-    JSAutoByteString filename(cx, filenameStr);
+    UniqueChars filename = JS_EncodeStringToLatin1(cx, filenameStr);
     if (!filename)
         return nullptr;
 
     if (IsAbsolutePath(filename))
         return filenameStr;
 
     JS::AutoFilename scriptFilename;
     if (resolveMode == ScriptRelative) {
@@ -143,89 +143,89 @@ ResolvePath(JSContext* cx, HandleString 
     } else {
         const char* cwd = getcwd(buffer, PATH_MAX);
         if (!cwd)
             return nullptr;
     }
 
     size_t len = strlen(buffer);
     buffer[len] = '/';
-    strncpy(buffer + len + 1, filename.ptr(), sizeof(buffer) - (len+1));
+    strncpy(buffer + len + 1, filename.get(), sizeof(buffer) - (len+1));
     if (buffer[PATH_MAX] != '\0')
         return nullptr;
 
     return JS_NewStringCopyZ(cx, buffer);
 }
 
 JSObject*
 FileAsTypedArray(JSContext* cx, JS::HandleString pathnameStr)
 {
-    JSAutoByteString pathname(cx, pathnameStr);
+    UniqueChars pathname = JS_EncodeStringToLatin1(cx, pathnameStr);
     if (!pathname)
         return nullptr;
 
-    FILE* file = fopen(pathname.ptr(), "rb");
+    FILE* file = fopen(pathname.get(), "rb");
     if (!file) {
         /*
          * Use Latin1 variant here because the encoding of the return value of
          * strerror function can be non-UTF-8.
          */
-        JS_ReportErrorLatin1(cx, "can't open %s: %s", pathname.ptr(), strerror(errno));
+        JS_ReportErrorLatin1(cx, "can't open %s: %s", pathname.get(), strerror(errno));
         return nullptr;
     }
     AutoCloseFile autoClose(file);
 
     RootedObject obj(cx);
     if (fseek(file, 0, SEEK_END) != 0) {
-        pathname.clear();
-        if (!pathname.encodeUtf8(cx, pathnameStr))
+        pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
+        if (!pathname)
             return nullptr;
-        JS_ReportErrorUTF8(cx, "can't seek end of %s", pathname.ptr());
+        JS_ReportErrorUTF8(cx, "can't seek end of %s", pathname.get());
     } else {
         size_t len = ftell(file);
         if (fseek(file, 0, SEEK_SET) != 0) {
-            pathname.clear();
-            if (!pathname.encodeUtf8(cx, pathnameStr))
+            pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
+            if (!pathname)
                 return nullptr;
-            JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.ptr());
+            JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.get());
         } else {
             obj = JS_NewUint8Array(cx, len);
             if (!obj)
                 return nullptr;
             js::TypedArrayObject& ta = obj->as<js::TypedArrayObject>();
             if (ta.isSharedMemory()) {
                 // Must opt in to use shared memory.  For now, don't.
                 //
                 // (It is incorrect to read into the buffer without
                 // synchronization since that can create a race.  A
                 // lock here won't fix it - both sides must
                 // participate.  So what one must do is to create a
                 // temporary buffer, read into that, and use a
                 // race-safe primitive to copy memory into the
                 // buffer.)
-                pathname.clear();
-                if (!pathname.encodeUtf8(cx, pathnameStr))
+                pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
+                if (!pathname)
                     return nullptr;
-                JS_ReportErrorUTF8(cx, "can't read %s: shared memory buffer", pathname.ptr());
+                JS_ReportErrorUTF8(cx, "can't read %s: shared memory buffer", pathname.get());
                 return nullptr;
             }
             char* buf = static_cast<char*>(ta.viewDataUnshared());
             size_t cc = fread(buf, 1, len, file);
             if (cc != len) {
                 if (ptrdiff_t(cc) < 0) {
                     /*
                      * Use Latin1 variant here because the encoding of the return
                      * value of strerror function can be non-UTF-8.
                      */
-                    JS_ReportErrorLatin1(cx, "can't read %s: %s", pathname.ptr(), strerror(errno));
+                    JS_ReportErrorLatin1(cx, "can't read %s: %s", pathname.get(), strerror(errno));
                 } else {
-                    pathname.clear();
-                    if (!pathname.encodeUtf8(cx, pathnameStr))
+                    pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
+                    if (!pathname)
                         return nullptr;
-                    JS_ReportErrorUTF8(cx, "can't read %s: short read", pathname.ptr());
+                    JS_ReportErrorUTF8(cx, "can't read %s: short read", pathname.get());
                 }
                 obj = nullptr;
             }
         }
     }
     return obj;
 }
 
@@ -314,51 +314,51 @@ osfile_writeTypedArrayToFile(JSContext* 
         return false;
     }
 
     RootedString givenPath(cx, args[0].toString());
     RootedString str(cx, ResolvePath(cx, givenPath, RootRelative));
     if (!str)
         return false;
 
-    JSAutoByteString filename(cx, str);
+    UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
     if (!filename)
         return false;
 
-    FILE* file = fopen(filename.ptr(), "wb");
+    FILE* file = fopen(filename.get(), "wb");
     if (!file) {
         /*
          * Use Latin1 variant here because the encoding of the return value of
          * strerror function can be non-UTF-8.
          */
-        JS_ReportErrorLatin1(cx, "can't open %s: %s", filename.ptr(), strerror(errno));
+        JS_ReportErrorLatin1(cx, "can't open %s: %s", filename.get(), strerror(errno));
         return false;
     }
     AutoCloseFile autoClose(file);
 
     TypedArrayObject* obj = &args[1].toObject().as<TypedArrayObject>();
 
     if (obj->isSharedMemory()) {
         // Must opt in to use shared memory.  For now, don't.
         //
         // See further comments in FileAsTypedArray, above.
-        filename.clear();
-        if (!filename.encodeUtf8(cx, str))
+        filename = JS_EncodeStringToUTF8(cx, str);
+        if (!filename)
             return false;
-        JS_ReportErrorUTF8(cx, "can't write %s: shared memory buffer", filename.ptr());
+        JS_ReportErrorUTF8(cx, "can't write %s: shared memory buffer", filename.get());
         return false;
     }
     void* buf = obj->viewDataUnshared();
     if (fwrite(buf, obj->bytesPerElement(), obj->length(), file) != obj->length() ||
         !autoClose.release())
     {
-        filename.clear();
-        if (!filename.encodeUtf8(cx, str))
+        filename = JS_EncodeStringToUTF8(cx, str);
+        if (!filename)
             return false;
-        JS_ReportErrorUTF8(cx, "can't write %s", filename.ptr());
+        JS_ReportErrorUTF8(cx, "can't write %s", filename.get());
         return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 /* static */ RCFile*
@@ -467,26 +467,26 @@ const js::Class FileObject::class_ = {
 };
 
 static FileObject*
 redirect(JSContext* cx, HandleString relFilename, RCFile** globalFile)
 {
     RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative));
     if (!filename)
         return nullptr;
-    JSAutoByteString filenameABS(cx, filename);
+    UniqueChars filenameABS = JS_EncodeStringToLatin1(cx, filename);
     if (!filenameABS)
         return nullptr;
-    RCFile* file = RCFile::create(cx, filenameABS.ptr(), "wb");
+    RCFile* file = RCFile::create(cx, filenameABS.get(), "wb");
     if (!file) {
         /*
          * Use Latin1 variant here because the encoding of the return value of
          * strerror function can be non-UTF-8.
          */
-        JS_ReportErrorLatin1(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno));
+        JS_ReportErrorLatin1(cx, "cannot redirect to %s: %s", filenameABS.get(), strerror(errno));
         return nullptr;
     }
 
     // Grant the global gOutFile ownership of the new file, release ownership
     // of its old file, and return a FileObject owning the old file.
     file->acquire(); // Global owner of new file
 
     FileObject* fileObj = FileObject::create(cx, *globalFile); // Newly created owner of old file
@@ -631,17 +631,17 @@ ospath_isAbsolute(JSContext* cx, unsigne
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1 || !args[0].isString()) {
         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
                                   "isAbsolute");
         return false;
     }
 
-    JSAutoByteString path(cx, args[0].toString());
+    UniqueChars path = JS_EncodeStringToLatin1(cx, args[0].toString());
     if (!path)
         return false;
 
     args.rval().setBoolean(IsAbsolutePath(path));
     return true;
 }
 
 static bool
@@ -660,17 +660,17 @@ ospath_join(JSContext* cx, unsigned argc
     StringBuffer buffer(cx);
 
     for (unsigned i = 0; i < args.length(); i++) {
         if (!args[i].isString()) {
             JS_ReportErrorASCII(cx, "join expects string arguments only");
             return false;
         }
 
-        JSAutoByteString path(cx, args[i].toString());
+        UniqueChars path = JS_EncodeStringToLatin1(cx, args[i].toString());
         if (!path)
             return false;
 
         if (IsAbsolutePath(path)) {
             MOZ_ALWAYS_TRUE(buffer.resize(0));
         } else if (i != 0) {
             if (!buffer.append(PathSeparator))
                 return false;
@@ -706,21 +706,21 @@ os_getenv(JSContext* cx, unsigned argc, 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() < 1) {
         JS_ReportErrorASCII(cx, "os.getenv requires 1 argument");
         return false;
     }
     RootedString key(cx, ToString(cx, args[0]));
     if (!key)
         return false;
-    JSAutoByteString keyBytes;
-    if (!keyBytes.encodeUtf8(cx, key))
+    UniqueChars keyBytes = JS_EncodeStringToUTF8(cx, key);
+    if (!keyBytes)
         return false;
 
-    if (const char* valueBytes = getenv(keyBytes.ptr())) {
+    if (const char* valueBytes = getenv(keyBytes.get())) {
         RootedString value(cx, JS_NewStringCopyZ(cx, valueBytes));
         if (!value)
             return false;
         args.rval().setString(value);
     } else {
         args.rval().setUndefined();
     }
     return true;
@@ -790,21 +790,21 @@ os_system(JSContext* cx, unsigned argc, 
         JS_ReportErrorASCII(cx, "os.system requires 1 argument");
         return false;
     }
 
     JSString* str = JS::ToString(cx, args[0]);
     if (!str)
         return false;
 
-    JSAutoByteString command(cx, str);
+    UniqueChars command = JS_EncodeStringToLatin1(cx, str);
     if (!command)
         return false;
 
-    int result = system(command.ptr());
+    int result = system(command.get());
     if (result == -1) {
         ReportSysError(cx, "system call failed");
         return false;
     }
 
     args.rval().setInt32(result);
     return true;
 }
@@ -819,17 +819,17 @@ os_spawn(JSContext* cx, unsigned argc, V
         JS_ReportErrorASCII(cx, "os.spawn requires 1 argument");
         return false;
     }
 
     JSString* str = JS::ToString(cx, args[0]);
     if (!str)
         return false;
 
-    JSAutoByteString command(cx, str);
+    UniqueChars command = JS_EncodeStringToLatin1(cx, str);
     if (!command)
         return false;
 
     int32_t childPid = fork();
     if (childPid == -1) {
         ReportSysError(cx, "fork failed");
         return false;
     }
@@ -837,17 +837,17 @@ os_spawn(JSContext* cx, unsigned argc, V
     if (childPid) {
         args.rval().setInt32(childPid);
         return true;
     }
 
     // We are in the child
 
     const char* cmd[] = {"sh", "-c", nullptr, nullptr};
-    cmd[2] = command.ptr();
+    cmd[2] = command.get();
 
     execvp("sh", (char * const*)cmd);
     exit(1);
 }
 
 static bool
 os_kill(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -72,17 +72,17 @@
 #include "frontend/Parser.h"
 #include "gc/PublicIterators.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitRealm.h"
 #include "jit/shared/CodeGenerator-shared.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Debug.h"
 #include "js/GCVector.h"
 #include "js/Initialization.h"
 #include "js/JSON.h"
 #include "js/Printf.h"
 #include "js/SourceBufferHolder.h"
@@ -1048,46 +1048,51 @@ BoundToAsyncStack(JSContext* cx, unsigne
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedFunction function(cx, (&GetFunctionNativeReserved(&args.callee(), 0)
                                  .toObject().as<JSFunction>()));
     RootedObject options(cx, &GetFunctionNativeReserved(&args.callee(), 1).toObject());
 
     RootedSavedFrame stack(cx, nullptr);
-    JSAutoByteString cause;
     bool isExplicit;
 
     RootedValue v(cx);
 
     if (!JS_GetProperty(cx, options, "stack", &v))
         return false;
     if (!v.isObject() || !v.toObject().is<SavedFrame>()) {
         JS_ReportErrorASCII(cx, "The 'stack' property must be a SavedFrame object.");
         return false;
     }
     stack = &v.toObject().as<SavedFrame>();
 
     if (!JS_GetProperty(cx, options, "cause", &v))
         return false;
     RootedString causeString(cx, ToString(cx, v));
-    if (!causeString || !cause.encodeUtf8(cx, causeString)) {
+    if (!causeString) {
+        MOZ_ASSERT(cx->isExceptionPending());
+        return false;
+    }
+
+    UniqueChars cause = JS_EncodeStringToUTF8(cx, causeString);
+    if (!cause) {
         MOZ_ASSERT(cx->isExceptionPending());
         return false;
     }
 
     if (!JS_GetProperty(cx, options, "explicit", &v))
         return false;
     isExplicit = v.isUndefined() ? true : ToBoolean(v);
 
     auto kind = (isExplicit
                  ? JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT
                  : JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::IMPLICIT);
 
-    JS::AutoSetAsyncStackForNewCalls asasfnckthxbye(cx, stack, cause.ptr(), kind);
+    JS::AutoSetAsyncStackForNewCalls asasfnckthxbye(cx, stack, cause.get(), kind);
     return Call(cx, UndefinedHandleValue, function,
                 JS::HandleValueArray::empty(), args.rval());
 }
 
 static bool
 BindToAsyncStack(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1166,26 +1171,24 @@ EvalAndPrint(JSContext* cx, const char* 
     if (compileOnly)
         return true;
     RootedValue result(cx);
     if (!JS_ExecuteScript(cx, script, &result))
         return false;
 
     if (!result.isUndefined() && gOutFile->isOpen()) {
         // Print.
-        RootedString str(cx);
-        str = JS_ValueToSource(cx, result);
+        RootedString str(cx, JS_ValueToSource(cx, result));
         if (!str)
             return false;
 
-        char* utf8chars = JS_EncodeStringToUTF8(cx, str);
+        UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str);
         if (!utf8chars)
             return false;
-        fprintf(gOutFile->fp, "%s\n", utf8chars);
-        JS_free(cx, utf8chars);
+        fprintf(gOutFile->fp, "%s\n", utf8chars.get());
     }
     return true;
 }
 
 static MOZ_MUST_USE bool
 ReadEvalPrintLoop(JSContext* cx, FILE* in, bool compileOnly)
 {
     ShellContext* sc = GetShellContext(cx);
@@ -1355,17 +1358,17 @@ CreateMappedArrayBuffer(JSContext* cx, u
         return false;
     // It's a little bizarre to resolve relative to the script, but for testing
     // I need a file at a known location, and the only good way I know of to do
     // that right now is to include it in the repo alongside the test script.
     // Bug 944164 would introduce an alternative.
     JSString* filenameStr = ResolvePath(cx, rawFilenameStr, ScriptRelative);
     if (!filenameStr)
         return false;
-    JSAutoByteString filename(cx, filenameStr);
+    UniqueChars filename = JS_EncodeStringToLatin1(cx, filenameStr);
     if (!filename)
         return false;
 
     uint32_t offset = 0;
     if (args.length() >= 2) {
         if (!JS::ToUint32(cx, args[1], &offset))
             return false;
     }
@@ -1377,19 +1380,19 @@ CreateMappedArrayBuffer(JSContext* cx, u
             return false;
         sizeGiven = true;
         if (size == 0) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
             return false;
         }
     }
 
-    FILE* file = fopen(filename.ptr(), "rb");
+    FILE* file = fopen(filename.get(), "rb");
     if (!file) {
-        ReportCantOpenErrorUnknownEncoding(cx, filename.ptr());
+        ReportCantOpenErrorUnknownEncoding(cx, filename.get());
         return false;
     }
     AutoCloseFile autoClose(file);
 
     if (!sizeGiven) {
         struct stat st;
         if (fstat(fileno(file), &st) < 0) {
             JS_ReportErrorASCII(cx, "Unable to stat file");
@@ -1463,43 +1466,46 @@ Options(JSContext* cx, unsigned argc, Va
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JS::ContextOptions oldContextOptions = JS::ContextOptionsRef(cx);
     for (unsigned i = 0; i < args.length(); i++) {
         RootedString str(cx, JS::ToString(cx, args[i]));
         if (!str)
             return false;
-        args[i].setString(str);
-
-        JSAutoByteString opt;
-        if (!opt.encodeUtf8(cx, str))
-            return false;
-
-        if (strcmp(opt.ptr(), "strict") == 0) {
+
+        RootedLinearString opt(cx, str->ensureLinear(cx));
+        if (!opt)
+            return false;
+
+        if (StringEqualsAscii(opt, "strict")) {
             JS::ContextOptionsRef(cx).toggleExtraWarnings();
-        } else if (strcmp(opt.ptr(), "werror") == 0) {
+        } else if (StringEqualsAscii(opt, "werror")) {
             // Disallow toggling werror when there are off-thread jobs, to avoid
             // confusing CompileError::throwError.
             ShellContext* sc = GetShellContext(cx);
             if (!sc->offThreadJobs.empty()) {
                 JS_ReportErrorASCII(cx, "can't toggle werror when there are off-thread jobs");
                 return false;
             }
             JS::ContextOptionsRef(cx).toggleWerror();
-        } else if (strcmp(opt.ptr(), "throw_on_asmjs_validation_failure") == 0) {
+        } else if (StringEqualsAscii(opt, "throw_on_asmjs_validation_failure")) {
             JS::ContextOptionsRef(cx).toggleThrowOnAsmJSValidationFailure();
-        } else if (strcmp(opt.ptr(), "strict_mode") == 0) {
+        } else if (StringEqualsAscii(opt, "strict_mode")) {
             JS::ContextOptionsRef(cx).toggleStrictMode();
         } else {
+            UniqueChars optChars = JS_EncodeStringToUTF8(cx, opt);
+            if (!optChars)
+                return false;
+
             JS_ReportErrorUTF8(cx,
                                "unknown option name '%s'."
                                " The valid names are strict,"
                                " werror, and strict_mode.",
-                               opt.ptr());
+                               optChars.get());
             return false;
         }
     }
 
     UniqueChars names = DuplicateString("");
     bool found = false;
     if (names && oldContextOptions.extraWarnings()) {
         names = JS_sprintf_append(std::move(names), "%s%s", found ? "," : "", "strict");
@@ -1542,29 +1548,29 @@ LoadScript(JSContext* cx, unsigned argc,
                                       "load");
             return false;
         }
         str = ResolvePath(cx, str, scriptRelative ? ScriptRelative : RootRelative);
         if (!str) {
             JS_ReportErrorASCII(cx, "unable to resolve path");
             return false;
         }
-        JSAutoByteString filename(cx, str);
+        UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
         if (!filename)
             return false;
         errno = 0;
         CompileOptions opts(cx);
         opts.setIntroductionType("js shell load")
             .setUTF8(true)
             .setIsRunOnce(true)
             .setNoScriptRval(true);
         RootedScript script(cx);
         RootedValue unused(cx);
-        if ((compileOnly && !Compile(cx, opts, filename.ptr(), &script)) ||
-            !Evaluate(cx, opts, filename.ptr(), &unused))
+        if ((compileOnly && !Compile(cx, opts, filename.get(), &script)) ||
+            !Evaluate(cx, opts, filename.get(), &unused))
         {
             return false;
         }
     }
 
     args.rval().setUndefined();
     return true;
 }
@@ -1581,17 +1587,17 @@ LoadScriptRelativeToScript(JSContext* cx
     return LoadScript(cx, argc, vp, true);
 }
 
 // Populate |options| with the options given by |opts|'s properties. If we
 // need to convert a filename to a C string, let fileNameBytes own the
 // bytes.
 static bool
 ParseCompileOptions(JSContext* cx, CompileOptions& options, HandleObject opts,
-                    JSAutoByteString& fileNameBytes)
+                    UniqueChars& fileNameBytes)
 {
     RootedValue v(cx);
     RootedString s(cx);
 
     if (!JS_GetProperty(cx, opts, "isRunOnce", &v))
         return false;
     if (!v.isUndefined())
         options.setIsRunOnce(ToBoolean(v));
@@ -1604,20 +1610,20 @@ ParseCompileOptions(JSContext* cx, Compi
     if (!JS_GetProperty(cx, opts, "fileName", &v))
         return false;
     if (v.isNull()) {
         options.setFile(nullptr);
     } else if (!v.isUndefined()) {
         s = ToString(cx, v);
         if (!s)
             return false;
-        char* fileName = fileNameBytes.encodeLatin1(cx, s);
-        if (!fileName)
-            return false;
-        options.setFile(fileName);
+        fileNameBytes = JS_EncodeStringToLatin1(cx, s);
+        if (!fileNameBytes)
+            return false;
+        options.setFile(fileNameBytes.get());
     }
 
     if (!JS_GetProperty(cx, opts, "element", &v))
         return false;
     if (v.isObject())
         options.setElement(&v.toObject());
 
     if (!JS_GetProperty(cx, opts, "elementAttributeName", &v))
@@ -1813,17 +1819,17 @@ Evaluate(JSContext* cx, unsigned argc, V
     }
 
     if (!code || (args.length() == 2 && args[1].isPrimitive())) {
         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "evaluate");
         return false;
     }
 
     CompileOptions options(cx);
-    JSAutoByteString fileNameBytes;
+    UniqueChars fileNameBytes;
     RootedString displayURL(cx);
     RootedString sourceMapURL(cx);
     RootedObject global(cx, nullptr);
     bool catchTermination = false;
     bool loadBytecode = false;
     bool saveBytecode = false;
     bool saveIncrementalBytecode = false;
     bool assertEqBytecode = false;
@@ -2087,72 +2093,72 @@ Evaluate(JSContext* cx, unsigned argc, V
     }
 
     return JS_WrapValue(cx, args.rval());
 }
 
 JSString*
 js::shell::FileAsString(JSContext* cx, JS::HandleString pathnameStr)
 {
-    JSAutoByteString pathname(cx, pathnameStr);
+    UniqueChars pathname = JS_EncodeStringToLatin1(cx, pathnameStr);
     if (!pathname)
         return nullptr;
 
     FILE* file;
 
-    file = fopen(pathname.ptr(), "rb");
+    file = fopen(pathname.get(), "rb");
     if (!file) {
-        ReportCantOpenErrorUnknownEncoding(cx, pathname.ptr());
+        ReportCantOpenErrorUnknownEncoding(cx, pathname.get());
         return nullptr;
     }
 
     AutoCloseFile autoClose(file);
 
     if (fseek(file, 0, SEEK_END) != 0) {
-        pathname.clear();
-        if (!pathname.encodeUtf8(cx, pathnameStr))
+        pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
+        if (!pathname)
             return nullptr;
-        JS_ReportErrorUTF8(cx, "can't seek end of %s", pathname.ptr());
+        JS_ReportErrorUTF8(cx, "can't seek end of %s", pathname.get());
         return nullptr;
     }
 
     size_t len = ftell(file);
     if (fseek(file, 0, SEEK_SET) != 0) {
-        pathname.clear();
-        if (!pathname.encodeUtf8(cx, pathnameStr))
+        pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
+        if (!pathname)
             return nullptr;
-        JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.ptr());
+        JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.get());
         return nullptr;
     }
 
     UniqueChars buf(js_pod_malloc<char>(len + 1));
     if (!buf)
         return nullptr;
 
     size_t cc = fread(buf.get(), 1, len, file);
     if (cc != len) {
         if (ptrdiff_t(cc) < 0) {
-            ReportCantOpenErrorUnknownEncoding(cx, pathname.ptr());
+            ReportCantOpenErrorUnknownEncoding(cx, pathname.get());
         } else {
-            pathname.clear();
-            if (!pathname.encodeUtf8(cx, pathnameStr))
+            pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
+            if (!pathname)
                 return nullptr;
-            JS_ReportErrorUTF8(cx, "can't read %s: short read", pathname.ptr());
+            JS_ReportErrorUTF8(cx, "can't read %s: short read", pathname.get());
         }
         return nullptr;
     }
 
     UniqueTwoByteChars ucbuf(
         JS::LossyUTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf.get(), len), &len).get()
     );
     if (!ucbuf) {
-        pathname.clear();
-        if (!pathname.encodeUtf8(cx, pathnameStr))
+        pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
+        if (!pathname)
             return nullptr;
-        JS_ReportErrorUTF8(cx, "Invalid UTF-8 in file '%s'", pathname.ptr());
+        JS_ReportErrorUTF8(cx, "Invalid UTF-8 in file '%s'", pathname.get());
         return nullptr;
     }
 
     return JS_NewUCStringCopyN(cx, ucbuf.get(), len);
 }
 
 /*
  * Function to run scripts and return compilation + execution time. Semantics
@@ -2183,23 +2189,23 @@ Run(JSContext* cx, unsigned argc, Value*
 
     JS::SourceBufferHolder srcBuf(chars.twoByteRange().begin().get(), str->length(),
                                   JS::SourceBufferHolder::NoOwnership);
 
     RootedScript script(cx);
     int64_t startClock = PRMJ_Now();
     {
         /* FIXME: This should use UTF-8 (bug 987069). */
-        JSAutoByteString filename(cx, str);
+        UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
         if (!filename)
             return false;
 
         JS::CompileOptions options(cx);
         options.setIntroductionType("js shell run")
-               .setFileAndLine(filename.ptr(), 1)
+               .setFileAndLine(filename.get(), 1)
                .setIsRunOnce(true)
                .setNoScriptRval(true);
         if (!JS_CompileUCScript(cx, srcBuf, options, &script))
             return false;
     }
 
     if (!JS_ExecuteScript(cx, script))
         return false;
@@ -2315,41 +2321,41 @@ ReadLineBuf(JSContext* cx, unsigned argc
 
         size_t len = 0;
         while(len < buflen) {
             if (currentBuf[len] == '\n')
                 break;
             len++;
         }
 
-        JSString* str = JS_NewStringCopyN(cx, currentBuf, len);
+        JSString* str = JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(currentBuf, len));
         if (!str)
             return false;
 
         if (currentBuf[len] == '\0')
             sc->readLineBufPos += len;
         else
             sc->readLineBufPos += len + 1;
 
         args.rval().setString(str);
         return true;
     }
 
     if (args.length() == 1) {
-        if (sc->readLineBuf)
-            sc->readLineBuf.reset();
+        sc->readLineBuf = nullptr;
+        sc->readLineBufPos = 0;
 
         RootedString str(cx, JS::ToString(cx, args[0]));
         if (!str)
             return false;
-        sc->readLineBuf = UniqueChars(JS_EncodeStringToUTF8(cx, str));
+        sc->readLineBuf = JS_EncodeStringToUTF8(cx, str);
         if (!sc->readLineBuf)
             return false;
 
-        sc->readLineBufPos = 0;
+        args.rval().setUndefined();
         return true;
     }
 
     JS_ReportErrorASCII(cx, "Must specify at most one argument");
     return false;
 }
 
 static bool
@@ -2361,21 +2367,20 @@ PutStr(JSContext* cx, unsigned argc, Val
         if (!gOutFile->isOpen()) {
             JS_ReportErrorASCII(cx, "output file is closed");
             return false;
         }
 
         RootedString str(cx, JS::ToString(cx, args[0]));
         if (!str)
             return false;
-        char* bytes = JS_EncodeStringToUTF8(cx, str);
+        UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
         if (!bytes)
             return false;
-        fputs(bytes, gOutFile->fp);
-        JS_free(cx, bytes);
+        fputs(bytes.get(), gOutFile->fp);
         fflush(gOutFile->fp);
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
@@ -2394,21 +2399,20 @@ PrintInternal(JSContext* cx, const CallA
         JS_ReportErrorASCII(cx, "output file is closed");
         return false;
     }
 
     for (unsigned i = 0; i < args.length(); i++) {
         RootedString str(cx, JS::ToString(cx, args[i]));
         if (!str)
             return false;
-        char* bytes = JS_EncodeStringToUTF8(cx, str);
+        UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
         if (!bytes)
             return false;
-        fprintf(file->fp, "%s%s", i ? " " : "", bytes);
-        JS_free(cx, bytes);
+        fprintf(file->fp, "%s%s", i ? " " : "", bytes.get());
     }
 
     fputc('\n', file->fp);
     fflush(file->fp);
 
     args.rval().setUndefined();
     return true;
 }
@@ -2502,23 +2506,23 @@ StopTimingMutator(JSContext* cx, unsigne
                 mutator_ms, mutator_ms / total_ms * 100.0, gc_ms, gc_ms / total_ms * 100.0);
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static const char*
-ToSource(JSContext* cx, MutableHandleValue vp, JSAutoByteString* bytes)
-{
-    JSString* str = JS_ValueToSource(cx, vp);
+ToSource(JSContext* cx, HandleValue vp, UniqueChars* bytes)
+{
+    RootedString str(cx, JS_ValueToSource(cx, vp));
     if (str) {
-        vp.setString(str);
-        if (bytes->encodeLatin1(cx, str))
-            return bytes->ptr();
+        *bytes = JS_EncodeStringToUTF8(cx, str);
+        if (*bytes)
+            return bytes->get();
     }
     JS_ClearPendingException(cx);
     return "<<error converting value to string>>";
 }
 
 static bool
 AssertEq(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2533,29 +2537,29 @@ AssertEq(JSContext* cx, unsigned argc, V
                                   "assertEq");
         return false;
     }
 
     bool same;
     if (!JS_SameValue(cx, args[0], args[1], &same))
         return false;
     if (!same) {
-        JSAutoByteString bytes0, bytes1;
+        UniqueChars bytes0, bytes1;
         const char* actual = ToSource(cx, args[0], &bytes0);
         const char* expected = ToSource(cx, args[1], &bytes1);
         if (args.length() == 2) {
-            JS_ReportErrorNumberLatin1(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED,
-                                       actual, expected);
+            JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED,
+                                     actual, expected);
         } else {
-            JSAutoByteString bytes2(cx, args[2].toString());
+            RootedString message(cx, args[2].toString());
+            UniqueChars bytes2 = JS_EncodeStringToUTF8(cx, message);
             if (!bytes2)
                 return false;
-            JS_ReportErrorNumberLatin1(cx, my_GetErrorMessage, nullptr,
-                                       JSSMSG_ASSERT_EQ_FAILED_MSG,
-                                       actual, expected, bytes2.ptr());
+            JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED_MSG,
+                                     actual, expected, bytes2.get());
         }
         return false;
     }
     args.rval().setUndefined();
     return true;
 }
 
 static JSScript*
@@ -3091,17 +3095,18 @@ DisassembleToString(JSContext* cx, unsig
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return false;
     if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter))
         return false;
 
-    JSString* str = JS_NewStringCopyZ(cx, sprinter.string());
+    JS::ConstUTF8CharsZ utf8chars(sprinter.string(), strlen(sprinter.string()));
+    JSString* str = JS_NewStringCopyUTF8Z(cx, utf8chars);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static bool
 Disassemble(JSContext* cx, unsigned argc, Value* vp)
@@ -3143,41 +3148,40 @@ DisassFile(JSContext* cx, unsigned argc,
         args.rval().setUndefined();
         return true;
     }
 
     // We should change DisassembleOptionParser to store CallArgs.
     JSString* str = JS::ToString(cx, HandleValue::fromMarkedLocation(&p.argv[0]));
     if (!str)
         return false;
-    JSAutoByteString filename(cx, str);
+    UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
     if (!filename)
         return false;
     RootedScript script(cx);
 
     {
         CompileOptions options(cx);
         options.setIntroductionType("js shell disFile")
                .setUTF8(true)
-               .setFileAndLine(filename.ptr(), 1)
+               .setFileAndLine(filename.get(), 1)
                .setIsRunOnce(true)
                .setNoScriptRval(true);
 
-        if (!JS::Compile(cx, options, filename.ptr(), &script))
+        if (!JS::Compile(cx, options, filename.get(), &script))
             return false;
     }
 
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return false;
-    bool ok = DisassembleScript(cx, script, nullptr, p.lines, p.recursive, p.sourceNotes, &sprinter);
-    if (ok)
-        fprintf(gOutFile->fp, "%s\n", sprinter.string());
-    if (!ok)
-        return false;
+    if (!DisassembleScript(cx, script, nullptr, p.lines, p.recursive, p.sourceNotes, &sprinter))
+        return false;
+
+    fprintf(gOutFile->fp, "%s\n", sprinter.string());
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 DisassWithSrc(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -3363,31 +3367,31 @@ static bool
 Crash(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0)
         MOZ_CRASH("forced crash");
     RootedString message(cx, JS::ToString(cx, args[0]));
     if (!message)
         return false;
-    char* utf8chars = JS_EncodeStringToUTF8(cx, message);
+    UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, message);
     if (!utf8chars)
         return false;
     if (args.get(1).isObject()) {
         RootedValue v(cx);
         RootedObject opts(cx, &args[1].toObject());
         if (!JS_GetProperty(cx, opts, "suppress_minidump", &v))
             return false;
         if (v.isBoolean() && v.toBoolean())
             js::NoteIntentionalCrash();
     }
 #ifndef DEBUG
-    MOZ_ReportCrash(utf8chars, __FILE__, __LINE__);
+    MOZ_ReportCrash(utf8chars.get(), __FILE__, __LINE__);
 #endif
-    MOZ_CRASH_UNSAFE_OOL(utf8chars);
+    MOZ_CRASH_UNSAFE_OOL(utf8chars.get());
 }
 
 static bool
 GetSLX(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedScript script(cx);
 
@@ -4179,17 +4183,17 @@ StackDump(JSContext* cx, unsigned argc, 
         JS_ReportErrorASCII(cx, "output file is closed");
         return false;
     }
 
     bool showArgs = ToBoolean(args.get(0));
     bool showLocals = ToBoolean(args.get(1));
     bool showThisProps = ToBoolean(args.get(2));
 
-    JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, showArgs, showLocals, showThisProps);
+    JS::UniqueChars buf = JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
     if (!buf) {
         fputs("Failed to format JavaScript stack for dump\n", gOutFile->fp);
         JS_ClearPendingException(cx);
     } else {
         fputs(buf.get(), gOutFile->fp);
     }
 
     args.rval().setUndefined();
@@ -4368,17 +4372,17 @@ ParseModule(JSContext* cx, unsigned argc
     if (args.length() > 1) {
         if (!args[1].isString()) {
             const char* typeName = InformalValueTypeName(args[1]);
             JS_ReportErrorASCII(cx, "expected filename string, got %s", typeName);
             return false;
         }
 
         RootedString str(cx, args[1].toString());
-        filename.reset(JS_EncodeString(cx, str));
+        filename = JS_EncodeStringToLatin1(cx, str);
         if (!filename)
             return false;
 
         options.setFileAndLine(filename.get(), 1);
     } else {
         options.setFileAndLine("<string>", 1);
     }
 
@@ -4742,30 +4746,31 @@ BinParse(JSContext* cx, unsigned argc, V
         RootedValue optionFormat(cx);
         if (!JS_GetProperty(cx, objOptions, "format", &optionFormat))
             return false;
 
         if (optionFormat.isUndefined()) {
             // By default, `useMultipart` is `true`.
             useMultipart = true;
         } else if (optionFormat.isString()) {
-            RootedString stringFormat(cx);
-            stringFormat = optionFormat.toString();
-            JS::Rooted<JSLinearString*> linearFormat(cx);
-            linearFormat = stringFormat->ensureLinear(cx);
+            RootedLinearString linearFormat(cx, optionFormat.toString()->ensureLinear(cx));
+            if (!linearFormat)
+                return false;
             if (StringEqualsAscii(linearFormat, "multipart")) {
                 useMultipart = true;
             } else if (StringEqualsAscii(linearFormat, "simple")) {
                 useMultipart = false;
             } else {
-                JSAutoByteString printable;
+                UniqueChars printable = JS_EncodeStringToUTF8(cx, linearFormat);
+                if (!printable)
+                    return false;
+
                 JS_ReportErrorUTF8(cx,
                                    "Unknown value for option `format`, expected 'multipart' or "
-                                   "'simple', got %s",
-                                   ValueToPrintableUTF8(cx, optionFormat, &printable));
+                                   "'simple', got %s", printable.get());
                 return false;
             }
         } else {
             const char* typeName = InformalValueTypeName(optionFormat);
             JS_ReportErrorASCII(cx, "option `format` should be a string, got %s", typeName);
             return false;
         }
     }
@@ -4978,17 +4983,17 @@ OffThreadCompileScript(JSContext* cx, un
         return false;
     }
     if (!args[0].isString()) {
         const char* typeName = InformalValueTypeName(args[0]);
         JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
         return false;
     }
 
-    JSAutoByteString fileNameBytes;
+    UniqueChars fileNameBytes;
     CompileOptions options(cx);
     options.setIntroductionType("js shell offThreadCompileScript")
            .setFileAndLine("<string>", 1);
 
     if (args.length() >= 2) {
         if (args[1].isPrimitive()) {
             JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
                                       "evaluate");
@@ -5083,17 +5088,17 @@ OffThreadCompileModule(JSContext* cx, un
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1 || !args[0].isString()) {
         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
                                   "offThreadCompileModule");
         return false;
     }
 
-    JSAutoByteString fileNameBytes;
+    UniqueChars fileNameBytes;
     CompileOptions options(cx);
     options.setIntroductionType("js shell offThreadCompileModule")
            .setFileAndLine("<string>", 1);
     options.setIsRunOnce(true)
            .setSourceIsLazy(false);
     options.forceAsync = true;
 
     JSString* scriptContents = args[0].toString();
@@ -5187,17 +5192,17 @@ OffThreadDecodeScript(JSContext* cx, uns
     }
     if (!args[0].isObject() || !CacheEntry_isCacheEntry(&args[0].toObject())) {
         const char* typeName = InformalValueTypeName(args[0]);
         JS_ReportErrorASCII(cx, "expected cache entry, got %s", typeName);
         return false;
     }
     RootedObject cacheEntry(cx, &args[0].toObject());
 
-    JSAutoByteString fileNameBytes;
+    UniqueChars fileNameBytes;
     CompileOptions options(cx);
     options.setIntroductionType("js shell offThreadDecodeScript")
            .setFileAndLine("<string>", 1);
 
     if (args.length() >= 2) {
         if (args[1].isPrimitive()) {
             JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
                                       "evaluate");
@@ -5325,23 +5330,16 @@ class AutoCStringVector
     }
     char* operator[](size_t i) const {
         return argv_[i];
     }
     void replace(size_t i, UniqueChars arg) {
         js_free(argv_[i]);
         argv_[i] = arg.release();
     }
-    const char* back() const {
-        return argv_.back();
-    }
-    void replaceBack(UniqueChars arg) {
-        js_free(argv_.back());
-        argv_.back() = arg.release();
-    }
 };
 
 #if defined(XP_WIN)
 static bool
 EscapeForShell(JSContext* cx, AutoCStringVector& argv)
 {
     // Windows will break arguments in argv by various spaces, so we wrap each
     // argument in quotes and escape quotes within. Even with quotes, \ will be
@@ -5410,36 +5408,42 @@ NestedShell(JSContext* cx, unsigned argc
     // Propagate selected flags from the current shell
     for (unsigned i = 0; i < sPropagatedFlags.length(); i++) {
         UniqueChars flags = DuplicateString(cx, sPropagatedFlags[i]);
         if (!flags || !argv.append(std::move(flags)))
             return false;
     }
 
     // The arguments to nestedShell are stringified and append to argv.
-    RootedString str(cx);
     for (unsigned i = 0; i < args.length(); i++) {
-        str = ToString(cx, args[i]);
+        JSString* str = ToString(cx, args[i]);
         if (!str)
             return false;
 
-        UniqueChars arg(JS_EncodeString(cx, str));
-        if (!arg || !argv.append(std::move(arg)))
-            return false;
-
-        // As a special case, if the caller passes "--js-cache", replace that
-        // with "--js-cache=$(jsCacheDir)"
-        if (!strcmp(argv.back(), "--js-cache") && jsCacheDir) {
-            UniqueChars newArg = JS_smprintf("--js-cache=%s", jsCacheDir);
-            if (!newArg) {
+        JSLinearString* linear = str->ensureLinear(cx);
+        if (!linear)
+            return false;
+
+        UniqueChars arg;
+        if (StringEqualsAscii(linear, "--js-cache") && jsCacheDir) {
+            // As a special case, if the caller passes "--js-cache", use
+            // "--js-cache=$(jsCacheDir)" instead.
+            arg = JS_smprintf("--js-cache=%s", jsCacheDir);
+            if (!arg) {
                 JS_ReportOutOfMemory(cx);
                 return false;
             }
-            argv.replaceBack(std::move(newArg));
-        }
+        } else {
+            arg = JS_EncodeStringToLatin1(cx, str);
+            if (!arg)
+                return false;
+        }
+
+        if (!argv.append(std::move(arg)))
+            return false;
     }
 
     // execv assumes argv is null-terminated
     if (!argv.append(nullptr))
         return false;
 
     int status = 0;
 #if defined(XP_WIN)
@@ -6956,17 +6960,17 @@ class ShellAutoEntryMonitor : JS::dbg::A
 
     void Entry(JSContext* cx, JSFunction* function, JS::HandleValue asyncStack,
                const char* asyncCause) override {
         MOZ_ASSERT(!enteredWithoutExit);
         enteredWithoutExit = true;
 
         RootedString displayId(cx, JS_GetFunctionDisplayId(function));
         if (displayId) {
-            UniqueChars displayIdStr(JS_EncodeStringToUTF8(cx, displayId));
+            UniqueChars displayIdStr = JS_EncodeStringToUTF8(cx, displayId);
             if (!displayIdStr) {
                 // We report OOM in buildResult.
                 cx->recoverFromOutOfMemory();
                 oom = true;
                 return;
             }
             oom = !log.append(std::move(displayIdStr));
             return;
@@ -7154,21 +7158,21 @@ SetARMHwCapFlags(JSContext* cx, unsigned
         return false;
     }
 
     RootedString flagsListString(cx, JS::ToString(cx, args.get(0)));
     if (!flagsListString)
         return false;
 
 #if defined(JS_CODEGEN_ARM)
-    JSAutoByteString flagsList(cx, flagsListString);
+    UniqueChars flagsList = JS_EncodeStringToLatin1(cx, flagsListString);
     if (!flagsList)
         return false;
 
-    jit::ParseARMHwCapFlags(flagsList.ptr());
+    jit::ParseARMHwCapFlags(flagsList.get());
 #endif
 
     args.rval().setUndefined();
     return true;
 }
 
 #ifndef __AFL_HAVE_MANUAL_CONTROL
 # define __AFL_LOOP(x) true
@@ -8192,17 +8196,17 @@ PrintStackTrace(JSContext* cx, HandleVal
     if (!stackObj)
         return true;
 
     JSPrincipals* principals = exnObj->as<ErrorObject>().realm()->principals();
     RootedString stackStr(cx);
     if (!BuildStackString(cx, principals, stackObj, &stackStr, 2))
         return false;
 
-    UniqueChars stack(JS_EncodeStringToUTF8(cx, stackStr));
+    UniqueChars stack = JS_EncodeStringToUTF8(cx, stackStr);
     if (!stack)
         return false;
 
     FILE* fp = ErrorFilePointer();
     fputs("Stack:\n", fp);
     fputs(stack.get(), fp);
 
     return true;
@@ -9051,17 +9055,17 @@ ProcessArgs(JSContext* cx, OptionParser*
         RootedString jspath(cx, JS_NewStringCopyZ(cx, path));
         if (!jspath)
             return false;
 
         JSString* absolutePath = js::shell::ResolvePath(cx, jspath, RootRelative);
         if (!absolutePath)
             return false;
 
-        sc->moduleLoadPath = UniqueChars(JS_EncodeString(cx, absolutePath));
+        sc->moduleLoadPath = JS_EncodeStringToLatin1(cx, absolutePath);
     } else {
         sc->moduleLoadPath = js::shell::GetCWD();
     }
 
     if (!sc->moduleLoadPath)
         return false;
 
     if (!modulePaths.empty() && !InitModuleLoader(cx))
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -27,17 +27,16 @@
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "builtin/String.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/SourceNotes.h"
 #include "gc/FreeOp.h"
 #include "gc/GCInternals.h"
-#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/Printf.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/CodeCoverage.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
@@ -1126,82 +1125,65 @@ js::DumpScript(JSContext* cx, JSScript* 
     if (!sprinter.init())
         return false;
     RootedScript script(cx, scriptArg);
     bool ok = Disassemble(cx, script, true, &sprinter);
     fprintf(fp, "%s", sprinter.string());
     return ok;
 }
 
-static bool
-ToDisassemblySource(JSContext* cx, HandleValue v, JSAutoByteString* bytes)
+static UniqueChars
+ToDisassemblySource(JSContext* cx, HandleValue v)
 {
-    if (v.isString()) {
-        Sprinter sprinter(cx);
-        if (!sprinter.init())
-            return false;
-        char* nbytes = QuoteString(&sprinter, v.toString(), '"');
-        if (!nbytes)
-            return false;
-        UniqueChars copy = JS_smprintf("%s", nbytes);
-        if (!copy) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-        bytes->initBytes(std::move(copy));
-        return true;
-    }
-
-    if (JS::RuntimeHeapIsBusy()) {
-        UniqueChars source = JS_smprintf("<value>");
-        if (!source) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-        bytes->initBytes(std::move(source));
-        return true;
-    }
+    if (v.isString())
+        return QuoteString(cx, v.toString(), '"');
+
+    if (JS::RuntimeHeapIsBusy())
+        return DuplicateString(cx, "<value>");
 
     if (v.isObject()) {
         JSObject& obj = v.toObject();
 
         if (obj.is<JSFunction>()) {
             RootedFunction fun(cx, &obj.as<JSFunction>());
             JSString* str = JS_DecompileFunction(cx, fun);
             if (!str)
-                return false;
-            return bytes->encodeLatin1(cx, str);
+                return nullptr;
+            return StringToNewUTF8CharsZ(cx, *str);
         }
 
         if (obj.is<RegExpObject>()) {
             JSString* source = obj.as<RegExpObject>().toString(cx);
             if (!source)
-                return false;
-            return bytes->encodeLatin1(cx, source);
+                return nullptr;
+            return StringToNewUTF8CharsZ(cx, *source);
         }
     }
 
-    return !!ValueToPrintableLatin1(cx, v, bytes, true);
+    JSString* str = ValueToSource(cx, v);
+    if (!str)
+        return nullptr;
+    return QuoteString(cx, str);
 }
 
 static bool
-ToDisassemblySource(JSContext* cx, HandleScope scope, JSAutoByteString* bytes)
+ToDisassemblySource(JSContext* cx, HandleScope scope, UniqueChars* bytes)
 {
     UniqueChars source = JS_smprintf("%s {", ScopeKindString(scope->kind()));
     if (!source) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
-        JSAutoByteString nameBytes;
-        if (!AtomToPrintableString(cx, bi.name(), &nameBytes))
+        UniqueChars nameBytes = AtomToPrintableString(cx, bi.name());
+        if (!nameBytes)
             return false;
 
-        source = JS_sprintf_append(std::move(source), "%s: ", nameBytes.ptr());
+        source = JS_sprintf_append(std::move(source), "%s: ", nameBytes.get());
         if (!source) {
             ReportOutOfMemory(cx);
             return false;
         }
 
         BindingLocation loc = bi.location();
         switch (loc.kind()) {
           case BindingLocation::Kind::Global:
@@ -1244,17 +1226,17 @@ ToDisassemblySource(JSContext* cx, Handl
     }
 
     source = JS_sprintf_append(std::move(source), "}");
     if (!source) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    bytes->initBytes(std::move(source));
+    *bytes = std::move(source);
     return true;
 }
 
 static bool
 DumpJumpOrigins(HandleScript script, jsbytecode* pc, const BytecodeParser* parser, Sprinter* sp)
 {
     bool called = false;
     auto callback = [&script, &sp, &called](jsbytecode* pc, BytecodeParser::JumpKind kind) {
@@ -1416,83 +1398,83 @@ Disassemble1(JSContext* cx, HandleScript
         ptrdiff_t off = GET_JUMP_OFFSET(pc);
         if (!sp->jsprintf(" %u (%+d)", unsigned(loc + int(off)), int(off)))
             return 0;
         break;
       }
 
       case JOF_SCOPE: {
         RootedScope scope(cx, script->getScope(GET_UINT32_INDEX(pc)));
-        JSAutoByteString bytes;
+        UniqueChars bytes;
         if (!ToDisassemblySource(cx, scope, &bytes))
             return 0;
-        if (!sp->jsprintf(" %s", bytes.ptr()))
+        if (!sp->jsprintf(" %s", bytes.get()))
             return 0;
         break;
       }
 
       case JOF_ENVCOORD: {
         RootedValue v(cx,
             StringValue(EnvironmentCoordinateName(cx->caches().envCoordinateNameCache, script, pc)));
-        JSAutoByteString bytes;
-        if (!ToDisassemblySource(cx, v, &bytes))
+        UniqueChars bytes = ToDisassemblySource(cx, v);
+        if (!bytes)
             return 0;
         EnvironmentCoordinate ec(pc);
-        if (!sp->jsprintf(" %s (hops = %u, slot = %u)", bytes.ptr(), ec.hops(), ec.slot()))
+        if (!sp->jsprintf(" %s (hops = %u, slot = %u)", bytes.get(), ec.hops(), ec.slot()))
             return 0;
         break;
       }
 
       case JOF_ATOM: {
         RootedValue v(cx, StringValue(script->getAtom(GET_UINT32_INDEX(pc))));
-        JSAutoByteString bytes;
-        if (!ToDisassemblySource(cx, v, &bytes))
+        UniqueChars bytes = ToDisassemblySource(cx, v);
+        if (!bytes)
             return 0;
-        if (!sp->jsprintf(" %s", bytes.ptr()))
+        if (!sp->jsprintf(" %s", bytes.get()))
             return 0;
         break;
       }
 
       case JOF_DOUBLE: {
         RootedValue v(cx, script->getConst(GET_UINT32_INDEX(pc)));
-        JSAutoByteString bytes;
-        if (!ToDisassemblySource(cx, v, &bytes))
+        UniqueChars bytes = ToDisassemblySource(cx, v);
+        if (!bytes)
             return 0;
-        if (!sp->jsprintf(" %s", bytes.ptr()))
+        if (!sp->jsprintf(" %s", bytes.get()))
             return 0;
         break;
       }
 
       case JOF_OBJECT: {
         /* Don't call obj.toSource if analysis/inference is active. */
         if (script->zone()->types.activeAnalysis) {
             if (!sp->jsprintf(" object"))
                 return 0;
             break;
         }
 
         JSObject* obj = script->getObject(GET_UINT32_INDEX(pc));
         {
-            JSAutoByteString bytes;
             RootedValue v(cx, ObjectValue(*obj));
-            if (!ToDisassemblySource(cx, v, &bytes))
+            UniqueChars bytes = ToDisassemblySource(cx, v);
+            if (!bytes)
                 return 0;
-            if (!sp->jsprintf(" %s", bytes.ptr()))
+            if (!sp->jsprintf(" %s", bytes.get()))
                 return 0;
         }
         break;
       }
 
       case JOF_REGEXP: {
         js::RegExpObject* obj = script->getRegExp(pc);
-        JSAutoByteString bytes;
         RootedValue v(cx, ObjectValue(*obj));
-        if (!ToDisassemblySource(cx, v, &bytes))
+        UniqueChars bytes = ToDisassemblySource(cx, v);
+        if (!bytes)
             return 0;
-        if (!sp->jsprintf(" %s", bytes.ptr()))
+        if (!sp->jsprintf(" %s", bytes.get()))
             return 0;
         break;
       }
 
       case JOF_TABLESWITCH:
       {
         int32_t i, low, high;
 
@@ -1641,17 +1623,17 @@ struct ExpressionDecompiler
 #endif
     {}
     bool init();
     bool decompilePCForStackOperand(jsbytecode* pc, int i);
     bool decompilePC(jsbytecode* pc, uint8_t defIndex);
     bool decompilePC(const OffsetAndDefIndex& offsetAndDefIndex);
     JSAtom* getArg(unsigned slot);
     JSAtom* loadAtom(jsbytecode* pc);
-    bool quote(JSString* s, uint32_t quote);
+    bool quote(JSString* s, char quote);
     bool write(const char* s);
     bool write(JSString* str);
     UniqueChars getOutput();
 #if defined(DEBUG) || defined(JS_JITSPEW)
     void setStackDump() {
         isStackDump = true;
     }
 #endif
@@ -2110,19 +2092,19 @@ bool
 ExpressionDecompiler::write(JSString* str)
 {
     if (str == cx->names().dotThis)
         return write("this");
     return sprinter.putString(str);
 }
 
 bool
-ExpressionDecompiler::quote(JSString* s, uint32_t quote)
+ExpressionDecompiler::quote(JSString* s, char quote)
 {
-    return QuoteString(&sprinter, s, quote) != nullptr;
+    return QuoteString(&sprinter, s, quote);
 }
 
 JSAtom*
 ExpressionDecompiler::loadAtom(jsbytecode* pc)
 {
     return script->getAtom(pc);
 }
 
@@ -2312,17 +2294,17 @@ js::DecompileValueGenerator(JSContext* c
     if (!fallback) {
         if (v.isUndefined())
             return DuplicateString(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
         fallback = ValueToSource(cx, v);
         if (!fallback)
             return nullptr;
     }
 
-    return UniqueChars(JS_EncodeString(cx, fallback));
+    return StringToNewUTF8CharsZ(cx, *fallback);
 }
 
 static bool
 DecompileArgumentFromStack(JSContext* cx, int formalIndex, UniqueChars* res)
 {
     MOZ_ASSERT(formalIndex >= 0);
 
     *res = nullptr;
@@ -2385,34 +2367,32 @@ DecompileArgumentFromStack(JSContext* cx
         return false;
     if (!ed.decompilePCForStackOperand(current, formalStackIndex))
         return false;
 
     *res = ed.getOutput();
     return *res != nullptr;
 }
 
-UniqueChars
+JSString*
 js::DecompileArgument(JSContext* cx, int formalIndex, HandleValue v)
 {
     {
         UniqueChars result;
         if (!DecompileArgumentFromStack(cx, formalIndex, &result))
             return nullptr;
-        if (result && strcmp(result.get(), "(intermediate value)"))
-            return result;
+        if (result && strcmp(result.get(), "(intermediate value)")) {
+            JS::ConstUTF8CharsZ utf8chars(result.get(), strlen(result.get()));
+            return NewStringCopyUTF8Z<CanGC>(cx, utf8chars);
+        }
     }
     if (v.isUndefined())
-        return DuplicateString(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
-
-    RootedString fallback(cx, ValueToSource(cx, v));
-    if (!fallback)
-        return nullptr;
-
-    return UniqueChars(JS_EncodeString(cx, fallback));
+        return cx->names().undefined; // Prevent users from seeing "(void 0)"
+
+    return ValueToSource(cx, v);
 }
 
 extern bool
 js::IsValidBytecodeOffset(JSContext* cx, JSScript* script, size_t offset)
 {
     // This could be faster (by following jump instructions if the target is <= offset).
     for (BytecodeRange r(cx, script); !r.empty(); r.popFront()) {
         size_t here = r.frontOffset();
@@ -2709,17 +2689,18 @@ GetPCCountJSON(JSContext* cx, const Scri
             if (!ed.init())
                 return false;
             // defIndex passed here is not used.
             if (!ed.decompilePC(pc, /* defIndex = */ 0))
                 return false;
             UniqueChars text = ed.getOutput();
             if (!text)
                 return false;
-            JSString* str = NewLatin1StringZ(cx, std::move(text));
+            JS::ConstUTF8CharsZ utf8chars(text.get(), strlen(text.get()));
+            JSString* str = NewStringCopyUTF8Z<CanGC>(cx, utf8chars);
             if (!AppendJSONProperty(buf, "text"))
                 return false;
             if (!str || !(str = StringToSource(cx, str)))
                 return false;
             if (!buf.append(str))
                 return false;
         }
 
--- a/js/src/vm/BytecodeUtil.h
+++ b/js/src/vm/BytecodeUtil.h
@@ -527,17 +527,17 @@ GetVariableBytecodeLength(jsbytecode* pc
 UniqueChars
 DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v,
                         HandleString fallback, int skipStackHits = 0);
 
 /*
  * Decompile the formal argument at formalIndex in the nearest non-builtin
  * stack frame, falling back with converting v to source.
  */
-UniqueChars
+JSString*
 DecompileArgument(JSContext* cx, int formalIndex, HandleValue v);
 
 static inline unsigned
 GetBytecodeLength(jsbytecode* pc)
 {
     JSOp op = (JSOp)*pc;
     MOZ_ASSERT(op < JSOP_LIMIT);
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -20,17 +20,17 @@
 #include "frontend/Parser.h"
 #include "gc/FreeOp.h"
 #include "gc/HashUtil.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/Date.h"
 #include "js/SourceBufferHolder.h"
 #include "js/StableStringChars.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/Vector.h"
 #include "js/Wrapper.h"
 #include "proxy/ScriptedProxyHandler.h"
 #include "util/Text.h"
@@ -481,20 +481,20 @@ ParseEvalOptions(JSContext* cx, HandleVa
 
     RootedValue v(cx);
     if (!JS_GetProperty(cx, opts, "url", &v))
         return false;
     if (!v.isUndefined()) {
         RootedString url_str(cx, ToString<CanGC>(cx, v));
         if (!url_str)
             return false;
-        JSAutoByteString url_bytes(cx, url_str);
+        UniqueChars url_bytes = JS_EncodeStringToLatin1(cx, url_str);
         if (!url_bytes)
             return false;
-        if (!options.setFilename(cx, url_bytes.ptr()))
+        if (!options.setFilename(cx, url_bytes.get()))
             return false;
     }
 
     if (!JS_GetProperty(cx, opts, "lineNumber", &v))
         return false;
     if (!v.isUndefined()) {
         uint32_t lineno;
         if (!ToUint32(cx, v, &lineno))
@@ -4476,17 +4476,17 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
 
     /* A script must be in one of these realms to match the query. */
     RealmSet realms;
 
     /* If this is a string, matching scripts have urls equal to it. */
     RootedValue url;
 
     /* url as a C string. */
-    JSAutoByteString urlCString;
+    UniqueChars urlCString;
 
     /* If this is a string, matching scripts' sources have displayURLs equal to
      * it. */
     RootedLinearString displayURLString;
 
     /*
      * If this is a source referent, matching scripts will have sources equal
      * to this instance. Ideally we'd use a Maybe here, but Maybe interacts
@@ -4562,17 +4562,18 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
     /*
      * Given that parseQuery or omittedQuery has been called, prepare to match
      * scripts. Set urlCString and displayURLChars as appropriate.
      */
     bool prepareQuery() {
         // Compute urlCString and displayURLChars, if a url or displayURL was
         // given respectively.
         if (url.isString()) {
-            if (!urlCString.encodeLatin1(cx, url.toString()))
+            urlCString = JS_EncodeStringToLatin1(cx, url.toString());
+            if (!urlCString)
                 return false;
         }
 
         return true;
     }
 
     bool delazifyScripts() {
         // All scripts in debuggee realms must be visible, so delazify
@@ -4603,24 +4604,24 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
         //
         // * hasLine
         //   Only JSScript supports GetScriptLineExtent.
         return innermost || hasLine;
     }
 
     template <typename T>
     MOZ_MUST_USE bool commonFilter(T script, const JS::AutoRequireNoGC& nogc) {
-        if (urlCString.ptr()) {
+        if (urlCString) {
             bool gotFilename = false;
-            if (script->filename() && strcmp(script->filename(), urlCString.ptr()) == 0)
+            if (script->filename() && strcmp(script->filename(), urlCString.get()) == 0)
                 gotFilename = true;
 
             bool gotSourceURL = false;
             if (!gotFilename && script->scriptSource()->introducerFilename() &&
-                strcmp(script->scriptSource()->introducerFilename(), urlCString.ptr()) == 0)
+                strcmp(script->scriptSource()->introducerFilename(), urlCString.get()) == 0)
             {
                 gotSourceURL = true;
             }
             if (!gotFilename && !gotSourceURL)
                 return false;
         }
         if (displayURLString) {
             if (!script->scriptSource() || !script->scriptSource()->hasDisplayURL())
@@ -4915,17 +4916,17 @@ class MOZ_STACK_CLASS Debugger::ObjectQu
 
         if (!referent.is<JSObject>() || referent.exposeToJS().isUndefined())
             return true;
 
         JSObject* obj = referent.as<JSObject>();
 
         if (!className.isUndefined()) {
             const char* objClassName = obj->getClass()->name;
-            if (strcmp(objClassName, classNameCString.ptr()) != 0)
+            if (strcmp(objClassName, classNameCString.get()) != 0)
                 return true;
         }
 
         return objects.append(obj);
     }
 
   private:
     /* The context in which we should do our work. */
@@ -4936,25 +4937,26 @@ class MOZ_STACK_CLASS Debugger::ObjectQu
 
     /*
      * If this is non-null, matching objects will have a class whose name is
      * this property.
      */
     RootedValue className;
 
     /* The className member, as a C string. */
-    JSAutoByteString classNameCString;
+    UniqueChars classNameCString;
 
     /*
      * Given that either omittedQuery or parseQuery has been called, prepare the
      * query for matching objects.
      */
     bool prepareQuery() {
         if (className.isString()) {
-            if (!classNameCString.encodeLatin1(cx, className.toString()))
+            classNameCString = JS_EncodeStringToLatin1(cx, className.toString());
+            if (!classNameCString)
                 return false;
         }
 
         return true;
     }
 };
 
 bool
@@ -5425,17 +5427,17 @@ Debugger::wrapLazyScript(JSContext* cx, 
 JSObject*
 Debugger::wrapWasmScript(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance)
 {
     Rooted<DebuggerScriptReferent> referent(cx, wasmInstance.get());
     return wrapVariantReferent(cx, referent);
 }
 
 static JSObject*
-DebuggerScript_check(JSContext* cx, const Value& v, const char* fnname)
+DebuggerScript_check(JSContext* cx, HandleValue v, const char* fnname)
 {
     JSObject* thisobj = NonNullObject(cx, v);
     if (!thisobj)
         return nullptr;
     if (thisobj->getClass() != &DebuggerScript_class) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                                   "Debugger.Script", fnname, thisobj->getClass()->name);
         return nullptr;
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -3,17 +3,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/EnvironmentObject-inl.h"
 
 #include "builtin/ModuleObject.h"
 #include "gc/Policy.h"
-#include "js/AutoByteString.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/AsyncFunction.h"
 #include "vm/GlobalObject.h"
 #include "vm/Iteration.h"
 #include "vm/ProxyObject.h"
 #include "vm/Realm.h"
 #include "vm/Shape.h"
 #include "vm/Xdr.h"
@@ -1410,20 +1409,19 @@ LiveEnvironmentVal::staticAsserts()
 
 /*****************************************************************************/
 
 namespace {
 
 static void
 ReportOptimizedOut(JSContext* cx, HandleId id)
 {
-    JSAutoByteString printable;
-    if (ValueToPrintableLatin1(cx, IdToValue(id), &printable)) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT,
-                                   printable.ptr());
+    if (UniqueChars printable = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier)) {
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT,
+                                 printable.get());
     }
 }
 
 /*
  * DebugEnvironmentProxy is the handler for DebugEnvironmentProxy proxy
  * objects. Having a custom handler (rather than trying to reuse js::Wrapper)
  * gives us several important abilities:
  *  - We want to pass the EnvironmentObject as the receiver to forwarded scope
@@ -3354,21 +3352,19 @@ js::CheckVarNameConflict(JSContext* cx, 
         return false;
     }
     return true;
 }
 
 static void
 ReportCannotDeclareGlobalBinding(JSContext* cx, HandlePropertyName name, const char* reason)
 {
-    JSAutoByteString printable;
-    if (AtomToPrintableString(cx, name, &printable)) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                                   JSMSG_CANT_DECLARE_GLOBAL_BINDING,
-                                   printable.ptr(), reason);
+    if (UniqueChars printable = AtomToPrintableString(cx, name)) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_DECLARE_GLOBAL_BINDING,
+                                  printable.get(), reason);
     }
 }
 
 bool
 js::CheckCanDeclareGlobalBinding(JSContext* cx, Handle<GlobalObject*> global,
                                  HandlePropertyName name, bool isFunction)
 {
     RootedId id(cx, NameToId(name));
--- a/js/src/vm/ErrorObject.cpp
+++ b/js/src/vm/ErrorObject.cpp
@@ -8,17 +8,16 @@
 #include "vm/ErrorObject-inl.h"
 
 #include "mozilla/Range.h"
 
 #include <utility>
 
 #include "jsexn.h"
 
-#include "js/AutoByteString.h"
 #include "js/CallArgs.h"
 #include "js/CharacterEncoding.h"
 #include "vm/GlobalObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
@@ -141,20 +140,20 @@ js::ErrorObject::getOrCreateErrorReport(
     // the nitty-gritty malloc stuff.
     JSErrorReport report;
 
     // Type.
     JSExnType type_ = type();
     report.exnType = type_;
 
     // Filename.
-    JSAutoByteString filenameStr;
-    if (!filenameStr.encodeLatin1(cx, fileName(cx)))
+    UniqueChars filenameStr = JS_EncodeStringToLatin1(cx, fileName(cx));
+    if (!filenameStr)
         return nullptr;
-    report.filename = filenameStr.ptr();
+    report.filename = filenameStr.get();
 
     // Coordinates.
     report.lineno = lineNumber();
     report.column = columnNumber();
 
     // Message. Note that |new Error()| will result in an undefined |message|
     // slot, so we need to explicitly substitute the empty string in that case.
     RootedString message(cx, getMessage());
--- a/js/src/vm/ForOfIterator.cpp
+++ b/js/src/vm/ForOfIterator.cpp
@@ -62,17 +62,17 @@ ForOfIterator::init(HandleValue iterable
     // Throw if obj[@@iterator] isn't callable.
     // js::Invoke is about to check for this kind of error anyway, but it would
     // throw an inscrutable error message about |method| rather than this nice
     // one about |obj|.
     if (!callee.isObject() || !callee.toObject().isCallable()) {
         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, nullptr);
         if (!bytes)
             return false;
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes.get());
         return false;
     }
 
     RootedValue res(cx);
     if (!js::Call(cx, callee, iterable, &res))
         return false;
 
     if (!res.isObject())
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -26,17 +26,17 @@
 #include "builtin/ModuleObject.h"
 #include "builtin/Promise.h"
 #include "builtin/String.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "jit/Jit.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "util/StringBuffer.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #ifdef ENABLE_BIGINT
 #include "vm/BigIntType.h"
 #endif
 #include "vm/BytecodeUtil.h"
 #include "vm/Debugger.h"
@@ -1856,33 +1856,33 @@ js::ReportInNotObjectError(JSContext* cx
             if (!buf.appendSubstring(str, 0, MaxStringLength))
                 return nullptr;
             if (!buf.append("..."))
                 return nullptr;
             str = buf.finishString();
             if (!str)
                 return nullptr;
         }
-        return UniqueChars(JS_EncodeString(cx, str));
+        return StringToNewUTF8CharsZ(cx, *str);
     };
 
     if (lref.isString() && rref.isString()) {
         UniqueChars lbytes = uniqueCharsFromString(cx, lref);
         if (!lbytes)
             return;
         UniqueChars rbytes = uniqueCharsFromString(cx, rref);
         if (!rbytes)
             return;
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_IN_STRING,
-                                   lbytes.get(), rbytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_IN_STRING,
+                                 lbytes.get(), rbytes.get());
         return;
     }
 
-    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_IN_NOT_OBJECT,
-                               InformalValueTypeName(rref));
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_IN_NOT_OBJECT,
+                              InformalValueTypeName(rref));
 }
 
 static MOZ_NEVER_INLINE bool
 Interpret(JSContext* cx, RunState& state)
 {
 /*
  * Define macros for an interpreter loop. Opcode dispatch may be either by a
  * switch statement or by indirect goto (aka a threaded interpreter), depending
@@ -5334,19 +5334,18 @@ js::NewArrayOperationWithTemplate(JSCont
     return obj;
 }
 
 void
 js::ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber, HandleId id)
 {
     MOZ_ASSERT(errorNumber == JSMSG_UNINITIALIZED_LEXICAL ||
                errorNumber == JSMSG_BAD_CONST_ASSIGN);
-    JSAutoByteString printable;
-    if (ValueToPrintableLatin1(cx, IdToValue(id), &printable))
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, printable.ptr());
+    if (UniqueChars printable = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier))
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, printable.get());
 }
 
 void
 js::ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber, HandlePropertyName name)
 {
     RootedId id(cx, NameToId(name));
     ReportRuntimeLexicalError(cx, errorNumber, id);
 }
@@ -5377,20 +5376,19 @@ js::ReportRuntimeLexicalError(JSContext*
     }
 
     ReportRuntimeLexicalError(cx, errorNumber, name);
 }
 
 void
 js::ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name, const char* redeclKind)
 {
-    JSAutoByteString printable;
-    if (AtomToPrintableString(cx, name, &printable)) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_REDECLARED_VAR,
-                                   redeclKind, printable.ptr());
+    if (UniqueChars printable = AtomToPrintableString(cx, name)) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_REDECLARED_VAR, redeclKind,
+                                  printable.get());
     }
 }
 
 bool
 js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind)
 {
     switch (kind) {
       case CheckIsObjectKind::IteratorNext:
@@ -5456,29 +5454,30 @@ js::ThrowUninitializedThis(JSContext* cx
                 break;
             }
         }
         MOZ_ASSERT(fun);
     }
 
     if (fun->isDerivedClassConstructor()) {
         const char* name = "anonymous";
-        JSAutoByteString str;
+        UniqueChars str;
         if (fun->explicitName()) {
-            if (!AtomToPrintableString(cx, fun->explicitName(), &str))
+            str = AtomToPrintableString(cx, fun->explicitName());
+            if (!str)
                 return false;
-            name = str.ptr();
+            name = str.get();
         }
 
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNINITIALIZED_THIS, name);
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNINITIALIZED_THIS, name);
         return false;
     }
 
     MOZ_ASSERT(fun->isArrow());
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNINITIALIZED_THIS_ARROW);
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNINITIALIZED_THIS_ARROW);
     return false;
 }
 
 JSObject*
 js::HomeObjectSuperBase(JSContext* cx, HandleObject homeObj)
 {
     RootedObject superBase(cx);
 
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -16,17 +16,17 @@
 #include "mozilla/Unused.h"
 
 #include <string.h>
 
 #include "jstypes.h"
 
 #include "builtin/String.h"
 #include "gc/Marking.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "util/Text.h"
 #include "vm/JSContext.h"
 #include "vm/SymbolType.h"
 #include "vm/Xdr.h"
 
 #include "gc/AtomMarking-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
@@ -111,24 +111,20 @@ inline JSAtom*
 js::AtomStateEntry::asPtr(JSContext* cx) const
 {
     JSAtom* atom = asPtrUnbarriered();
     if (!cx->helperThread())
         JSString::readBarrier(atom);
     return atom;
 }
 
-const char*
-js::AtomToPrintableString(JSContext* cx, JSAtom* atom, JSAutoByteString* bytes)
+UniqueChars
+js::AtomToPrintableString(JSContext* cx, JSAtom* atom)
 {
-    JSString* str = QuoteString(cx, atom, 0);
-    if (!str)
-        return nullptr;
-    bytes->initBytes(EncodeLatin1(cx, str));
-    return bytes->ptr();
+    return QuoteString(cx, atom);
 }
 
 #define DEFINE_PROTO_STRING(name,init,clasp) const char js_##name##_str[] = #name;
 JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING)
 #undef DEFINE_PROTO_STRING
 
 #define CONST_CHAR_STR(idpart, id, text) const char js_##idpart##_str[] = text;
 FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR)
--- a/js/src/vm/JSAtom.h
+++ b/js/src/vm/JSAtom.h
@@ -6,28 +6,27 @@
 
 #ifndef vm_JSAtom_h
 #define vm_JSAtom_h
 
 #include "mozilla/Maybe.h"
 
 #include "gc/Rooting.h"
 #include "js/TypeDecls.h"
+#include "js/Utility.h"
 #include "vm/CommonPropertyNames.h"
 
-class JSAutoByteString;
-
 namespace js {
 
 /*
  * Return a printable, lossless char[] representation of a string-type atom.
- * The lifetime of the result matches the lifetime of bytes.
+ * The returned string is guaranteed to contain only ASCII characters.
  */
-extern const char*
-AtomToPrintableString(JSContext* cx, JSAtom* atom, JSAutoByteString* bytes);
+extern UniqueChars
+AtomToPrintableString(JSContext* cx, JSAtom* atom);
 
 class PropertyName;
 
 }  /* namespace js */
 
 /* Well-known predefined C strings. */
 #define DECLARE_PROTO_STR(name,init,clasp) extern const char js_##name##_str[];
 JS_FOR_EACH_PROTOTYPE(DECLARE_PROTO_STR)
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -32,17 +32,16 @@
 #include "jspubtd.h"
 #include "jstypes.h"
 
 #include "builtin/String.h"
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "jit/Ion.h"
 #include "jit/PcScriptCache.h"
-#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/Printf.h"
 #ifdef JS_SIMULATOR_ARM64
 # include "jit/arm64/vixl/Simulator-vixl.h"
 #endif
 #ifdef JS_SIMULATOR_ARM
 # include "jit/arm/Simulator-arm.h"
 #endif
@@ -437,20 +436,20 @@ js::ReportUsageErrorASCII(JSContext* cx,
     RootedValue usage(cx);
     if (!JS_GetProperty(cx, callee, "usage", &usage))
         return;
 
     if (!usage.isString()) {
         JS_ReportErrorASCII(cx, "%s", msg);
     } else {
         RootedString usageStr(cx, usage.toString());
-        JSAutoByteString str;
-        if (!str.encodeUtf8(cx, usageStr))
+        UniqueChars str = JS_EncodeStringToUTF8(cx, usageStr);
+        if (!str)
             return;
-        JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.ptr());
+        JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.get());
     }
 }
 
 enum class PrintErrorKind {
     Error,
     Warning,
     StrictWarning,
     Note
@@ -888,20 +887,18 @@ js::ReportErrorNumberUCArray(JSContext* 
     ReportError(cx, &report, callback, userRef);
 
     return warning;
 }
 
 void
 js::ReportIsNotDefined(JSContext* cx, HandleId id)
 {
-    JSAutoByteString printable;
-    if (!ValueToPrintableUTF8(cx, IdToValue(id), &printable))
-        return;
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, printable.ptr());
+    if (UniqueChars printable = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier))
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, printable.get());
 }
 
 void
 js::ReportIsNotDefined(JSContext* cx, HandlePropertyName name)
 {
     RootedId id(cx, NameToId(name));
     ReportIsNotDefined(cx, id);
 }
@@ -917,104 +914,91 @@ js::ReportIsNullOrUndefinedForPropertyAc
         return;
     }
 
     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
     if (!bytes)
         return;
 
     if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES,
-                                   bytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES, bytes.get());
     } else if (v.isUndefined()) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                                   bytes.get(), js_undefined_str);
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, bytes.get(),
+                                 js_undefined_str);
     } else {
         MOZ_ASSERT(v.isNull());
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                                   bytes.get(), js_null_str);
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, bytes.get(),
+                                 js_null_str);
     }
 }
 
-char*
-EncodeIdAsLatin1(JSContext* cx, HandleId id, JSAutoByteString& bytes)
-{
-    RootedValue idVal(cx, IdToValue(id));
-    RootedString idStr(cx, ValueToSource(cx, idVal));
-    if (!idStr)
-        return nullptr;
-
-    return bytes.encodeLatin1(cx, idStr);
-}
-
 void
 js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v, HandleId key,
                                              bool reportScanStack)
 {
     MOZ_ASSERT(v.isNullOrUndefined());
 
-    JSAutoByteString keyBytes;
-    if (!EncodeIdAsLatin1(cx, key, keyBytes))
+    UniqueChars keyBytes = IdToPrintableUTF8(cx, key, IdToPrintableBehavior::IdIsPropertyKey);
+    if (!keyBytes)
         return;
 
     if (!reportScanStack) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
-                                   keyBytes.ptr(),
-                                   v.isUndefined() ? js_undefined_str : js_null_str);
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
+                                 keyBytes.get(),
+                                 v.isUndefined() ? js_undefined_str : js_null_str);
         return;
     }
 
     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
     if (!bytes)
         return;
 
     if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
-                                   keyBytes.ptr(), bytes.get());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
+                                 keyBytes.get(), bytes.get());
     } else if (v.isUndefined()) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
-                                   bytes.get(), js_undefined_str, keyBytes.ptr());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
+                                 bytes.get(), js_undefined_str, keyBytes.get());
     } else {
         MOZ_ASSERT(v.isNull());
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
-                                   bytes.get(), js_null_str, keyBytes.ptr());
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
+                                 bytes.get(), js_null_str, keyBytes.get());
     }
 }
 
 void
 js::ReportMissingArg(JSContext* cx, HandleValue v, unsigned arg)
 {
     char argbuf[11];
     UniqueChars bytes;
 
     SprintfLiteral(argbuf, "%u", arg);
     if (IsFunctionObject(v)) {
         RootedAtom name(cx, v.toObject().as<JSFunction>().explicitName());
         bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, name);
         if (!bytes)
             return;
     }
-    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                               JSMSG_MISSING_FUN_ARG,
-                               argbuf, bytes ? bytes.get() : "");
+    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_MISSING_FUN_ARG,
+                             argbuf, bytes ? bytes.get() : "");
 }
 
 bool
 js::ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNumber,
                           int spindex, HandleValue v, HandleString fallback,
                           const char* arg1, const char* arg2)
 {
     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
     UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback);
     if (!bytes)
         return false;
 
-    return JS_ReportErrorFlagsAndNumberLatin1(cx, flags, GetErrorMessage, nullptr, errorNumber,
-                                              bytes.get(), arg1, arg2);
+    return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr, errorNumber,
+                                            bytes.get(), arg1, arg2);
 }
 
 JSObject*
 js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report)
 {
     RootedArrayObject notesArray(cx, NewDenseEmptyArray(cx));
     if (!notesArray)
         return nullptr;
--- a/js/src/vm/JSFunction-inl.h
+++ b/js/src/vm/JSFunction-inl.h
@@ -6,29 +6,30 @@
 
 #ifndef vm_JSFunction_inl_h
 #define vm_JSFunction_inl_h
 
 #include "vm/JSFunction.h"
 
 #include "gc/Allocator.h"
 #include "gc/GCTrace.h"
+#include "js/CharacterEncoding.h"
 #include "vm/EnvironmentObject.h"
 
 #include "vm/JSObject-inl.h"
 
-class JSAutoByteString;
-
 namespace js {
 
 inline const char*
-GetFunctionNameBytes(JSContext* cx, JSFunction* fun, JSAutoByteString* bytes)
+GetFunctionNameBytes(JSContext* cx, JSFunction* fun, UniqueChars* bytes)
 {
-    if (JSAtom* name = fun->explicitName())
-        return bytes->encodeLatin1(cx, name);
+    if (JSAtom* name = fun->explicitName()) {
+        *bytes = EncodeLatin1(cx, name);
+        return bytes->get();
+    }
     return js_anonymous_str;
 }
 
 inline bool
 CanReuseFunctionForClone(JSContext* cx, HandleFunction fun)
 {
     if (!fun->isSingleton())
         return false;
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -26,17 +26,16 @@
 #include "builtin/SelfHostingDefines.h"
 #include "builtin/String.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
-#include "js/AutoByteString.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/CompileOptions.h"
 #include "js/Proxy.h"
 
 #include "js/SourceBufferHolder.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
@@ -2494,29 +2493,29 @@ js::ReportIncompatibleMethod(JSContext* 
     } else if (thisv.isSymbol()) {
         MOZ_ASSERT(clasp != &SymbolObject::class_);
     } else {
         MOZ_ASSERT(thisv.isUndefined() || thisv.isNull());
     }
 #endif
 
     if (JSFunction* fun = ReportIfNotFunction(cx, args.calleev())) {
-        JSAutoByteString funNameBytes;
+        UniqueChars funNameBytes;
         if (const char* funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
             JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                                        clasp->name, funName, InformalValueTypeName(thisv));
         }
     }
 }
 
 void
 js::ReportIncompatible(JSContext* cx, const CallArgs& args)
 {
     if (JSFunction* fun = ReportIfNotFunction(cx, args.calleev())) {
-        JSAutoByteString funNameBytes;
+        UniqueChars funNameBytes;
         if (const char* funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
             JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_METHOD,
                                        funName, "method", InformalValueTypeName(args.thisv()));
         }
     }
 }
 
 namespace JS {
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -30,17 +30,17 @@
 #endif
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
 #include "builtin/String.h"
 #include "builtin/Symbol.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Policy.h"
 #include "jit/BaselineJIT.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/MemoryMetrics.h"
 #include "js/Proxy.h"
 #include "js/UbiNode.h"
 #include "js/UniquePtr.h"
 #include "js/Wrapper.h"
 #include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/ArgumentsObject.h"
@@ -74,45 +74,44 @@
 #include "vm/StringObject-inl.h"
 #include "vm/TypedArrayObject-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 void
-js::ReportNotObject(JSContext* cx, const Value& v)
+js::ReportNotObject(JSContext* cx, HandleValue v)
 {
     MOZ_ASSERT(!v.isObject());
 
-    RootedValue value(cx, v);
-    UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr);
-    if (bytes)
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
-                                   bytes.get());
+    if (UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr)) {
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
+                                 bytes.get());
+    }
 }
 
 void
 js::ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun, HandleValue v)
 {
     MOZ_ASSERT(!v.isObject());
 
-    JSAutoByteString bytes;
+    UniqueChars bytes;
     if (const char* chars = ValueToSourceForError(cx, v, bytes)) {
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT_ARG,
                                    nth, fun, chars);
     }
 }
 
 void
 js::ReportNotObjectWithName(JSContext* cx, const char* name, HandleValue v)
 {
     MOZ_ASSERT(!v.isObject());
 
-    JSAutoByteString bytes;
+    UniqueChars bytes;
     if (const char* chars = ValueToSourceForError(cx, v, bytes)) {
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT_NAME,
                                    name, chars);
     }
 }
 
 JS_PUBLIC_API(const char*)
 JS::InformalValueTypeName(const Value& v)
@@ -224,18 +223,18 @@ js::GetFirstArgumentAsObject(JSContext* 
         return false;
     }
 
     HandleValue v = args[0];
     if (!v.isObject()) {
         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
         if (!bytes)
             return false;
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                                   bytes.get(), "not an object");
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
+                                 bytes.get(), "not an object");
         return false;
     }
 
     objp.set(&v.toObject());
     return true;
 }
 
 static bool
@@ -248,34 +247,29 @@ GetPropertyIfPresent(JSContext* cx, Hand
         vp.setUndefined();
         return true;
     }
 
     return GetProperty(cx, obj, obj, id, vp);
 }
 
 bool
-js::Throw(JSContext* cx, jsid id, unsigned errorNumber, const char* details)
+js::Throw(JSContext* cx, HandleId id, unsigned errorNumber, const char* details)
 {
     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == (details ? 2 : 1));
-
-    RootedValue idVal(cx, IdToValue(id));
-    JSString* idstr = ValueToSource(cx, idVal);
-    if (!idstr)
-       return false;
-    JSAutoByteString bytes(cx, idstr);
+    MOZ_ASSERT_IF(details, JS::StringIsASCII(details));
+
+    UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
     if (!bytes)
         return false;
 
-    if (details) {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, bytes.ptr(),
-                                   details);
-    } else {
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, bytes.ptr());
-    }
+    if (details)
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, bytes.get(), details);
+    else
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, bytes.get());
 
     return false;
 }
 
 
 /*** PropertyDescriptor operations and DefineProperties ******************************************/
 
 static const char js_getter_str[] = "getter";
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -1236,20 +1236,20 @@ template<XDRMode mode>
 XDRResult
 XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj);
 
 /*
  * Report a TypeError: "so-and-so is not an object".
  * Using NotNullObject is usually less code.
  */
 extern void
-ReportNotObject(JSContext* cx, const Value& v);
+ReportNotObject(JSContext* cx, HandleValue v);
 
 inline JSObject*
-NonNullObject(JSContext* cx, const Value& v)
+NonNullObject(JSContext* cx, HandleValue v)
 {
     if (v.isObject())
         return &v.toObject();
     ReportNotObject(cx, v);
     return nullptr;
 }
 
 
@@ -1287,17 +1287,17 @@ NonNullObjectWithName(JSContext* cx, con
 
 
 extern bool
 GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* method,
                          MutableHandleObject objp);
 
 /* Helper for throwing, always returns false. */
 extern bool
-Throw(JSContext* cx, jsid id, unsigned errorNumber, const char* details = nullptr);
+Throw(JSContext* cx, HandleId id, unsigned errorNumber, const char* details = nullptr);
 
 /*
  * ES6 rev 29 (6 Dec 2014) 7.3.13. Mark obj as non-extensible, and adjust each
  * of obj's own properties' attributes appropriately: each property becomes
  * non-configurable, and if level == Frozen, data properties become
  * non-writable as well.
  */
 extern bool
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -7,17 +7,17 @@
 #include "vm/NativeObject-inl.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 
 #include "gc/Marking.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/Value.h"
 #include "vm/Debugger.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 #include "gc/Nursery-inl.h"
 #include "vm/ArrayObject-inl.h"
 #include "vm/EnvironmentObject-inl.h"
@@ -2299,19 +2299,22 @@ GetNonexistentProperty(JSContext* cx, Ha
     pc += CodeSpec[*pc].length;
     if (Detecting(cx, script, pc))
         return true;
 
     unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
     script->setWarnedAboutUndefinedProp();
 
     // Ok, bad undefined property reference: whine about it.
-    RootedValue val(cx, IdToValue(id));
-    return ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, JSDVG_IGNORE_STACK, val,
-                                    nullptr, nullptr, nullptr);
+    UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
+    if (!bytes)
+        return false;
+
+    return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr,
+                                            JSMSG_UNDEFINED_PROP, bytes.get());
 }
 
 /* The NoGC version of GetNonexistentProperty, present only to make types line up. */
 bool
 GetNonexistentProperty(JSContext* cx, const jsid& id, IsNameLookup nameLookup,
                        FakeMutableHandle<Value> vp)
 {
     return false;
@@ -2451,17 +2454,17 @@ js::GetNameBoundInEnvironment(JSContext*
         return GeneralizedGetProperty(cx, env, id, receiver, NameLookup, vp);
     return NativeGetPropertyInline<CanGC>(cx, env.as<NativeObject>(), receiver, id, NameLookup, vp);
 }
 
 
 /*** [[Set]] *************************************************************************************/
 
 static bool
-MaybeReportUndeclaredVarAssignment(JSContext* cx, HandleString propname)
+MaybeReportUndeclaredVarAssignment(JSContext* cx, HandleId id)
 {
     unsigned flags;
     {
         jsbytecode* pc;
         JSScript* script = cx->currentScript(&pc, JSContext::AllowCrossRealm::Allow);
         if (!script)
             return true;
 
@@ -2470,21 +2473,21 @@ MaybeReportUndeclaredVarAssignment(JSCon
         if (IsStrictSetPC(pc))
             flags = JSREPORT_ERROR;
         else if (cx->realm()->behaviors().extraWarnings(cx))
             flags = JSREPORT_WARNING | JSREPORT_STRICT;
         else
             return true;
     }
 
-    JSAutoByteString bytes;
-    if (!bytes.encodeUtf8(cx, propname))
+    UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier);
+    if (!bytes)
         return false;
     return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr,
-                                            JSMSG_UNDECLARED_VAR, bytes.ptr());
+                                            JSMSG_UNDECLARED_VAR, bytes.get());
 }
 
 /*
  * Finish assignment to a shapeful data property of a native object obj. This
  * conforms to no standard and there is a lot of legacy baggage here.
  */
 static bool
 NativeSetExistingDataProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape,
@@ -2590,18 +2593,17 @@ js::SetPropertyOnProto(JSContext* cx, Ha
  * steps 4.d.i and 5.
  */
 template <QualifiedBool IsQualified>
 static bool
 SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v,
                        HandleValue receiver, ObjectOpResult& result)
 {
     if (!IsQualified && receiver.isObject() && receiver.toObject().isUnqualifiedVarObj()) {
-        RootedString idStr(cx, JSID_TO_STRING(id));
-        if (!MaybeReportUndeclaredVarAssignment(cx, idStr))
+        if (!MaybeReportUndeclaredVarAssignment(cx, id))
             return false;
     }
 
     // Pure optimization for the common case. There's no point performing the
     // lookup in step 5.c again, as our caller just did it for us.
     if (IsQualified && receiver.isObject() && obj == &receiver.toObject()) {
         // Ensure that a custom GetOwnPropertyOp, if present, doesn't
         // introduce additional properties which weren't previously found by
--- a/js/src/vm/Printer.cpp
+++ b/js/src/vm/Printer.cpp
@@ -3,24 +3,26 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/Printer.h"
 
 #include "mozilla/PodOperations.h"
 #include "mozilla/Printf.h"
+#include "mozilla/RangedPtr.h"
 
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 #include "jsutil.h"
 
 #include "ds/LifoAlloc.h"
+#include "js/CharacterEncoding.h"
 #include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/JSContext.h"
 
 using mozilla::PodCopy;
 
 namespace
 {
@@ -94,17 +96,17 @@ Sprinter::realloc_(size_t newSize)
     MOZ_ASSERT(newSize > (size_t) offset);
     char* newBuf = (char*) js_realloc(base, newSize);
     if (!newBuf) {
         reportOutOfMemory();
         return false;
     }
     base = newBuf;
     size = newSize;
-    base[size - 1] = 0;
+    base[size - 1] = '\0';
     return true;
 }
 
 Sprinter::Sprinter(JSContext* cx, bool shouldReportOOM)
   : context(cx),
 #ifdef DEBUG
     initialized(false),
 #endif
@@ -128,44 +130,44 @@ Sprinter::init()
     base = js_pod_malloc<char>(DefaultSize);
     if (!base) {
         reportOutOfMemory();
         return false;
     }
 #ifdef DEBUG
     initialized = true;
 #endif
-    *base = 0;
+    *base = '\0';
     size = DefaultSize;
-    base[size - 1] = 0;
+    base[size - 1] = '\0';
     return true;
 }
 
 void
 Sprinter::checkInvariants() const
 {
     MOZ_ASSERT(initialized);
     MOZ_ASSERT((size_t) offset < size);
-    MOZ_ASSERT(base[size - 1] == 0);
+    MOZ_ASSERT(base[size - 1] == '\0');
 }
 
-char*
+UniqueChars
 Sprinter::release()
 {
     checkInvariants();
     if (hadOOM_)
         return nullptr;
 
     char* str = base;
     base = nullptr;
     offset = size = 0;
 #ifdef DEBUG
     initialized = false;
 #endif
-    return str;
+    return UniqueChars(str);
 }
 
 char*
 Sprinter::stringAt(ptrdiff_t off) const
 {
     MOZ_ASSERT(off >= 0 && (size_t) off < size);
     return base + off;
 }
@@ -209,45 +211,38 @@ Sprinter::put(const char* s, size_t len)
         /* buffer was realloc'ed */
         if (base != oldBase)
             s = stringAt(s - oldBase);  /* this is where it lives now */
         memmove(bp, s, len);
     } else {
         js_memcpy(bp, s, len);
     }
 
-    bp[len] = 0;
+    bp[len] = '\0';
     return true;
 }
 
 bool
 Sprinter::putString(JSString* s)
 {
     InvariantChecker ic(this);
 
-    size_t length = s->length();
+    JSFlatString* flat = s->ensureFlat(context);
+    if (!flat)
+        return false;
+
+    size_t length = JS::GetDeflatedUTF8StringLength(flat);
 
     char* buffer = reserve(length);
     if (!buffer)
         return false;
 
-    JSLinearString* linear = s->ensureLinear(context);
-    if (!linear)
-        return false;
+    JS::DeflateStringToUTF8Buffer(flat, mozilla::RangedPtr<char>(buffer, length));
 
-    JS::AutoCheckCannotGC nogc;
-    if (linear->hasLatin1Chars()) {
-        PodCopy(reinterpret_cast<Latin1Char*>(buffer), linear->latin1Chars(nogc), length);
-    } else {
-        const char16_t* src = linear->twoByteChars(nogc);
-        for (size_t i = 0; i < length; i++)
-            buffer[i] = char(src[i]);
-    }
-
-    buffer[length] = 0;
+    buffer[length] = '\0';
     return true;
 }
 
 ptrdiff_t
 Sprinter::getOffset() const
 {
     return offset;
 }
@@ -283,27 +278,24 @@ const char js_EscapeMap[] = {
     '\v', 'v',
     '"',  '"',
     '\'', '\'',
     '\\', '\\',
     '\0'
 };
 
 template <typename CharT>
-static char*
-QuoteString(Sprinter* sp, const mozilla::Range<const CharT> chars, char16_t quote)
+static bool
+QuoteString(Sprinter* sp, const mozilla::Range<const CharT> chars, char quote)
 {
     using CharPtr = mozilla::RangedPtr<const CharT>;
 
-    /* Sample off first for later return value pointer computation. */
-    ptrdiff_t offset = sp->getOffset();
-
     if (quote) {
-        if (!sp->jsprintf("%c", char(quote)))
-            return nullptr;
+        if (!sp->putChar(quote))
+            return false;
     }
 
     const CharPtr end = chars.end();
 
     /* Loop control variables: end points at end of string sentinel. */
     for (CharPtr t = chars.begin(); t < end; ++t) {
         /* Move t forward from s past un-quote-worthy characters. */
         const CharPtr s = t;
@@ -314,83 +306,73 @@ QuoteString(Sprinter* sp, const mozilla:
                 break;
             c = *t;
         }
 
         {
             ptrdiff_t len = t - s;
             ptrdiff_t base = sp->getOffset();
             if (!sp->reserve(len))
-                return nullptr;
+                return false;
 
             for (ptrdiff_t i = 0; i < len; ++i)
                 (*sp)[base + i] = char(s[i]);
-            (*sp)[base + len] = 0;
+            (*sp)[base + len] = '\0';
         }
 
         if (t == end)
             break;
 
         /* Use js_EscapeMap, \u, or \x only if necessary. */
         const char* escape;
         if (!(c >> 8) && c != 0 && (escape = strchr(js_EscapeMap, int(c))) != nullptr) {
             if (!sp->jsprintf("\\%c", escape[1]))
-                return nullptr;
+                return false;
         } else {
             /*
              * Use \x only if the high byte is 0 and we're in a quoted string,
              * because ECMA-262 allows only \u, not \x, in Unicode identifiers
              * (see bug 621814).
              */
             if (!sp->jsprintf((quote && !(c >> 8)) ? "\\x%02X" : "\\u%04X", c))
-                return nullptr;
+                return false;
         }
     }
 
     /* Sprint the closing quote and return the quoted string. */
     if (quote) {
-        if (!sp->jsprintf("%c", char(quote)))
-            return nullptr;
+        if (!sp->putChar(quote))
+            return false;
     }
 
-    /*
-     * If we haven't Sprint'd anything yet, Sprint an empty string so that
-     * the return below gives a valid result.
-     */
-    if (offset == sp->getOffset()) {
-        if (!sp->put(""))
-            return nullptr;
-    }
-
-    return sp->stringAt(offset);
+    return true;
 }
 
-char*
-QuoteString(Sprinter* sp, JSString* str, char16_t quote)
+bool
+QuoteString(Sprinter* sp, JSString* str, char quote /*= '\0' */)
 {
     JSLinearString* linear = str->ensureLinear(sp->context);
     if (!linear)
-        return nullptr;
+        return false;
 
     JS::AutoCheckCannotGC nogc;
     return linear->hasLatin1Chars()
            ? QuoteString(sp, linear->latin1Range(nogc), quote)
            : QuoteString(sp, linear->twoByteRange(nogc), quote);
 }
 
-JSString*
-QuoteString(JSContext* cx, JSString* str, char16_t quote)
+UniqueChars
+QuoteString(JSContext* cx, JSString* str, char quote /* = '\0' */)
 {
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return nullptr;
-    char* bytes = QuoteString(&sprinter, str, quote);
-    if (!bytes)
+    if (!QuoteString(&sprinter, str, quote))
         return nullptr;
-    return NewStringCopyZ<CanGC>(cx, bytes);
+    return sprinter.release();
 }
 
 Fprinter::Fprinter(FILE* fp)
   : file_(nullptr),
     init_(false)
 {
     init(fp);
 }
--- a/js/src/vm/Printer.h
+++ b/js/src/vm/Printer.h
@@ -10,16 +10,17 @@
 #include "mozilla/Attributes.h"
 
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "js/TypeDecls.h"
+#include "js/Utility.h"
 
 namespace js {
 
 class LifoAlloc;
 
 // Generic printf interface, similar to an ostream in the standard library.
 //
 // This class is useful to make generic printers which can work either with a
@@ -95,17 +96,17 @@ class Sprinter final : public GenericPri
 
     // Initialize this sprinter, returns false on error.
     MOZ_MUST_USE bool init();
 
     void checkInvariants() const;
 
     const char* string() const { return base; }
     const char* stringEnd() const { return base + offset; }
-    char* release();
+    JS::UniqueChars release();
 
     // Returns the string at offset |off|.
     char* stringAt(ptrdiff_t off) const;
     // Returns the char at offset |off|.
     char& operator[](size_t off);
 
     // Attempt to reserve len + 1 space (for a trailing nullptr byte). If the
     // attempt succeeds, return a pointer to the start of that space and adjust the
@@ -206,21 +207,24 @@ class LSprinter final : public GenericPr
     // return true on success, false on failure.
     virtual bool put(const char* s, size_t len) override;
     using GenericPrinter::put; // pick up |inline bool put(const char* s);|
 };
 
 // Map escaped code to the letter/symbol escaped with a backslash.
 extern const char       js_EscapeMap[];
 
-// Return a GC'ed string containing the chars in str, with any non-printing
-// chars or quotes (' or " as specified by the quote argument) escaped, and
-// with the quote character at the beginning and end of the result string.
-extern JSString*
-QuoteString(JSContext* cx, JSString* str, char16_t quote);
+// Return a C-string containing the chars in str, with any non-printing chars
+// escaped. If the optional quote parameter is present and is not '\0', quotes
+// (as specified by the quote argument) are also escaped, and the quote
+// character is appended at the beginning and end of the result string.
+// The returned string is guaranteed to contain only ASCII characters.
+extern JS::UniqueChars
+QuoteString(JSContext* cx, JSString* str, char quote = '\0');
 
-extern char*
-QuoteString(Sprinter* sp, JSString* str, char16_t quote);
-
+// Appends the quoted string to the given Sprinter. Follows the same semantics
+// as QuoteString from above.
+extern bool
+QuoteString(Sprinter* sp, JSString* str, char quote = '\0');
 
 } // namespace js
 
 #endif // vm_Printer_h
--- a/js/src/vm/Probes.cpp
+++ b/js/src/vm/Probes.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/Probes-inl.h"
 
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "vm/JSContext.h"
 
 #ifdef INCLUDE_MOZILLA_DTRACE
 #include "vm/JSScript-inl.h"
 #endif
 
 #define TYPEOF(cx,v)    (v.isNull() ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
 
@@ -29,40 +29,41 @@ ScriptFilename(const JSScript* script)
     if (!script)
         return probes::nullName;
     if (!script->filename())
         return probes::anonymousName;
     return script->filename();
 }
 
 static const char*
-FunctionName(JSContext* cx, JSFunction* fun, JSAutoByteString* bytes)
+FunctionName(JSContext* cx, JSFunction* fun, UniqueChars* bytes)
 {
     if (!fun)
         return probes::nullName;
     if (!fun->displayAtom())
         return probes::anonymousName;
-    return bytes->encodeLatin1(cx, fun->displayAtom()) ? bytes->ptr() : probes::nullName;
+    *bytes = JS_EncodeStringToLatin1(cx, fun->displayAtom());
+    return *bytes ? bytes->get() : probes::nullName;
 }
 
 /*
  * These functions call the DTrace macros for the JavaScript USDT probes.
  * Originally this code was inlined in the JavaScript code; however since
  * a number of operations are called, these have been placed into functions
  * to reduce any negative compiler optimization effect that the addition of
  * a number of usually unused lines of code would cause.
  */
 void
 probes::DTraceEnterJSFun(JSContext* cx, JSFunction* fun, JSScript* script)
 {
-    JSAutoByteString funNameBytes;
+    UniqueChars funNameBytes;
     JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), probes::nullName,
                               FunctionName(cx, fun, &funNameBytes));
 }
 
 void
 probes::DTraceExitJSFun(JSContext* cx, JSFunction* fun, JSScript* script)
 {
-    JSAutoByteString funNameBytes;
+    UniqueChars funNameBytes;
     JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), probes::nullName,
                                FunctionName(cx, fun, &funNameBytes));
 }
 #endif
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1815,25 +1815,24 @@ SavedStacks::MetadataBuilder::build(JSCo
     return frame;
 }
 
 const SavedStacks::MetadataBuilder SavedStacks::metadataBuilder;
 
 /* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsSystem;
 /* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsNotSystem;
 
-UTF8CharsZ
+UniqueChars
 BuildUTF8StackString(JSContext* cx, JSPrincipals* principals, HandleObject stack)
 {
     RootedString stackStr(cx);
     if (!JS::BuildStackString(cx, principals, stack, &stackStr))
-        return UTF8CharsZ();
+        return nullptr;
 
-    char* chars = JS_EncodeStringToUTF8(cx, stackStr);
-    return UTF8CharsZ(chars, strlen(chars));
+    return JS_EncodeStringToUTF8(cx, stackStr);
 }
 
 uint32_t
 FixupColumnForDisplay(uint32_t column)
 {
     // As described in WasmFrameIter::computeLine(), for wasm frames, the
     // function index is returned as the column with the high bit set. In paths
     // that format error stacks into strings, this information can be used to
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -315,17 +315,17 @@ struct MutableWrappedPtrOperations<Saved
     void setColumn(uint32_t v) { loc().column = v; }
 
   private:
     SavedStacks::LocationValue& loc() {
         return static_cast<Wrapper*>(this)->get();
     }
 };
 
-UTF8CharsZ
+JS::UniqueChars
 BuildUTF8StackString(JSContext* cx, JSPrincipals* principals, HandleObject stack);
 
 uint32_t
 FixupColumnForDisplay(uint32_t column);
 
 } /* namespace js */
 
 #endif /* vm_SavedStacks_h */
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -9,17 +9,16 @@
 #include "mozilla/ScopeExit.h"
 
 #include <memory>
 #include <new>
 
 #include "builtin/ModuleObject.h"
 #include "gc/Allocator.h"
 #include "gc/FreeOp.h"
-#include "js/AutoByteString.h"
 #include "util/StringBuffer.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSScript.h"
 #include "wasm/WasmInstance.h"
 
 #include "gc/ObjectKind-inl.h"
 #include "vm/Shape-inl.h"
 
@@ -1515,20 +1514,20 @@ PositionalFormalParameterIter::Positiona
     settle();
 }
 
 void
 js::DumpBindings(JSContext* cx, Scope* scopeArg)
 {
     RootedScope scope(cx, scopeArg);
     for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
-        JSAutoByteString bytes;
-        if (!AtomToPrintableString(cx, bi.name(), &bytes))
+        UniqueChars bytes = AtomToPrintableString(cx, bi.name());
+        if (!bytes)
             return;
-        fprintf(stderr, "%s %s ", BindingKindString(bi.kind()), bytes.ptr());
+        fprintf(stderr, "%s %s ", BindingKindString(bi.kind()), bytes.get());
         switch (bi.location().kind()) {
           case BindingLocation::Kind::Global:
             if (bi.isTopLevelFunction())
                 fprintf(stderr, "global function\n");
             else
                 fprintf(stderr, "global\n");
             break;
           case BindingLocation::Kind::Argument:
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -33,17 +33,16 @@
 #include "builtin/String.h"
 #include "builtin/TypedObject.h"
 #include "builtin/WeakMapObject.h"
 #include "gc/HashUtil.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
-#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/Date.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Compression.h"
@@ -288,38 +287,33 @@ ThrowErrorWithType(JSContext* cx, JSExnT
     uint32_t errorNumber = args[0].toInt32();
 
 #ifdef DEBUG
     const JSErrorFormatString* efs = GetErrorMessage(nullptr, errorNumber);
     MOZ_ASSERT(efs->argCount == args.length() - 1);
     MOZ_ASSERT(efs->exnType == type, "error-throwing intrinsic and error number are inconsistent");
 #endif
 
-    JSAutoByteString errorArgs[3];
+    UniqueChars errorArgs[3];
     for (unsigned i = 1; i < 4 && i < args.length(); i++) {
-        RootedValue val(cx, args[i]);
-        if (val.isInt32()) {
+        HandleValue val = args[i];
+        if (val.isInt32() || val.isString()) {
             JSString* str = ToString<CanGC>(cx, val);
             if (!str)
                 return;
-            errorArgs[i - 1].encodeLatin1(cx, str);
-        } else if (val.isString()) {
-            errorArgs[i - 1].encodeLatin1(cx, val.toString());
+            errorArgs[i - 1] = StringToNewUTF8CharsZ(cx, *str);
         } else {
-            UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr);
-            if (!bytes)
-                return;
-            errorArgs[i - 1].initBytes(std::move(bytes));
+            errorArgs[i - 1] = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr);
         }
         if (!errorArgs[i - 1])
             return;
     }
 
-    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber,
-                               errorArgs[0].ptr(), errorArgs[1].ptr(), errorArgs[2].ptr());
+    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
+                             errorArgs[0].get(), errorArgs[1].get(), errorArgs[2].get());
 }
 
 static bool
 intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() >= 1);
 
@@ -524,23 +518,20 @@ intrinsic_FinishBoundFunctionInit(JSCont
  */
 static bool
 intrinsic_DecompileArg(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
 
     HandleValue value = args[1];
-    UniqueChars str = DecompileArgument(cx, args[0].toInt32(), value);
+    JSString* str = DecompileArgument(cx, args[0].toInt32(), value);
     if (!str)
         return false;
-    JSString* result = NewStringCopyZ<CanGC>(cx, str.get());
-    if (!result)
-        return false;
-    args.rval().setString(result);
+    args.rval().setString(str);
     return true;
 }
 
 static bool
 intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -1864,17 +1855,17 @@ js::ReportIncompatibleSelfHostedMethod(J
     // like array.sort(somethingSelfHosted), where we want to report the error
     // in the somethingSelfHosted, not in the sort() call.
     ScriptFrameIter iter(cx);
     MOZ_ASSERT(iter.isFunctionFrame());
 
     while (!iter.done()) {
         MOZ_ASSERT(iter.callee(cx)->isSelfHostedOrIntrinsic() &&
                    !iter.callee(cx)->isBoundFunction());
-        JSAutoByteString funNameBytes;
+        UniqueChars funNameBytes;
         const char* funName = GetFunctionNameBytes(cx, iter.callee(cx), &funNameBytes);
         if (!funName)
             return false;
         if (strcmp(funName, "IsTypedArrayEnsuringArrayBuffer") != 0) {
             JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_METHOD,
                                        funName, "method", InformalValueTypeName(args.thisv()));
             return false;
         }
@@ -2861,18 +2852,22 @@ VerifyGlobalNames(JSContext* cx, Handle<
                     nameMissing = true;
                     break;
                 }
             }
         }
     }
 
     if (nameMissing) {
-        RootedValue value(cx, IdToValue(id));
-        ReportValueError(cx, JSMSG_NO_SUCH_SELF_HOSTED_PROP, JSDVG_IGNORE_STACK, value, nullptr);
+        UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
+        if (!bytes)
+            return false;
+
+        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_SUCH_SELF_HOSTED_PROP,
+                                 bytes.get());
         return false;
     }
 #endif // DEBUG
 
     return true;
 }
 
 bool
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -12,20 +12,23 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/TextUtils.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 
+#include "jsfriendapi.h"
+
+#include "frontend/BytecodeCompiler.h"
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "gc/Nursery.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/StableStringChars.h"
 #include "js/UbiNode.h"
 #include "util/StringBuffer.h"
 #include "vm/GeckoProfiler.h"
 
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
@@ -2057,46 +2060,33 @@ js::EncodeLatin1(JSContext* cx, JSString
     if (!buf)
         return nullptr;
 
     mozilla::PodCopy(buf, linear->latin1Chars(nogc), len);
     buf[len] = '\0';
     return UniqueChars(reinterpret_cast<char*>(buf));
 }
 
-const char*
-js::ValueToPrintableLatin1(JSContext* cx, const Value& vArg, JSAutoByteString* bytes,
-                           bool asSource)
+UniqueChars
+js::IdToPrintableUTF8(JSContext* cx, HandleId id, IdToPrintableBehavior behavior)
 {
-    RootedValue v(cx, vArg);
+    // ToString(<symbol>) throws a TypeError, therefore require that callers
+    // request source representation when |id| is a property key.
+    MOZ_ASSERT_IF(behavior == IdToPrintableBehavior::IdIsIdentifier,
+                  JSID_IS_ATOM(id) && frontend::IsIdentifier(JSID_TO_ATOM(id)));
+
+    RootedValue v(cx, IdToValue(id));
     JSString* str;
-    if (asSource)
+    if (behavior == IdToPrintableBehavior::IdIsPropertyKey)
         str = ValueToSource(cx, v);
     else
         str = ToString<CanGC>(cx, v);
     if (!str)
         return nullptr;
-    str = QuoteString(cx, str, 0);
-    if (!str)
-        return nullptr;
-    return bytes->encodeLatin1(cx, str);
-}
-
-const char*
-js::ValueToPrintableUTF8(JSContext* cx, const Value& vArg, JSAutoByteString* bytes, bool asSource)
-{
-    RootedValue v(cx, vArg);
-    JSString* str;
-    if (asSource)
-        str = ValueToSource(cx, v);
-    else
-        str = ToString<CanGC>(cx, v);
-    if (!str)
-        return nullptr;
-    return bytes->encodeUtf8(cx, RootedString(cx, str));
+    return StringToNewUTF8CharsZ(cx, *str);
 }
 
 template <AllowGC allowGC>
 JSString*
 js::ToStringSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
 {
     /* As with ToObjectSlow, callers must verify that |arg| isn't a string. */
     MOZ_ASSERT(!arg.isString());
@@ -2167,18 +2157,18 @@ SymbolToSource(JSContext* cx, Symbol* sy
         MOZ_ASSERT(uint32_t(code) < JS::WellKnownSymbolLimit);
         return desc;
     }
 
     StringBuffer buf(cx);
     if (code == SymbolCode::InSymbolRegistry ? !buf.append("Symbol.for(") : !buf.append("Symbol("))
         return nullptr;
     if (desc) {
-        desc = StringToSource(cx, desc);
-        if (!desc || !buf.append(desc))
+        UniqueChars quoted = QuoteString(cx, desc, '"');
+        if (!quoted || !buf.append(quoted.get(), strlen(quoted.get())))
             return nullptr;
     }
     if (!buf.append(')'))
         return nullptr;
     return buf.finishString();
 }
 
 JSString*
@@ -2217,10 +2207,13 @@ js::ValueToSource(JSContext* cx, HandleV
     }
 
     return ObjectToSource(cx, obj);
 }
 
 JSString*
 js::StringToSource(JSContext* cx, JSString* str)
 {
-    return QuoteString(cx, str, '"');
+    UniqueChars chars = QuoteString(cx, str, '"');
+    if (!chars)
+        return nullptr;
+    return NewStringCopyZ<CanGC>(cx, chars.get());
 }
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -21,17 +21,16 @@
 #include "gc/Nursery.h"
 #include "gc/Rooting.h"
 #include "js/CharacterEncoding.h"
 #include "js/RootingAPI.h"
 #include "js/UniquePtr.h"
 #include "util/Text.h"
 #include "vm/Printer.h"
 
-class JSAutoByteString;
 class JSDependentString;
 class JSExtensibleString;
 class JSExternalString;
 class JSInlineString;
 class JSRope;
 
 namespace JS {
 
@@ -1673,33 +1672,33 @@ SubstringKernel(JSContext* cx, HandleStr
 /*** Conversions *********************************************************************************/
 
 /*
  * Convert a string to a printable C string.
  */
 UniqueChars
 EncodeLatin1(JSContext* cx, JSString* str);
 
-/*
- * Convert a value to a printable C string.
- *
- * As the function name implies, any characters in a converted printable string will be Latin1
- * characters. If there are any non-Latin1 characters in the original value, then those characters
- * will be changed to Unicode escape sequences(I.e. \udddd, dddd are 4 hex digits) in the printable
- * string.
- */
-extern const char*
-ValueToPrintableLatin1(JSContext* cx, const Value&, JSAutoByteString* bytes,
-                       bool asSource = false);
+enum class IdToPrintableBehavior : bool {
+    /*
+     * Request the printable representation of an identifier.
+     */
+    IdIsIdentifier,
+
+    /*
+     * Request the printable representation of a property key.
+     */
+    IdIsPropertyKey
+};
 
 /*
- * Convert a value to a printable C string encoded in UTF-8.
+ * Convert a jsid to a printable C string encoded in UTF-8.
  */
-extern const char*
-ValueToPrintableUTF8(JSContext* cx, const Value&, JSAutoByteString* bytes, bool asSource = false);
+extern UniqueChars
+IdToPrintableUTF8(JSContext* cx, HandleId id, IdToPrintableBehavior behavior);
 
 /*
  * Convert a non-string value to a string, returning null after reporting an
  * error, otherwise returning a new string reference.
  */
 template <AllowGC allowGC>
 extern JSString*
 ToStringSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg);
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -1319,18 +1319,18 @@ TypedArrayObjectTemplate<T>::fromObject(
     RootedObject arrayLike(cx);
     if (!callee.isNullOrUndefined()) {
         // Throw if other[Symbol.iterator] isn't callable.
         if (!callee.isObject() || !callee.toObject().isCallable()) {
             RootedValue otherVal(cx, ObjectValue(*other));
             UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, otherVal, nullptr);
             if (!bytes)
                 return nullptr;
-            JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
-                                       bytes.get());
+            JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
+                                     bytes.get());
             return nullptr;
         }
 
         FixedInvokeArgs<2> args2(cx);
         args2[0].setObject(*other);
         args2[1].set(callee);
 
         // Step 6.a.
--- a/js/src/vm/UbiNodeCensus.cpp
+++ b/js/src/vm/UbiNodeCensus.cpp
@@ -1,20 +1,21 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "js/UbiNodeCensus.h"
 
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/StableStringChars.h"
 #include "util/Text.h"
 #include "vm/JSContext.h"
+#include "vm/Printer.h"
 #include "vm/Realm.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 namespace JS {
@@ -1256,26 +1257,22 @@ ParseBreakdown(JSContext* cx, HandleValu
         CountTypePtr noFilenameType(ParseChildBreakdown(cx, breakdown, cx->names().noFilename));
         if (!noFilenameType)
             return nullptr;
 
         return CountTypePtr(cx->new_<ByFilename>(std::move(thenType), std::move(noFilenameType)));
     }
 
     // We didn't recognize the breakdown type; complain.
-    RootedString bySource(cx, ValueToSource(cx, byValue));
-    if (!bySource)
-        return nullptr;
-
-    JSAutoByteString byBytes(cx, bySource);
+    UniqueChars byBytes = QuoteString(cx, by, '"');
     if (!byBytes)
         return nullptr;
 
-    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_CENSUS_BREAKDOWN,
-                               byBytes.ptr());
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_CENSUS_BREAKDOWN,
+                              byBytes.get());
     return nullptr;
 }
 
 // Get the default census breakdown:
 //
 // { by: "coarseType",
 //   objects: { by: "objectClass" },
 //   other:   { by: "internalType" },
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -27,17 +27,16 @@
 #include <new>
 
 #include "jsmath.h"
 #include "jsutil.h"
 
 #include "builtin/String.h"
 #include "frontend/Parser.h"
 #include "gc/Policy.h"
-#include "js/AutoByteString.h"
 #include "js/MemoryMetrics.h"
 #include "js/Printf.h"
 #include "js/SourceBufferHolder.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/ErrorReporting.h"
@@ -2012,19 +2011,18 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
         failfVAOffset(pn->pn_pos.begin, fmt, ap);
         va_end(ap);
         return false;
     }
 
     bool failNameOffset(uint32_t offset, const char* fmt, PropertyName* name) {
         // This function is invoked without the caller properly rooting its locals.
         gc::AutoSuppressGC suppress(cx_);
-        JSAutoByteString bytes;
-        if (AtomToPrintableString(cx_, name, &bytes))
-            failfOffset(offset, fmt, bytes.ptr());
+        if (UniqueChars bytes = AtomToPrintableString(cx_, name))
+            failfOffset(offset, fmt, bytes.get());
         return false;
     }
 
     bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
         return failNameOffset(pn->pn_pos.begin, fmt, name);
     }
 
     bool failOverRecursed() {
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -12,17 +12,17 @@
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
 #include "jsapi.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/Printf.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsExceptionHandler.h"
 #include "nsIComponentManager.h"
 #include "mozilla/Module.h"
 #include "nsIFile.h"
@@ -101,32 +101,32 @@ Dump(JSContext* cx, unsigned argc, Value
 
     if (args.length() == 0)
         return true;
 
     RootedString str(cx, JS::ToString(cx, args[0]));
     if (!str)
         return false;
 
-    JSAutoByteString utf8str;
-    if (!utf8str.encodeUtf8(cx, str))
+    JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
+    if (!utf8str)
         return false;
 
 #ifdef ANDROID
-    __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.ptr());
+    __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get());
 #endif
 #ifdef XP_WIN
     if (IsDebuggerPresent()) {
         nsAutoJSString wstr;
         if (!wstr.init(cx, str))
             return false;
         OutputDebugStringW(wstr.get());
     }
 #endif
-    fputs(utf8str.ptr(), stdout);
+    fputs(utf8str.get(), stdout);
     fflush(stdout);
     return true;
 }
 
 static bool
 Debug(JSContext* cx, unsigned argc, Value* vp)
 {
 #ifdef DEBUG
@@ -1236,43 +1236,43 @@ mozJSComponentLoader::ExtractExports(JSC
             !value.isString() ||
             !JS_ValueToId(cx, value, &symbolId)) {
             return ReportOnCallerUTF8(cxhelper, ERROR_ARRAY_ELEMENT, aInfo, i);
         }
 
         symbolHolder = ResolveModuleObjectPropertyById(cx, aMod->obj, symbolId);
         if (!symbolHolder ||
             !JS_GetPropertyById(cx, symbolHolder, symbolId, &value)) {
-            JSAutoByteString bytes;
             RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
-            if (!bytes.encodeUtf8(cx, symbolStr))
+            JS::UniqueChars bytes = JS_EncodeStringToUTF8(cx, symbolStr);
+            if (!bytes)
                 return NS_ERROR_FAILURE;
             return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
-                                      aInfo, bytes.ptr());
+                                      aInfo, bytes.get());
         }
 
         if (value.isUndefined()) {
             missing = true;
         }
 
         if (!JS_SetPropertyById(cx, aExports, symbolId, value)) {
-            JSAutoByteString bytes;
             RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
-            if (!bytes.encodeUtf8(cx, symbolStr))
+            JS::UniqueChars bytes = JS_EncodeStringToUTF8(cx, symbolStr);
+            if (!bytes)
                 return NS_ERROR_FAILURE;
             return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
-                                      aInfo, bytes.ptr());
+                                      aInfo, bytes.get());
         }
 #ifdef DEBUG
         if (i == 0) {
             logBuffer.AssignLiteral("Installing symbols [ ");
         }
-        JSAutoByteString bytes(cx, JSID_TO_STRING(symbolId));
+        JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, JSID_TO_STRING(symbolId));
         if (!!bytes)
-            logBuffer.Append(bytes.ptr());
+            logBuffer.Append(bytes.get());
         logBuffer.Append(' ');
         if (i == symbolCount - 1) {
             nsCString location;
             MOZ_TRY(aInfo.GetLocation(location));
             LOG(("%s] from %s\n", logBuffer.get(), location.get()));
         }
 #endif
     }
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * The Components.Sandbox object.
  */
 
 #include "AccessCheck.h"
 #include "jsfriendapi.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/Proxy.h"
 #include "js/SourceBufferHolder.h"
 #include "js/StructuredClone.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsIException.h" // for nsIStackFrame
 #include "nsIScriptContext.h"
@@ -135,18 +135,18 @@ SandboxDump(JSContext* cx, unsigned argc
 
     if (args.length() == 0)
         return true;
 
     RootedString str(cx, ToString(cx, args[0]));
     if (!str)
         return false;
 
-    JSAutoByteString utf8str;
-    char* cstr = utf8str.encodeUtf8(cx, str);
+    JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
+    char* cstr = utf8str.get();
     if (!cstr)
         return false;
 
 #if defined(XP_MACOSX)
     // Be nice and convert all \r to \n.
     char* c = cstr;
     char* cEnd = cstr + strlen(cstr);
     while (c < cEnd) {
@@ -849,80 +849,84 @@ xpc::GlobalProperties::Parse(JSContext* 
     for (uint32_t i = 0; i < length; i++) {
         RootedValue nameValue(cx);
         ok = JS_GetElement(cx, obj, i, &nameValue);
         NS_ENSURE_TRUE(ok, false);
         if (!nameValue.isString()) {
             JS_ReportErrorASCII(cx, "Property names must be strings");
             return false;
         }
-        RootedString nameStr(cx, nameValue.toString());
-        JSAutoByteString name;
-        if (!name.encodeUtf8(cx, nameStr))
+        JSFlatString* nameStr = JS_FlattenString(cx, nameValue.toString());
+        if (!nameStr)
             return false;
-        if (!strcmp(name.ptr(), "Blob")) {
+        if (JS_FlatStringEqualsAscii(nameStr, "Blob")) {
             Blob = true;
-        } else if (!strcmp(name.ptr(), "ChromeUtils")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "ChromeUtils")) {
             ChromeUtils = true;
-        } else if (!strcmp(name.ptr(), "CSS")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "CSS")) {
             CSS = true;
-        } else if (!strcmp(name.ptr(), "CSSRule")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "CSSRule")) {
             CSSRule = true;
-        } else if (!strcmp(name.ptr(), "Directory")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "Directory")) {
             Directory = true;
-        } else if (!strcmp(name.ptr(), "DOMParser")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "DOMParser")) {
             DOMParser = true;
-        } else if (!strcmp(name.ptr(), "Element")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "Element")) {
             Element = true;
-        } else if (!strcmp(name.ptr(), "Event")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "Event")) {
             Event = true;
-        } else if (!strcmp(name.ptr(), "File")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "File")) {
             File = true;
-        } else if (!strcmp(name.ptr(), "FileReader")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "FileReader")) {
             FileReader = true;
-        } else if (!strcmp(name.ptr(), "FormData")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "FormData")) {
             FormData = true;
-        } else if (!strcmp(name.ptr(), "InspectorUtils")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "InspectorUtils")) {
             InspectorUtils = true;
-        } else if (!strcmp(name.ptr(), "MessageChannel")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "MessageChannel")) {
             MessageChannel = true;
-        } else if (!strcmp(name.ptr(), "Node")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "Node")) {
             Node = true;
-        } else if (!strcmp(name.ptr(), "NodeFilter")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "NodeFilter")) {
             NodeFilter = true;
-        } else if (!strcmp(name.ptr(), "TextDecoder")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "TextDecoder")) {
             TextDecoder = true;
-        } else if (!strcmp(name.ptr(), "TextEncoder")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "TextEncoder")) {
             TextEncoder = true;
-        } else if (!strcmp(name.ptr(), "URL")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "URL")) {
             URL = true;
-        } else if (!strcmp(name.ptr(), "URLSearchParams")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "URLSearchParams")) {
             URLSearchParams = true;
-        } else if (!strcmp(name.ptr(), "XMLHttpRequest")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "XMLHttpRequest")) {
             XMLHttpRequest = true;
-        } else if (!strcmp(name.ptr(), "XMLSerializer")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "XMLSerializer")) {
             XMLSerializer = true;
-        } else if (!strcmp(name.ptr(), "atob")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "atob")) {
             atob = true;
-        } else if (!strcmp(name.ptr(), "btoa")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "btoa")) {
             btoa = true;
-        } else if (!strcmp(name.ptr(), "caches")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "caches")) {
             caches = true;
-        } else if (!strcmp(name.ptr(), "crypto")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "crypto")) {
             crypto = true;
-        } else if (!strcmp(name.ptr(), "fetch")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "fetch")) {
             fetch = true;
-        } else if (!strcmp(name.ptr(), "indexedDB")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "indexedDB")) {
             indexedDB = true;
 #ifdef MOZ_WEBRTC
-        } else if (!strcmp(name.ptr(), "rtcIdentityProvider")) {
+        } else if (JS_FlatStringEqualsAscii(nameStr, "rtcIdentityProvider")) {
             rtcIdentityProvider = true;
 #endif
         } else {
-            JS_ReportErrorUTF8(cx, "Unknown property name: %s", name.ptr());
+            RootedString nameStr(cx, nameValue.toString());
+            JS::UniqueChars name = JS_EncodeStringToUTF8(cx, nameStr);
+            if (!name)
+                return false;
+
+            JS_ReportErrorUTF8(cx, "Unknown property name: %s", name.get());
             return false;
         }
     }
     return true;
 }
 
 bool
 xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj)
@@ -1535,20 +1539,19 @@ OptionsBase::ParseString(const char* nam
     if (!found)
         return true;
 
     if (!value.isString()) {
         JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
         return false;
     }
 
-    char* tmp = JS_EncodeString(mCx, value.toString());
+    JS::UniqueChars tmp = JS_EncodeStringToLatin1(mCx, value.toString());
     NS_ENSURE_TRUE(tmp, false);
-    prop.Assign(tmp, strlen(tmp));
-    js_free(tmp);
+    prop.Assign(tmp.get(), strlen(tmp.get()));
     return true;
 }
 
 /*
  * Helper that tries to get a string property from the options object.
  */
 bool
 OptionsBase::ParseString(const char* name, nsString& prop)
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -10,17 +10,17 @@
 #include "xpc_make_class.h"
 #include "XPCJSWeakReference.h"
 #include "WrapperFactory.h"
 #include "nsJSUtils.h"
 #include "mozJSComponentLoader.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollector.h"
 #include "jsfriendapi.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/SavedFrameAPI.h"
 #include "js/StructuredClone.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/Preferences.h"
 #include "nsJSEnvironment.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ResultExtensions.h"
@@ -241,22 +241,22 @@ nsXPCComponents_Interfaces::Resolve(nsIX
                                     bool* _retval)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
 
     if (!JSID_IS_STRING(id))
         return NS_OK;
 
-    JSAutoByteString name;
     RootedString str(cx, JSID_TO_STRING(id));
+    JS::UniqueChars name = JS_EncodeStringToLatin1(cx, str);
 
     // we only allow interfaces by name here
-    if (name.encodeLatin1(cx, str) && name.ptr()[0] != '{') {
-        const nsXPTInterfaceInfo* info = nsXPTInterfaceInfo::ByName(name.ptr());
+    if (name && name[0] != '{') {
+        const nsXPTInterfaceInfo* info = nsXPTInterfaceInfo::ByName(name.get());
         if (!info)
             return NS_OK;
 
         nsCOMPtr<nsIJSIID> nsid = nsJSIID::NewID(info);
 
         if (nsid) {
             nsXPConnect* xpc = nsXPConnect::XPConnect();
             RootedObject idobj(cx);
@@ -426,20 +426,20 @@ nsXPCComponents_InterfacesByID::Resolve(
 
     if (!JSID_IS_STRING(id))
         return NS_OK;
 
     RootedString str(cx, JSID_TO_STRING(id));
     if (38 != JS_GetStringLength(str))
         return NS_OK;
 
-    JSAutoByteString utf8str;
-    if (utf8str.encodeUtf8(cx, str)) {
+    JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
+    if (utf8str) {
         nsID iid;
-        if (!iid.Parse(utf8str.ptr()))
+        if (!iid.Parse(utf8str.get()))
             return NS_OK;
 
         const nsXPTInterfaceInfo* info = nsXPTInterfaceInfo::ByIID(iid);
         if (!info)
             return NS_OK;
 
         nsCOMPtr<nsIJSIID> nsid = nsJSIID::NewID(info);
 
@@ -618,21 +618,23 @@ nsXPCComponents_Classes::Resolve(nsIXPCo
                                  JSContext* cx, JSObject* objArg,
                                  jsid idArg, bool* resolvedp,
                                  bool* _retval)
 
 {
     RootedId id(cx, idArg);
     RootedObject obj(cx, objArg);
 
-    JSAutoByteString name;
-    if (JSID_IS_STRING(id) &&
-        name.encodeLatin1(cx, JSID_TO_STRING(id)) &&
-        name.ptr()[0] != '{') { // we only allow contractids here
-        nsCOMPtr<nsIJSCID> nsid = nsJSCID::NewID(name.ptr());
+    if (!JSID_IS_STRING(id))
+        return NS_OK;
+
+    JS::UniqueChars name = JS_EncodeStringToLatin1(cx, JSID_TO_STRING(id));
+    if (name &&
+        name[0] != '{') { // we only allow contractids here
+        nsCOMPtr<nsIJSCID> nsid = nsJSCID::NewID(name.get());
         if (nsid) {
             nsXPConnect* xpc = nsXPConnect::XPConnect();
             RootedObject idobj(cx);
             if (NS_SUCCEEDED(xpc->WrapNative(cx, obj,
                                              static_cast<nsIJSCID*>(nsid),
                                              NS_GET_IID(nsIJSCID),
                                              idobj.address()))) {
                 if (idobj) {
@@ -827,22 +829,22 @@ nsXPCComponents_ClassesByID::Resolve(nsI
                                      bool* _retval)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
 
     if (!JSID_IS_STRING(id))
         return NS_OK;
 
-    JSAutoByteString name;
     RootedString str(cx, JSID_TO_STRING(id));
-    if (name.encodeLatin1(cx, str) && name.ptr()[0] == '{' &&
-        IsRegisteredCLSID(name.ptr())) // we only allow canonical CLSIDs here
+    JS::UniqueChars name = JS_EncodeStringToLatin1(cx, str);
+    if (name && name[0] == '{' &&
+        IsRegisteredCLSID(name.get())) // we only allow canonical CLSIDs here
     {
-        nsCOMPtr<nsIJSCID> nsid = nsJSCID::NewID(name.ptr());
+        nsCOMPtr<nsIJSCID> nsid = nsJSCID::NewID(name.get());
         if (nsid) {
             nsXPConnect* xpc = nsXPConnect::XPConnect();
             RootedObject idobj(cx);
             if (NS_SUCCEEDED(xpc->WrapNative(cx, obj,
                                              static_cast<nsIJSCID*>(nsid),
                                              NS_GET_IID(nsIJSCID),
                                              idobj.address()))) {
                 if (idobj) {
@@ -993,24 +995,26 @@ nsXPCComponents_Results::NewEnumerate(ns
 NS_IMETHODIMP
 nsXPCComponents_Results::Resolve(nsIXPConnectWrappedNative* wrapper,
                                  JSContext* cx, JSObject* objArg,
                                  jsid idArg, bool* resolvedp,
                                  bool* _retval)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
-    JSAutoByteString name;
-
-    if (JSID_IS_STRING(id) && name.encodeLatin1(cx, JSID_TO_STRING(id))) {
+    if (!JSID_IS_STRING(id))
+        return NS_OK;
+
+    JS::UniqueChars name = JS_EncodeStringToLatin1(cx, JSID_TO_STRING(id));
+    if (name) {
         const char* rv_name;
         const void* iter = nullptr;
         nsresult rv;
         while (nsXPCException::IterateNSResults(&rv, &rv_name, nullptr, &iter)) {
-            if (!strcmp(name.ptr(), rv_name)) {
+            if (!strcmp(name.get(), rv_name)) {
                 *resolvedp = true;
                 if (!JS_DefinePropertyById(cx, obj, id, (uint32_t)rv,
                                            JSPROP_ENUMERATE |
                                            JSPROP_READONLY |
                                            JSPROP_PERMANENT |
                                            JSPROP_RESOLVING)) {
                     return NS_ERROR_UNEXPECTED;
                 }
@@ -1156,25 +1160,27 @@ nsXPCComponents_ID::CallOrConstruct(nsIX
     if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateInstance(cx, nsJSID::GetCID()))) {
         // the security manager vetoed. It should have set an exception.
         *_retval = false;
         return NS_OK;
     }
 
     // convert the first argument into a string and see if it looks like an id
 
-    JSString* jsstr;
-    JSAutoByteString bytes;
+    JSString* jsstr = ToString(cx, args[0]);
+    if (!jsstr)
+        return ThrowAndFail(NS_ERROR_XPC_BAD_ID_STRING, cx, _retval);
+
+    JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, jsstr);
+    if (!bytes)
+        return ThrowAndFail(NS_ERROR_XPC_BAD_ID_STRING, cx, _retval);
+
     nsID id;
-
-    if (!(jsstr = ToString(cx, args[0])) ||
-        !bytes.encodeLatin1(cx, jsstr) ||
-        !id.Parse(bytes.ptr())) {
+    if (!id.Parse(bytes.get()))
         return ThrowAndFail(NS_ERROR_XPC_BAD_ID_STRING, cx, _retval);
-    }
 
     // make the new object and return it.
 
     JSObject* newobj = xpc_NewIDObject(cx, obj, id);
     if (!newobj)
         return NS_ERROR_UNEXPECTED;
 
     args.rval().setObject(*newobj);
@@ -1377,17 +1383,18 @@ struct MOZ_STACK_CLASS ExceptionArgParse
     /*
      * Parsing helpers.
      */
 
     bool parseMessage(HandleValue v) {
         JSString* str = ToString(cx, v);
         if (!str)
            return false;
-        eMsg = messageBytes.encodeLatin1(cx, str);
+        messageBytes = JS_EncodeStringToLatin1(cx, str);
+        eMsg = messageBytes.get();
         return !!eMsg;
     }
 
     bool parseResult(HandleValue v) {
         return JS::ToUint32(cx, v, (uint32_t*) &eResult);
     }
 
     bool parseStack(HandleValue v) {
@@ -1448,17 +1455,17 @@ struct MOZ_STACK_CLASS ExceptionArgParse
         return JS_GetProperty(cx, obj, name, rv);
     }
 
     /*
      * Internal data members.
      */
 
     // If there's a non-default exception string, hold onto the allocated bytes.
-    JSAutoByteString messageBytes;
+    JS::UniqueChars messageBytes;
 
     // Various bits and pieces that are helpful to have around.
     JSContext* cx;
     nsXPConnect* xpc;
 };
 
 // static
 nsresult
@@ -1866,22 +1873,27 @@ nsXPCComponents_Constructor::CallOrConst
         *_retval = false;
         return NS_OK;
     }
 
     // initialization params for the Constructor object we will create
     nsCOMPtr<nsIJSCID> cClassID;
     nsCOMPtr<nsIJSIID> cInterfaceID;
     const char*        cInitializer = nullptr;
-    JSAutoByteString  cInitializerBytes;
+    JS::UniqueChars cInitializerBytes;
 
     if (args.length() >= 3) {
         // args[2] is an initializer function or property name
         RootedString str(cx, ToString(cx, args[2]));
-        if (!str || !(cInitializer = cInitializerBytes.encodeLatin1(cx, str)))
+        if (!str)
+            return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval);
+
+        cInitializerBytes = JS_EncodeStringToLatin1(cx, str);
+        cInitializer = cInitializerBytes.get();
+        if (!cInitializer)
             return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval);
     }
 
     if (args.length() >= 2) {
         // args[1] is an iid name string
         // XXXjband support passing "Components.interfaces.foo"?
 
         nsCOMPtr<nsIXPCComponents_Interfaces> ifaces;
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -16,17 +16,16 @@
 #include "nsQueryObject.h"
 #include "nsScriptError.h"
 #include "WrapperFactory.h"
 
 #include "nsWrapperCacheInlines.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
-#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/PrimitiveConversions.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 
@@ -1328,41 +1327,41 @@ XPCConvert::JSValToXPCException(MutableH
                                       exceptn, nullptr, nullptr);
         } else {
             // It is a JSObject, but not a wrapped native...
 
             // If it is an engine Error with an error report then let's
             // extract the report and build an xpcexception from that
             const JSErrorReport* report;
             if (nullptr != (report = JS_ErrorFromException(cx, obj))) {
-                JSAutoByteString toStringResult;
+                JS::UniqueChars toStringResult;
                 RootedString str(cx, ToString(cx, s));
                 if (str)
-                    toStringResult.encodeUtf8(cx, str);
-                return JSErrorToXPCException(toStringResult.ptr(), ifaceName,
+                    toStringResult = JS_EncodeStringToUTF8(cx, str);
+                return JSErrorToXPCException(toStringResult.get(), ifaceName,
                                              methodName, report, exceptn);
             }
 
             // XXX we should do a check against 'js_ErrorClass' here and
             // do the right thing - even though it has no JSErrorReport,
             // The fact that it is a JSError exceptions means we can extract
             // particular info and our 'result' should reflect that.
 
             // otherwise we'll just try to convert it to a string
 
             JSString* str = ToString(cx, s);
             if (!str)
                 return NS_ERROR_FAILURE;
 
-            JSAutoByteString strBytes(cx, str);
+            JS::UniqueChars strBytes = JS_EncodeStringToLatin1(cx, str);
             if (!strBytes)
                 return NS_ERROR_FAILURE;
 
             return ConstructException(NS_ERROR_XPC_JS_THREW_JS_OBJECT,
-                                      strBytes.ptr(), ifaceName, methodName,
+                                      strBytes.get(), ifaceName, methodName,
                                       nullptr, exceptn, cx, s.address());
         }
     }
 
     if (s.isUndefined() || s.isNull()) {
         return ConstructException(NS_ERROR_XPC_JS_THREW_NULL,
                                   nullptr, ifaceName, methodName, nullptr,
                                   exceptn, cx, s.address());
@@ -1415,20 +1414,19 @@ XPCConvert::JSValToXPCException(MutableH
         }
     }
 
     // otherwise we'll just try to convert it to a string
     // Note: e.g., bools get converted to JSStrings by this code.
 
     JSString* str = ToString(cx, s);
     if (str) {
-        JSAutoByteString strBytes(cx, str);
-        if (!!strBytes) {
+        if (JS::UniqueChars strBytes = JS_EncodeStringToLatin1(cx, str)) {
             return ConstructException(NS_ERROR_XPC_JS_THREW_STRING,
-                                      strBytes.ptr(), ifaceName, methodName,
+                                      strBytes.get(), ifaceName, methodName,
                                       nullptr, exceptn, cx, s.address());
         }
     }
     return NS_ERROR_FAILURE;
 }
 
 /***************************************************************************/
 
--- a/js/xpconnect/src/XPCDebug.cpp
+++ b/js/xpconnect/src/XPCDebug.cpp
@@ -48,15 +48,15 @@ xpc_DumpJSStack(bool showArgs, bool show
 }
 
 JS::UniqueChars
 xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals,
                  bool showThisProps)
 {
     JS::AutoSaveExceptionState state(cx);
 
-    JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, showArgs, showLocals, showThisProps);
+    JS::UniqueChars buf = JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
     if (!buf)
         DebugDump("%s", "Failed to format JavaScript stack for dump\n");
 
     state.restore();
     return buf;
 }
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsXULAppAPI.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/Printf.h"
 #include "mozilla/ChaosMode.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "nsServiceManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsExceptionHandler.h"
@@ -245,18 +245,18 @@ ReadLine(JSContext* cx, unsigned argc, V
         str = JS::ToString(cx, args[0]);
         if (!str)
             return false;
     } else {
         str = JS_GetEmptyString(cx);
     }
 
     /* Get a line from the infile */
-    JSAutoByteString strBytes(cx, str);
-    if (!strBytes || !GetLine(cx, buf, gInFile, strBytes.ptr()))
+    JS::UniqueChars strBytes = JS_EncodeStringToLatin1(cx, str);
+    if (!strBytes || !GetLine(cx, buf, gInFile, strBytes.get()))
         return false;
 
     /* Strip newline character added by GetLine() */
     unsigned int buflen = strlen(buf);
     if (buflen == 0) {
         if (feof(gInFile)) {
             args.rval().setNull();
             return true;
@@ -283,23 +283,23 @@ Print(JSContext* cx, unsigned argc, Valu
     RootedString str(cx);
     nsAutoCString utf8output;
 
     for (unsigned i = 0; i < args.length(); i++) {
         str = ToString(cx, args[i]);
         if (!str)
             return false;
 
-        JSAutoByteString utf8str;
-        if (!utf8str.encodeUtf8(cx, str))
+        JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
+        if (!utf8str)
             return false;
 
         if (i)
             utf8output.Append(' ');
-        utf8output.Append(utf8str.ptr(), utf8str.length());
+        utf8output.Append(utf8str.get(), strlen(utf8str.get()));
     }
     utf8output.Append('\n');
     fputs(utf8output.get(), gOutFile);
     fflush(gOutFile);
     return true;
 }
 
 static bool
@@ -310,32 +310,32 @@ Dump(JSContext* cx, unsigned argc, Value
 
     if (!args.length())
          return true;
 
     RootedString str(cx, ToString(cx, args[0]));
     if (!str)
         return false;
 
-    JSAutoByteString utf8str;
-    if (!utf8str.encodeUtf8(cx, str))
+    JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
+    if (!utf8str)
         return false;
 
 #ifdef ANDROID
-    __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.ptr());
+    __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get());
 #endif
 #ifdef XP_WIN
     if (IsDebuggerPresent()) {
         nsAutoJSString wstr;
         if (!wstr.init(cx, str))
             return false;
         OutputDebugStringW(wstr.get());
     }
 #endif
-    fputs(utf8str.ptr(), gOutFile);
+    fputs(utf8str.get(), gOutFile);
     fflush(gOutFile);
     return true;
 }
 
 static bool
 Load(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -348,31 +348,31 @@ Load(JSContext* cx, unsigned argc, Value
         return false;
     }
 
     RootedString str(cx);
     for (unsigned i = 0; i < args.length(); i++) {
         str = ToString(cx, args[i]);
         if (!str)
             return false;
-        JSAutoByteString filename(cx, str);
+        JS::UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
         if (!filename)
             return false;
-        FILE* file = fopen(filename.ptr(), "r");
+        FILE* file = fopen(filename.get(), "r");
         if (!file) {
-            filename.clear();
-            if (!filename.encodeUtf8(cx, str))
+            filename = JS_EncodeStringToUTF8(cx, str);
+            if (!filename)
                 return false;
             JS_ReportErrorUTF8(cx, "cannot open file '%s' for reading",
-                               filename.ptr());
+                               filename.get());
             return false;
         }
         JS::CompileOptions options(cx);
         options.setUTF8(true)
-               .setFileAndLine(filename.ptr(), 1)
+               .setFileAndLine(filename.get(), 1)
                .setIsRunOnce(true);
         JS::Rooted<JSScript*> script(cx);
         JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
         JS::Compile(cx, options, file, &script);
         fclose(file);
         if (!script)
             return false;
 
@@ -474,35 +474,35 @@ SendCommand(JSContext* cx, unsigned argc
 
 static bool
 Options(JSContext* cx, unsigned argc, Value* vp)
 {
     JS::CallArgs args = CallArgsFromVp(argc, vp);
     ContextOptions oldContextOptions = ContextOptionsRef(cx);
 
     RootedString str(cx);
-    JSAutoByteString opt;
+    JS::UniqueChars opt;
     for (unsigned i = 0; i < args.length(); ++i) {
         str = ToString(cx, args[i]);
         if (!str)
             return false;
 
-        opt.clear();
-        if (!opt.encodeUtf8(cx, str))
+        opt = JS_EncodeStringToUTF8(cx, str);
+        if (!opt)
             return false;
 
-        if (strcmp(opt.ptr(), "strict") == 0)
+        if (strcmp(opt.get(), "strict") == 0)
             ContextOptionsRef(cx).toggleExtraWarnings();
-        else if (strcmp(opt.ptr(), "werror") == 0)
+        else if (strcmp(opt.get(), "werror") == 0)
             ContextOptionsRef(cx).toggleWerror();
-        else if (strcmp(opt.ptr(), "strict_mode") == 0)
+        else if (strcmp(opt.get(), "strict_mode") == 0)
             ContextOptionsRef(cx).toggleStrictMode();
         else {
             JS_ReportErrorUTF8(cx, "unknown option name '%s'. The valid names are "
-                               "strict, werror, and strict_mode.", opt.ptr());
+                               "strict, werror, and strict_mode.", opt.get());
             return false;
         }
     }
 
     UniqueChars names;
     if (oldContextOptions.extraWarnings()) {
         names = JS_sprintf_append(std::move(names), "%s", "strict");
         if (!names) {
@@ -721,21 +721,21 @@ ProcessLine(AutoJSAPI& jsapi, const char
     if (!JS_ExecuteScript(cx, script, &result))
         return false;
 
     if (result.isUndefined())
         return true;
     RootedString str(cx);
     if (!(str = ToString(cx, result)))
         return false;
-    JSAutoByteString bytes;
-    if (!bytes.encodeLatin1(cx, str))
+    JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, str);
+    if (!bytes)
         return false;
 
-    fprintf(gOutFile, "%s\n", bytes.ptr());
+    fprintf(gOutFile, "%s\n", bytes.get());
     return true;
 }
 
 static bool
 ProcessFile(AutoJSAPI& jsapi, const char* filename, FILE* file, bool forceTTY)
 {
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
--- a/js/xpconnect/src/XPCThrower.cpp
+++ b/js/xpconnect/src/XPCThrower.cpp
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Code for throwing errors into JavaScript. */
 
 #include "xpcprivate.h"
 #include "XPCWrapper.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/Printf.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/Exceptions.h"
 #include "nsString.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -159,20 +159,23 @@ void
 XPCThrower::Verbosify(XPCCallContext& ccx,
                       char** psz, bool own)
 {
     char* sz = nullptr;
 
     if (ccx.HasInterfaceAndMember()) {
         XPCNativeInterface* iface = ccx.GetInterface();
         jsid id = ccx.GetMember()->GetName();
-        JSAutoByteString bytes;
-        const char* name = JSID_IS_VOID(id) ? "Unknown" : bytes.encodeLatin1(ccx, JSID_TO_STRING(id));
-        if (!name) {
-            name = "";
+        const char* name;
+        JS::UniqueChars bytes;
+        if (!JSID_IS_VOID(id)) {
+            bytes = JS_EncodeStringToLatin1(ccx, JSID_TO_STRING(id));
+            name = bytes ? bytes.get() : "";
+        } else {
+            name = "Unknown";
         }
         sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name).release();
     }
 
     if (sz) {
         if (own)
             js_free(*psz);
         *psz = sz;
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* JavaScript JSClasses and JSOps for our Wrapped Native JS Objects. */
 
 #include "xpcprivate.h"
 #include "xpc_make_class.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Preferences.h"
-#include "js/AutoByteString.h"
+#include "js/CharacterEncoding.h"
 #include "js/Class.h"
 #include "js/Printf.h"
 
 using namespace mozilla;
 using namespace JS;
 
 /***************************************************************************/
 
@@ -284,30 +284,47 @@ DefinePropertyIfFound(XPCCallContext& cc
             }
         }
         // This *might* be a tearoff name that is not yet part of our
         // set. Let's lookup the name and see if it is the name of an
         // interface. Then we'll see if the object actually *does* this
         // interface and add a tearoff as necessary.
 
         if (wrapperToReflectInterfaceNames) {
-            JSAutoByteString name;
+            JS::UniqueChars name;
             RefPtr<XPCNativeInterface> iface2;
             XPCWrappedNativeTearOff* to;
             RootedObject jso(ccx);
             nsresult rv = NS_OK;
 
-            if (JSID_IS_STRING(id) &&
-                name.encodeLatin1(ccx, JSID_TO_STRING(id)) &&
-                (iface2 = XPCNativeInterface::GetNewOrUsed(name.ptr())) &&
-                nullptr != (to = wrapperToReflectInterfaceNames->
-                           FindTearOff(iface2, true, &rv)) &&
-                nullptr != (jso = to->GetJSObject()))
+            bool defineProperty = false;
+            do {
+                if (!JSID_IS_STRING(id))
+                    break;
+
+                name = JS_EncodeStringToLatin1(ccx, JSID_TO_STRING(id));
+                if (!name)
+                    break;
+
+                iface2 = XPCNativeInterface::GetNewOrUsed(name.get());
+                if (!iface2)
+                    break;
 
-            {
+                to = wrapperToReflectInterfaceNames->FindTearOff(iface2, true, &rv);
+                if (!to)
+                    break;
+
+                jso = to->GetJSObject();
+                if (!jso)
+                    break;
+
+                defineProperty = true;
+            } while (false);
+
+            if (defineProperty) {
                 AutoResolveName arn(ccx, id);
                 if (resolved)
                     *resolved = true;
                 return JS_DefinePropertyById(ccx, obj, id, jso,
                                              propFlags & ~JSPROP_ENUMERATE);
             } else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) {
                 return Throw(rv, ccx);
             }
--- a/layout/forms/nsFieldSetFrame.cpp
+++ b/layout/forms/nsFieldSetFrame.cpp
@@ -403,17 +403,17 @@ nsFieldSetFrame::Reflow(nsPresContext*  
   // We don't allow fieldsets to break vertically. If we did, we'd
   // need logic here to push and pull overflow frames.
   // Since we're not applying our padding in this frame, we need to add it here
   // to compute the available width for our children.
   WritingMode wm = GetWritingMode();
   WritingMode innerWM = inner ? inner->GetWritingMode() : wm;
   WritingMode legendWM = legend ? legend->GetWritingMode() : wm;
   LogicalSize innerAvailSize = aReflowInput.ComputedSizeWithPadding(innerWM);
-  LogicalSize legendAvailSize = aReflowInput.ComputedSizeWithPadding(legendWM);
+  LogicalSize legendAvailSize = aReflowInput.ComputedSize(legendWM);
   innerAvailSize.BSize(innerWM) = legendAvailSize.BSize(legendWM) =
     NS_UNCONSTRAINEDSIZE;
 
   // get our border and padding
   LogicalMargin border = aReflowInput.ComputedLogicalBorderPadding() -
                          aReflowInput.ComputedLogicalPadding();
 
   // Figure out how big the legend is if there is one.
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -849,16 +849,18 @@ public:
   };
 
 #define DISPLAY_REFLOW(dr_pres_context, dr_frame, dr_rf_state, dr_rf_metrics, dr_rf_status) \
   DR_cookie dr_cookie(dr_pres_context, dr_frame, dr_rf_state, dr_rf_metrics, dr_rf_status);
 #define DISPLAY_REFLOW_CHANGE() \
   dr_cookie.Change();
 #define DISPLAY_LAYOUT(dr_frame) \
   DR_layout_cookie dr_cookie(dr_frame);
+// FIXME DISPLAY_*_WIDTH should go through a renaming refactoring to reflect the
+// fact that it's displaying a minimum inline size, not a minimum width.
 #define DISPLAY_MIN_WIDTH(dr_frame, dr_result) \
   DR_intrinsic_width_cookie dr_cookie(dr_frame, "Min", dr_result)
 #define DISPLAY_PREF_WIDTH(dr_frame, dr_result) \
   DR_intrinsic_width_cookie dr_cookie(dr_frame, "Pref", dr_result)
 #define DISPLAY_PREF_SIZE(dr_frame, dr_result) \
   DR_intrinsic_size_cookie dr_cookie(dr_frame, "Pref", dr_result)
 #define DISPLAY_MIN_SIZE(dr_frame, dr_result) \
   DR_intrinsic_size_cookie dr_cookie(dr_frame, "Min", dr_result)
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1029,36 +1029,38 @@ nsImageFrame::GetContinuationOffset() co
   }
   NS_ASSERTION(offset >= 0, "bogus GetContentRect");
   return offset;
 }
 
 /* virtual */ nscoord
 nsImageFrame::GetMinISize(gfxContext *aRenderingContext)
 {
-  // XXX The caller doesn't account for constraints of the height,
-  // min-height, and max-height properties.
+  // XXX The caller doesn't account for constraints of the block-size,
+  // min-block-size, and max-block-size properties.
   DebugOnly<nscoord> result;
   DISPLAY_MIN_WIDTH(this, result);
   EnsureIntrinsicSizeAndRatio();
-  return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
-    mIntrinsicSize.width.GetCoordValue() : 0;
+  const nsStyleCoord& iSize = GetWritingMode().IsVertical() ?
+                                mIntrinsicSize.height : mIntrinsicSize.width;
+  return iSize.GetUnit() == eStyleUnit_Coord ? iSize.GetCoordValue() : 0;
 }
 
 /* virtual */ nscoord
 nsImageFrame::GetPrefISize(gfxContext *aRenderingContext)
 {
-  // XXX The caller doesn't account for constraints of the height,
-  // min-height, and max-height properties.
+  // XXX The caller doesn't account for constraints of the block-size,
+  // min-block-size, and max-block-size properties.
   DebugOnly<nscoord> result;
   DISPLAY_PREF_WIDTH(this, result);
   EnsureIntrinsicSizeAndRatio();
+  const nsStyleCoord& iSize = GetWritingMode().IsVertical() ?
+                                mIntrinsicSize.height : mIntrinsicSize.width;
   // convert from normal twips to scaled twips (printing...)
-  return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
-    mIntrinsicSize.width.GetCoordValue() : 0;
+  return iSize.GetUnit() == eStyleUnit_Coord ? iSize.GetCoordValue() : 0;
 }
 
 /* virtual */ IntrinsicSize
 nsImageFrame::GetIntrinsicSize()
 {
   return mIntrinsicSize;
 }
 
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -31,18 +31,18 @@ android {
 
     aaptOptions {
         // The omnijar is already a compressed file itself and Gecko expects it to be
         // STORED within the APK rather than DEFLATED.
         noCompress 'ja'
     }
 
     compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
     }
 
     dexOptions {
         javaMaxHeapSize "2g"
         jumboMode = true
     }
 
     lintOptions {
@@ -66,16 +66,17 @@ android {
         // unfortunate side effect of Proguarding the instrumentation (Robocop) APK, but nothing
         // uses runtime inspection or class-loading with that APK, so it shouldn't be a problem.
         def configureMinifyClosure = {
             // Bug 1229269: we can't yet shrinkResources effectively.  Be sure
             // to use -stripped.ap_ after enabling this.
             // shrinkResources true
             minifyEnabled true
             proguardFile "${topsrcdir}/mobile/android/config/proguard/proguard.cfg"
+            testProguardFile "${topsrcdir}/mobile/android/config/proguard/proguard-robocop.cfg"
         }
         release configureMinifyClosure
         if (mozconfig.substs.MOZILLA_OFFICIAL) {
             debug configureMinifyClosure
         }
 
         def isDebuggable = (!mozconfig.substs.MOZILLA_OFFICIAL) || (mozconfig.substs.NIGHTLY_BUILD && mozconfig.substs.MOZ_DEBUG)
         debug {
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -29,17 +29,16 @@ import android.graphics.drawable.Drawabl
 import android.net.Uri;
 import android.nfc.NdefMessage;
 import android.nfc.NdefRecord;
 import android.nfc.NfcAdapter;
 import android.nfc.NfcEvent;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.StrictMode;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
 import android.support.design.widget.Snackbar;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.content.res.ResourcesCompat;
 import android.support.v4.view.MenuItemCompat;
@@ -154,16 +153,17 @@ import org.mozilla.gecko.util.DrawableUt
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GamepadUtils;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.IntentUtils;
 import org.mozilla.gecko.util.MenuUtils;
 import org.mozilla.gecko.util.PrefUtils;
 import org.mozilla.gecko.util.ShortcutUtils;
+import org.mozilla.gecko.util.StrictModeContext;
 import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.WindowUtil;
 import org.mozilla.gecko.widget.ActionModePresenter;
 import org.mozilla.gecko.widget.AnchoredPopup;
 import org.mozilla.gecko.widget.AnimatedProgressBar;
 import org.mozilla.gecko.widget.GeckoActionProvider;
 import org.mozilla.gecko.widget.SplashScreen;
@@ -3706,16 +3706,17 @@ public class BrowserApp extends GeckoApp
         }
         return super.onKeyLongPress(keyCode, event);
     }
 
     /*
      * If the app has been launched a certain number of times, and we haven't asked for feedback before,
      * open a new tab with about:feedback when launching the app from the icon shortcut.
      */
+    @SuppressWarnings("try")
     @Override
     protected void onNewIntent(Intent externalIntent) {
 
         // Currently there is no way to exit PictureInPicture mode programmatically
         // https://issuetracker.google.com/issues/37254459
         // but because we are "singleTask" we will receive the Intents to open a new link.
         // When this happens, the new Intent will trigger `onPictureInPictureModeChanged(..)`
         //
@@ -3800,34 +3801,31 @@ public class BrowserApp extends GeckoApp
         }
 
         if (!mInitialized || !Intent.ACTION_MAIN.equals(action)) {
             return;
         }
 
         // Check to see how many times the app has been launched.
         final String keyName = getPackageName() + ".feedback_launch_count";
-        final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
 
         // Faster on main thread with an async apply().
-        try {
+        try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
             SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
             int launchCount = settings.getInt(keyName, 0);
             if (launchCount < FEEDBACK_LAUNCH_COUNT) {
                 // Increment the launch count and store the new value.