Bug 1402247 - Use encoding_rs for XPCOM string encoding conversions. draft
authorHenri Sivonen <hsivonen@hsivonen.fi>
Mon, 26 Mar 2018 15:44:50 +0300
changeset 773653 8e864a8f82a690c5a09cf9371deb8b7d7ecf7c09
parent 773652 a4da4d10be08a745347d7a08ddf43d469883a122
push id104266
push userbmo:hsivonen@hsivonen.fi
push dateWed, 28 Mar 2018 07:33:03 +0000
bugs1402247
milestone61.0a1
Bug 1402247 - Use encoding_rs for XPCOM string encoding conversions. MozReview-Commit-ID: JaJuExfILM9
Cargo.lock
docshell/base/nsDocShell.cpp
dom/base/DOMException.cpp
dom/base/nsTextFragment.cpp
dom/base/nsXMLContentSerializer.cpp
dom/events/Event.cpp
dom/offline/nsDOMOfflineResourceList.cpp
dom/plugins/base/nsJSNPRuntime.cpp
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/presentation/PresentationRequest.cpp
dom/webbrowserpersist/nsWebBrowserPersist.cpp
dom/xslt/xslt/txEXSLTFunctions.cpp
dom/xslt/xslt/txXSLTNumberCounters.cpp
editor/libeditor/CSSEditUtils.cpp
gfx/thebes/gfxFcPlatformFontList.cpp
intl/encoding_glue/src/lib.rs
intl/lwbrk/nsPangoBreaker.cpp
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCConvert.cpp
js/xpconnect/src/nsXPConnect.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/nsHttpBasicAuth.cpp
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/streamconv/converters/nsTXTToHTMLConv.cpp
parser/html/nsHtml5String.cpp
parser/htmlparser/nsExpatDriver.cpp
rdf/base/nsRDFContentSink.cpp
security/manager/ssl/nsNSSCertHelper.cpp
security/manager/ssl/nsNSSCertificate.cpp
servo/components/style/properties/gecko.mako.rs
servo/ports/geckolib/glue.rs
servo/support/gecko/nsstring/Cargo.toml
servo/support/gecko/nsstring/src/conversions.rs
servo/support/gecko/nsstring/src/lib.rs
toolkit/components/windowwatcher/nsWindowWatcher.cpp
toolkit/xre/nsAppRunner.cpp
uriloader/exthandler/nsExternalHelperAppService.cpp
widget/gtk/IMContextWrapper.cpp
widget/gtk/nsDeviceContextSpecG.cpp
widget/nsBaseWidget.cpp
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/base/nsAlgorithm.h
xpcom/ds/nsAtomTable.cpp
xpcom/ds/nsVariant.cpp
xpcom/glue/FileUtils.cpp
xpcom/string/moz.build
xpcom/string/nsReadableUtils.cpp
xpcom/string/nsReadableUtils.h
xpcom/string/nsReadableUtilsImpl.h
xpcom/string/nsReadableUtilsSSE2.cpp
xpcom/string/nsString.h
xpcom/string/nsSubstring.cpp
xpcom/string/nsTSubstring.h
xpcom/string/nsUTF8Utils.h
xpcom/string/nsUTF8UtilsNEON.cpp
xpcom/string/nsUTF8UtilsSSE2.cpp
xpcom/tests/gtest/TestAtoms.cpp
xpcom/tests/gtest/TestCRT.cpp
xpcom/tests/gtest/TestStrings.cpp
xpcom/tests/gtest/TestTextFormatter.cpp
xpcom/tests/gtest/TestUTF.cpp
xpcom/tests/gtest/UTFStrings.h
xpfe/components/directory/nsDirectoryViewer.cpp
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1172,16 +1172,17 @@ dependencies = [
  "nsstring 0.1.0",
 ]
 
 [[package]]
 name = "nsstring"
 version = "0.1.0"
 dependencies = [
  "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "nsstring-gtest"
 version = "0.1.0"
 dependencies = [
  "nsstring 0.1.0",
 ]
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4978,17 +4978,17 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, 
     mLSHE->AbandonBFCacheEntry();
   }
 
   nsAutoCString url;
   if (aURI) {
     nsresult rv = aURI->GetSpec(url);
     NS_ENSURE_SUCCESS(rv, rv);
   } else if (aURL) {
-    CopyUTF16toUTF8(aURL, url);
+    CopyUTF16toUTF8(MakeStringSpan(aURL), url);
   } else {
     return NS_ERROR_INVALID_POINTER;
   }
 
   // Create a URL to pass all the error information through to the page.
 
 #undef SAFE_ESCAPE
 #define SAFE_ESCAPE(output, input, params)                                     \
--- a/dom/base/DOMException.cpp
+++ b/dom/base/DOMException.cpp
@@ -234,17 +234,17 @@ Exception::GetName(nsAString& aName)
     CopyUTF8toUTF16(mName, aName);
   } else {
     aName.Truncate();
 
     const char* name = nullptr;
     nsXPCException::NameAndFormatForNSResult(mResult, &name, nullptr);
 
     if (name) {
-      CopyUTF8toUTF16(name, aName);
+      CopyUTF8toUTF16(mozilla::MakeStringSpan(name), aName);
     }
   }
 }
 
 void
 Exception::GetFilename(JSContext* aCx, nsAString& aFilename)
 {
   if (mLocation) {
--- a/dom/base/nsTextFragment.cpp
+++ b/dom/base/nsTextFragment.cpp
@@ -11,17 +11,16 @@
  */
 
 #include "nsTextFragment.h"
 #include "nsCRT.h"
 #include "nsReadableUtils.h"
 #include "nsMemory.h"
 #include "nsBidiUtils.h"
 #include "nsUnicharUtils.h"
-#include "nsUTF8Utils.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/SSE.h"
 #include "nsTextFragmentImpl.h"
 #include <algorithm>
 
 #define TEXTFRAG_WHITE_AFTER_NEWLINE 50
 #define TEXTFRAG_MAX_NEWLINES 7
@@ -314,18 +313,17 @@ nsTextFragment::SetTo(const char16_t* aB
   } else {
     // Use 1 byte storage because we can
     char* buff = static_cast<char*>(malloc(aLength));
     if (!buff) {
       return false;
     }
 
     // Copy data
-    LossyConvertEncoding16to8 converter(buff);
-    copy_string(aBuffer, aBuffer+aLength, converter);
+    LossyConvertUTF16toLatin1(MakeSpan(aBuffer, aLength), MakeSpan(buff, aLength));
     m1b = buff;
     mState.mIs2b = false;
   }
 
   // Setup our fields
   mState.mInHeap = true;
   mState.mLength = aLength;
 
@@ -346,19 +344,17 @@ nsTextFragment::CopyTo(char16_t *aDest, 
     aCount = mState.mLength - aOffset;
   }
 
   if (aCount != 0) {
     if (mState.mIs2b) {
       memcpy(aDest, Get2b() + aOffset, sizeof(char16_t) * aCount);
     } else {
       const char *cp = m1b + aOffset;
-      const char *end = cp + aCount;
-      LossyConvertEncoding8to16 converter(aDest);
-      copy_string(cp, end, converter);
+      ConvertLatin1toUTF16(MakeSpan(cp, aCount), MakeSpan(aDest, aCount));
     }
   }
 }
 
 bool
 nsTextFragment::Append(const char16_t* aBuffer, uint32_t aLength,
                        bool aUpdateBidi, bool aForce2b)
 {
@@ -435,18 +431,17 @@ nsTextFragment::Append(const char16_t* a
     // all to 2-byte
     nsStringBuffer* buff = nsStringBuffer::Alloc(size).take();
     if (!buff) {
       return false;
     }
 
     // Copy data into buff
     char16_t* data = static_cast<char16_t*>(buff->Data());
-    LossyConvertEncoding8to16 converter(data);
-    copy_string(m1b, m1b+mState.mLength, converter);
+    ConvertLatin1toUTF16(MakeSpan(m1b, mState.mLength), MakeSpan(data, mState.mLength));
 
     memcpy(data + mState.mLength, aBuffer, aLength * sizeof(char16_t));
     mState.mLength += aLength;
     mState.mIs2b = true;
 
     if (mState.mInHeap) {
       free(const_cast<char*>(m1b));
     }
@@ -478,18 +473,17 @@ nsTextFragment::Append(const char16_t* a
       return false;
     }
 
     memcpy(buff, m1b, mState.mLength);
     mState.mInHeap = true;
   }
 
   // Copy aBuffer into buff.
-  LossyConvertEncoding16to8 converter(buff + mState.mLength);
-  copy_string(aBuffer, aBuffer + aLength, converter);
+  LossyConvertUTF16toLatin1(MakeSpan(aBuffer, aLength), MakeSpan(buff + mState.mLength, aLength));
 
   m1b = buff;
   mState.mLength += aLength;
 
   return true;
 }
 
 /* virtual */ size_t
--- a/dom/base/nsXMLContentSerializer.cpp
+++ b/dom/base/nsXMLContentSerializer.cpp
@@ -586,19 +586,17 @@ nsXMLContentSerializer::ConfirmPrefix(ns
   // to create a namespace decl for the final prefix
   return true;
 }
 
 void
 nsXMLContentSerializer::GenerateNewPrefix(nsAString& aPrefix)
 {
   aPrefix.Assign('a');
-  char buf[128];
-  SprintfLiteral(buf, "%d", mPrefixIndex++);
-  AppendASCIItoUTF16(buf, aPrefix);
+  aPrefix.AppendInt(mPrefixIndex++);
 }
 
 bool
 nsXMLContentSerializer::SerializeAttr(const nsAString& aPrefix,
                                       const nsAString& aName,
                                       const nsAString& aValue,
                                       nsAString& aStr,
                                       bool aDoEscapeEntities)
@@ -1244,17 +1242,17 @@ nsXMLContentSerializer::AppendAndTransla
       if ((val <= kGTVal) && entityTable[val]) {
         entityText = kEntityStrings[entityTable[val]];
         break;
       }
     }
 
     NS_ENSURE_TRUE(aOutputStr.Append(fragmentStart, advanceLength, mozilla::fallible), false);
     if (entityText) {
-      NS_ENSURE_TRUE(AppendASCIItoUTF16(entityText, aOutputStr, mozilla::fallible), false);
+      NS_ENSURE_TRUE(AppendASCIItoUTF16(mozilla::MakeStringSpan(entityText), aOutputStr, mozilla::fallible), false);
       advanceLength++;
     }
   }
 
   return true;
 }
 
 bool
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -1239,17 +1239,17 @@ Event::GetWidgetEventType(WidgetEvent* a
   if (!aEvent->mSpecifiedEventTypeString.IsEmpty()) {
     aType = aEvent->mSpecifiedEventTypeString;
     return;
   }
 
   const char* name = GetEventName(aEvent->mMessage);
 
   if (name) {
-    CopyASCIItoUTF16(name, aType);
+    CopyASCIItoUTF16(mozilla::MakeStringSpan(name), aType);
     return;
   } else if (aEvent->mMessage == eUnidentifiedEvent &&
              aEvent->mSpecifiedEventType) {
     // Remove "on"
     aType = Substring(nsDependentAtomString(aEvent->mSpecifiedEventType), 2);
     aEvent->mSpecifiedEventTypeString = aType;
     return;
   }
--- a/dom/offline/nsDOMOfflineResourceList.cpp
+++ b/dom/offline/nsDOMOfflineResourceList.cpp
@@ -289,17 +289,17 @@ nsDOMOfflineResourceList::MozItem(uint32
   SetDOMStringToNull(aURI);
 
   rv = CacheKeys();
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aIndex >= mCachedKeysCount)
     return NS_ERROR_NOT_AVAILABLE;
 
-  CopyUTF8toUTF16(mCachedKeys[aIndex], aURI);
+  CopyUTF8toUTF16(mozilla::MakeStringSpan(mCachedKeys[aIndex]), aURI);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMOfflineResourceList::MozAdd(const nsAString& aURI)
 {
   if (IS_CHILD_PROCESS())
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -602,25 +602,25 @@ static void
 ThrowJSExceptionASCII(JSContext *cx, const char *message)
 {
   const char *ex = PeekException();
 
   if (ex) {
     nsAutoString ucex;
 
     if (message) {
-      AppendASCIItoUTF16(message, ucex);
-
-      AppendASCIItoUTF16(" [plugin exception: ", ucex);
+      AppendASCIItoUTF16(mozilla::MakeStringSpan(message), ucex);
+
+      ucex.AppendLiteral(" [plugin exception: ");
     }
 
-    AppendUTF8toUTF16(ex, ucex);
+    AppendUTF8toUTF16(mozilla::MakeStringSpan(ex), ucex);
 
     if (message) {
-      AppendASCIItoUTF16("].", ucex);
+      ucex.AppendLiteral("].");
     }
 
     JSString *str = ::JS_NewUCStringCopyN(cx, ucex.get(), ucex.Length());
 
     if (str) {
       JS::Rooted<JS::Value> exn(cx, JS::StringValue(str));
       ::JS_SetPendingException(cx, exn);
     }
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -851,17 +851,17 @@ nsNPAPIPluginInstance::GetFormValue(nsAS
 {
   aValue.Truncate();
 
   char *value = nullptr;
   nsresult rv = GetValueFromPlugin(NPPVformValue, &value);
   if (NS_FAILED(rv) || !value)
     return NS_ERROR_FAILURE;
 
-  CopyUTF8toUTF16(value, aValue);
+  CopyUTF8toUTF16(MakeStringSpan(value), aValue);
 
   // NPPVformValue allocates with NPN_MemAlloc(), which uses
   // nsMemory.
   free(value);
 
   return NS_OK;
 }
 
--- a/dom/presentation/PresentationRequest.cpp
+++ b/dom/presentation/PresentationRequest.cpp
@@ -211,17 +211,17 @@ PresentationRequest::StartWithDevice(con
     return promise.forget();
   }
 
   nsID uuid;
   uuidgen->GenerateUUIDInPlace(&uuid);
   char buffer[NSID_LENGTH];
   uuid.ToProvidedString(buffer);
   nsAutoString id;
-  CopyASCIItoUTF16(buffer, id);
+  CopyASCIItoUTF16(MakeSpan(buffer, NSID_LENGTH - 1), id);
 
   nsCOMPtr<nsIPresentationService> service =
     do_GetService(PRESENTATION_SERVICE_CONTRACTID);
   if(NS_WARN_IF(!service)) {
     promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
     return promise.forget();
   }
 
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -1569,17 +1569,17 @@ nsWebBrowserPersist::GetExtensionForCont
     nsresult rv;
     if (!mMIMEService)
     {
         mMIMEService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
         NS_ENSURE_TRUE(mMIMEService, NS_ERROR_FAILURE);
     }
 
     nsAutoCString contentType;
-    LossyCopyUTF16toASCII(aContentType, contentType);
+    LossyCopyUTF16toASCII(MakeStringSpan(aContentType), contentType);
     nsAutoCString ext;
     rv = mMIMEService->GetPrimaryExtension(contentType, EmptyCString(), ext);
     if (NS_SUCCEEDED(rv))
     {
         *aExt = UTF8ToNewUnicode(ext);
         NS_ENSURE_TRUE(*aExt, NS_ERROR_OUT_OF_MEMORY);
         return NS_OK;
     }
--- a/dom/xslt/xslt/txEXSLTFunctions.cpp
+++ b/dom/xslt/xslt/txEXSLTFunctions.cpp
@@ -305,17 +305,17 @@ txEXSLTFunctionCall::evaluate(txIEvalCon
             nsresult rv = mParams[0]->evaluate(aContext,
                                                getter_AddRefs(exprResult));
             NS_ENSURE_SUCCESS(rv, rv);
 
             RefPtr<StringResult> strRes;
             rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
             NS_ENSURE_SUCCESS(rv, rv);
 
-            AppendASCIItoUTF16(sTypes[exprResult->getResultType()],
+            AppendASCIItoUTF16(MakeStringSpan(sTypes[exprResult->getResultType()]),
                                strRes->mValue);
 
             NS_ADDREF(*aResult = strRes);
 
             return NS_OK;
         }
         case DIFFERENCE:
         case INTERSECTION:
--- a/dom/xslt/xslt/txXSLTNumberCounters.cpp
+++ b/dom/xslt/xslt/txXSLTNumberCounters.cpp
@@ -199,16 +199,16 @@ void txRomanCounter::appendNumber(int32_
         aNumber -= 1000;
     }
 
     int32_t posValue;
 
     // Hundreds
     posValue = aNumber / 100;
     aNumber %= 100;
-    AppendASCIItoUTF16(kTxRomanNumbers[posValue + mTableOffset], aDest);
+    AppendASCIItoUTF16(mozilla::MakeStringSpan(kTxRomanNumbers[posValue + mTableOffset]), aDest);
     // Tens
     posValue = aNumber / 10;
     aNumber %= 10;
-    AppendASCIItoUTF16(kTxRomanNumbers[10 + posValue + mTableOffset], aDest);
+    AppendASCIItoUTF16(mozilla::MakeStringSpan(kTxRomanNumbers[10 + posValue + mTableOffset]), aDest);
     // Ones
-    AppendASCIItoUTF16(kTxRomanNumbers[20 + aNumber + mTableOffset], aDest);
+    AppendASCIItoUTF16(mozilla::MakeStringSpan(kTxRomanNumbers[20 + aNumber + mTableOffset]), aDest);
 }
--- a/editor/libeditor/CSSEditUtils.cpp
+++ b/editor/libeditor/CSSEditUtils.cpp
@@ -60,17 +60,17 @@ void ProcessBValue(const nsAString* aInp
 
 static
 void ProcessDefaultValue(const nsAString* aInputString,
                          nsAString& aOutputString,
                          const char* aDefaultValueString,
                          const char* aPrependString,
                          const char* aAppendString)
 {
-  CopyASCIItoUTF16(aDefaultValueString, aOutputString);
+  CopyASCIItoUTF16(MakeStringSpan(aDefaultValueString), aOutputString);
 }
 
 static
 void ProcessSameValue(const nsAString* aInputString,
                       nsAString & aOutputString,
                       const char* aDefaultValueString,
                       const char* aPrependString,
                       const char* aAppendString)
@@ -87,21 +87,21 @@ void ProcessExtendedValue(const nsAStrin
                           nsAString& aOutputString,
                           const char* aDefaultValueString,
                           const char* aPrependString,
                           const char* aAppendString)
 {
   aOutputString.Truncate();
   if (aInputString) {
     if (aPrependString) {
-      AppendASCIItoUTF16(aPrependString, aOutputString);
+      AppendASCIItoUTF16(MakeStringSpan(aPrependString), aOutputString);
     }
     aOutputString.Append(*aInputString);
     if (aAppendString) {
-      AppendASCIItoUTF16(aAppendString, aOutputString);
+      AppendASCIItoUTF16(MakeStringSpan(aAppendString), aOutputString);
     }
   }
 }
 
 static
 void ProcessLengthValue(const nsAString* aInputString,
                         nsAString& aOutputString,
                         const char* aDefaultValueString,
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -122,41 +122,41 @@ FindCanonicalNameIndex(FcPattern* aFont,
 
 static void
 GetFaceNames(FcPattern* aFont, const nsAString& aFamilyName,
              nsAString& aPostscriptName, nsAString& aFullname)
 {
     // get the Postscript name
     FcChar8* psname;
     if (FcPatternGetString(aFont, FC_POSTSCRIPT_NAME, 0, &psname) == FcResultMatch) {
-        AppendUTF8toUTF16(ToCharPtr(psname), aPostscriptName);
+        AppendUTF8toUTF16(MakeStringSpan(ToCharPtr(psname)), aPostscriptName);
     }
 
     // get the canonical fullname (i.e. en name or first name)
     uint32_t en = FindCanonicalNameIndex(aFont, FC_FULLNAMELANG);
     FcChar8* fullname;
     if (FcPatternGetString(aFont, FC_FULLNAME, en, &fullname) == FcResultMatch) {
-        AppendUTF8toUTF16(ToCharPtr(fullname), aFullname);
+        AppendUTF8toUTF16(MakeStringSpan(ToCharPtr(fullname)), aFullname);
     }
 
     // if have fullname, done
     if (!aFullname.IsEmpty()) {
         return;
     }
 
     // otherwise, set the fullname to family + style name [en] and use that
     aFullname.Append(aFamilyName);
 
     // figure out the en style name
     en = FindCanonicalNameIndex(aFont, FC_STYLELANG);
     nsAutoString style;
     FcChar8* stylename = nullptr;
     FcPatternGetString(aFont, FC_STYLE, en, &stylename);
     if (stylename) {
-        AppendUTF8toUTF16(ToCharPtr(stylename), style);
+        AppendUTF8toUTF16(MakeStringSpan(ToCharPtr(stylename)), style);
     }
 
     if (!style.IsEmpty() && !style.EqualsLiteral("Regular")) {
         aFullname.Append(' ');
         aFullname.Append(style);
     }
 }
 
@@ -1556,17 +1556,17 @@ gfxFcPlatformFontList::AddPatternToFontL
     }
 
     // same as the last one? no need to add a new family, skip
     if (FcStrCmp(canonical, aLastFamilyName) != 0) {
         aLastFamilyName = canonical;
 
         // add new family if one doesn't already exist
         aFamilyName.Truncate();
-        AppendUTF8toUTF16(ToCharPtr(canonical), aFamilyName);
+        AppendUTF8toUTF16(MakeStringSpan(ToCharPtr(canonical)), aFamilyName);
         nsAutoString keyName(aFamilyName);
         ToLowerCase(keyName);
 
         aFontFamily = static_cast<gfxFontconfigFontFamily*>
             (mFontFamilies.GetWeak(keyName));
         if (!aFontFamily) {
             aFontFamily = new gfxFontconfigFontFamily(aFamilyName);
             mFontFamilies.Put(keyName, aFontFamily);
@@ -1787,17 +1787,17 @@ GetSystemFontList(nsTArray<nsString>& aL
         if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
                                (FcChar8 **) &family) != FcResultMatch)
         {
             continue;
         }
 
         // Remove duplicates...
         nsAutoString strFamily;
-        AppendUTF8toUTF16(family, strFamily);
+        AppendUTF8toUTF16(MakeStringSpan(family), strFamily);
         if (aListOfFonts.Contains(strFamily)) {
             continue;
         }
 
         aListOfFonts.AppendElement(strFamily);
     }
 
     aListOfFonts.Sort();
--- a/intl/encoding_glue/src/lib.rs
+++ b/intl/encoding_glue/src/lib.rs
@@ -585,8 +585,58 @@ fn checked_min(one: Option<usize>, other
 }
 
 // Bindings for encoding_rs::mem. These may move to a separate crate in the future.
 
 #[no_mangle]
 pub unsafe extern "C" fn encoding_mem_is_utf16_bidi(buffer: *const u16, len: usize) -> bool {
     encoding_rs::mem::is_utf16_bidi(::std::slice::from_raw_parts(buffer, len))
 }
+
+#[no_mangle]
+pub unsafe extern "C" fn encoding_mem_is_ascii(buffer: *const u8, len: usize) -> bool {
+    encoding_rs::mem::is_ascii(::std::slice::from_raw_parts(buffer, len))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn encoding_mem_is_basic_latin(buffer: *const u16, len: usize) -> bool {
+    encoding_rs::mem::is_basic_latin(::std::slice::from_raw_parts(buffer, len))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn encoding_mem_is_utf8_latin1(buffer: *const u8, len: usize) -> bool {
+    encoding_rs::mem::is_utf8_latin1(::std::slice::from_raw_parts(buffer, len))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn encoding_mem_is_utf16_latin1(buffer: *const u16, len: usize) -> bool {
+    encoding_rs::mem::is_utf16_latin1(::std::slice::from_raw_parts(buffer, len))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn encoding_mem_is_str_latin1(buffer: *const u8, len: usize) -> bool {
+    encoding_rs::mem::is_str_latin1(::std::str::from_utf8_unchecked(::std::slice::from_raw_parts(buffer, len)))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn encoding_mem_convert_utf16_to_latin1_lossy(src: *const u16, src_len: usize, dst: *mut u8, dst_len: usize) {
+    encoding_rs::mem::convert_utf16_to_latin1_lossy(::std::slice::from_raw_parts(src, src_len), ::std::slice::from_raw_parts_mut(dst, dst_len));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn encoding_mem_convert_utf8_to_latin1_lossy(src: *const u8, src_len: usize, dst: *mut u8, dst_len: usize) -> usize {
+    encoding_rs::mem::convert_utf8_to_latin1_lossy(::std::slice::from_raw_parts(src, src_len), ::std::slice::from_raw_parts_mut(dst, dst_len))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn encoding_mem_convert_latin1_to_utf16(src: *const u8, src_len: usize, dst: *mut u16, dst_len: usize) {
+    encoding_rs::mem::convert_latin1_to_utf16(::std::slice::from_raw_parts(src, src_len), ::std::slice::from_raw_parts_mut(dst, dst_len));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn encoding_mem_convert_utf16_to_utf8(src: *const u16, src_len: usize, dst: *mut u8, dst_len: usize) -> usize {
+    encoding_rs::mem::convert_utf16_to_utf8(::std::slice::from_raw_parts(src, src_len), ::std::slice::from_raw_parts_mut(dst, dst_len))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn encoding_mem_convert_utf8_to_utf16(src: *const u8, src_len: usize, dst: *mut u16, dst_len: usize) -> usize {
+    encoding_rs::mem::convert_utf8_to_utf16(::std::slice::from_raw_parts(src, src_len), ::std::slice::from_raw_parts_mut(dst, dst_len))
+}
--- a/intl/lwbrk/nsPangoBreaker.cpp
+++ b/intl/lwbrk/nsPangoBreaker.cpp
@@ -37,21 +37,23 @@ NS_GetComplexLineBreaks(const char16_t* 
 
     while (p < end)
     {
       aBreakBefore[u16Offset] = attr->is_line_break;
       if (NS_IS_LOW_SURROGATE(aText[u16Offset]))
         aBreakBefore[++u16Offset] = false; // Skip high surrogate
       ++u16Offset;
 
-      bool err;
-      uint32_t ch = UTF8CharEnumerator::NextChar(&p, end, &err);
+      // We're iterating over text obtained from NS_ConvertUTF16toUTF8,
+      // so we know we have valid UTF-8 and don't need to check for
+      // errors.
+      uint32_t ch = UTF8CharEnumerator::NextChar(&p, end);
       ++attr;
 
-      if (ch == 0 || err) {
+      if (!ch) {
         // pango_break (pango 1.16.2) only analyses text before the
         // first NUL (but sets one extra attr). Workaround loop to call
         // pango_break again to analyse after the NUL is done somewhere else
         // (gfx/thebes/gfxFontconfigFonts.cpp: SetupClusterBoundaries()).
         // So, we do the same here for pango_get_log_attrs.
         break;
       }
     }
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -2123,17 +2123,17 @@ nsXPCComponents_Utils::ReportError(Handl
 
     if (!scripterr) {
         scripterr = new nsScriptError();
     }
 
     if (err) {
         // It's a proper JS Error
         nsAutoString fileUni;
-        CopyUTF8toUTF16(err->filename, fileUni);
+        CopyUTF8toUTF16(mozilla::MakeStringSpan(err->filename), fileUni);
 
         uint32_t column = err->tokenOffset();
 
         const char16_t* linebuf = err->linebuf();
 
         nsresult rv = scripterr->InitWithWindowID(
                 err->message() ? NS_ConvertUTF8toUTF16(err->message().c_str())
                 : EmptyString(),
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -258,51 +258,77 @@ XPCConvert::NativeData2JS(MutableHandleV
     {
         const nsACString* utf8String = *static_cast<const nsACString* const*>(s);
 
         if (!utf8String || utf8String->IsVoid()) {
             d.setNull();
             return true;
         }
 
-        if (utf8String->IsEmpty()) {
+        uint32_t len = utf8String->Length();
+
+        if (!len) {
             d.set(JS_GetEmptyStringValue(cx));
             return true;
         }
 
-        const uint32_t len = CalcUTF8ToUnicodeLength(*utf8String);
-        // The cString is not empty at this point, but the calculated
-        // UTF-16 length is zero, meaning no valid conversion exists.
-        if (!len)
-            return false;
-
-        const size_t buffer_size = (len + 1) * sizeof(char16_t);
-        char16_t* buffer =
-            static_cast<char16_t*>(JS_malloc(cx, buffer_size));
-        if (!buffer)
-            return false;
-
-        uint32_t copied;
-        if (!UTF8ToUnicodeBuffer(*utf8String, buffer, &copied) ||
-            len != copied) {
-            // Copy or conversion during copy failed. Did not copy the
-            // whole string.
-            JS_free(cx, buffer);
+        CheckedInt<uint32_t> allocLen(len);
+        allocLen += 1;
+        if (!allocLen.isValid()) {
             return false;
         }
 
-        // JS_NewUCString takes ownership on success, i.e. a
+        // Usage of UTF-8 in XPConnect is mostly for things that are
+        // almost always ASCII, so the inexact allocations below
+        // should be fine.
+
+        if (IsUTF8Latin1(*utf8String)) {
+            char* buffer =
+                static_cast<char*>(JS_malloc(cx, allocLen.value()));
+            if (!buffer) {
+                return false;
+            }
+            size_t written = LossyConvertUTF8toLatin1(*utf8String, MakeSpan(buffer, len));
+            buffer[written] = 0;
+            // JS_NewLatin1String takes ownership on success, i.e. a
+            // successful call will make it the responsiblity of the JS VM
+            // to free the buffer.
+            // written can never exceed len, so the truncation is OK.
+            JSString* str = JS_NewLatin1String(cx, reinterpret_cast<JS::Latin1Char*>(buffer), written);
+            if (!str) {
+                JS_free(cx, buffer);
+                return false;
+            }
+            d.setString(str);
+            return true;
+        }
+
+        allocLen *= sizeof(char16_t);
+        if (!allocLen.isValid()) {
+            return false;
+        }
+
+        // ConvertUTF8toUTF16 requires the destination to be one code unit
+        // longer than the source.
+        char16_t* buffer =
+            static_cast<char16_t*>(JS_malloc(cx, allocLen.value()));
+        if (!buffer) {
+            return false;
+        }
+        size_t written = ConvertUTF8toUTF16(*utf8String, MakeSpan(buffer, len + 1));
+        buffer[written] = 0;
+        // JS_NewUCStringDontDeflate takes ownership on success, i.e. a
         // successful call will make it the responsiblity of the JS VM
         // to free the buffer.
-        JSString* str = JS_NewUCString(cx, buffer, len);
+        // written can never exceed len + 1, so the truncation is OK.
+        JSString* str = JS_NewUCStringDontDeflate(cx, buffer, written);
         if (!str) {
             JS_free(cx, buffer);
             return false;
         }
-
         d.setString(str);
         return true;
     }
     case nsXPTType::T_CSTRING:
     {
         const nsACString* cString = *static_cast<const nsACString* const*>(s);
 
         if (!cString || cString->IsVoid()) {
@@ -1016,19 +1042,19 @@ JSErrorToXPCException(const char* toStri
                       Exception** exceptn)
 {
     AutoJSContext cx;
     nsresult rv = NS_ERROR_FAILURE;
     RefPtr<nsScriptError> data;
     if (report) {
         nsAutoString bestMessage;
         if (report && report->message()) {
-            CopyUTF8toUTF16(report->message().c_str(), bestMessage);
+            CopyUTF8toUTF16(mozilla::MakeStringSpan(report->message().c_str()), bestMessage);
         } else if (toStringResult) {
-            CopyUTF8toUTF16(toStringResult, bestMessage);
+            CopyUTF8toUTF16(mozilla::MakeStringSpan(toStringResult), bestMessage);
         } else {
             bestMessage.AssignLiteral("JavaScript Error");
         }
 
         const char16_t* linebuf = report->linebuf();
 
         data = new nsScriptError();
         data->InitWithWindowID(
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -186,17 +186,18 @@ nsXPConnect::IsISupportsDescendant(nsIIn
 }
 
 void
 xpc::ErrorBase::Init(JSErrorBase* aReport)
 {
     if (!aReport->filename)
         mFileName.SetIsVoid(true);
     else
-        CopyASCIItoUTF16(aReport->filename, mFileName);
+        CopyASCIItoUTF16(mozilla::MakeStringSpan(aReport->filename), mFileName);
+        // XXX should the above use CopyUTF8toUTF16 instead?
 
     mLineNumber = aReport->lineno;
     mColumn = aReport->column;
 }
 
 void
 xpc::ErrorNote::Init(JSErrorNotes::Note* aNote)
 {
@@ -211,17 +212,17 @@ xpc::ErrorReport::Init(JSErrorReport* aR
 {
     xpc::ErrorBase::Init(aReport);
     mCategory = aIsChrome ? NS_LITERAL_CSTRING("chrome javascript")
                           : NS_LITERAL_CSTRING("content javascript");
     mWindowID = aWindowID;
 
     ErrorReportToMessageString(aReport, mErrorMsg);
     if (mErrorMsg.IsEmpty() && aToStringResult) {
-        AppendUTF8toUTF16(aToStringResult, mErrorMsg);
+        AppendUTF8toUTF16(mozilla::MakeStringSpan(aToStringResult), mErrorMsg);
     }
 
     mSourceLine.Assign(aReport->linebuf(), aReport->linebufLength());
     const JSErrorFormatString* efs = js::GetErrorMessage(nullptr, aReport->errorNumber);
 
     if (efs == nullptr) {
         mErrorMsgName.AssignASCII("");
     } else {
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -2400,17 +2400,17 @@ HttpBaseChannel::GetResponseVersion(uint
 }
 
 void
 HttpBaseChannel::NotifySetCookie(char const *aCookie)
 {
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (obs) {
     nsAutoString cookie;
-    CopyASCIItoUTF16(aCookie, cookie);
+    CopyASCIItoUTF16(mozilla::MakeStringSpan(aCookie), cookie);
     obs->NotifyObservers(static_cast<nsIChannel*>(this),
                          "http-on-response-set-cookie",
                          cookie.get());
   }
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::SetCookie(const char *aCookieHeader)
--- a/netwerk/protocol/http/nsHttpBasicAuth.cpp
+++ b/netwerk/protocol/http/nsHttpBasicAuth.cpp
@@ -84,20 +84,22 @@ nsHttpBasicAuth::GenerateCredentials(nsI
     *aFlags = 0;
 
     // we only know how to deal with Basic auth for http.
     bool isBasicAuth = !PL_strncasecmp(challenge, "basic", 5);
     NS_ENSURE_TRUE(isBasicAuth, NS_ERROR_UNEXPECTED);
 
     // we work with UTF-8 around here
     nsAutoCString userpass;
-    CopyUTF16toUTF8(user, userpass);
+    if (user) {
+      CopyUTF16toUTF8(mozilla::MakeStringSpan(user), userpass);
+    }
     userpass.Append(':'); // always send a ':' (see bug 129565)
     if (password) {
-        AppendUTF16toUTF8(password, userpass);
+        AppendUTF16toUTF8(mozilla::MakeStringSpan(password), userpass);
     }
 
     nsAutoCString authString;
     nsresult rv = Base64Encode(userpass, authString);
     NS_ENSURE_SUCCESS(rv, rv);
 
     authString.InsertLiteral("Basic ", 0);
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -6189,25 +6189,25 @@ nsHttpChannel::BeginConnect()
             rv = mRequestHead.SetHeader(nsHttp::Alternate_Service_Used, altUsedLine);
             MOZ_ASSERT(NS_SUCCEEDED(rv));
         }
 
         nsCOMPtr<nsIConsoleService> consoleService =
             do_GetService(NS_CONSOLESERVICE_CONTRACTID);
         if (consoleService) {
             nsAutoString message(NS_LITERAL_STRING("Alternate Service Mapping found: "));
-            AppendASCIItoUTF16(scheme.get(), message);
+            AppendASCIItoUTF16(scheme, message);
             message.AppendLiteral(u"://");
-            AppendASCIItoUTF16(host.get(), message);
+            AppendASCIItoUTF16(host, message);
             message.AppendLiteral(u":");
             message.AppendInt(port);
             message.AppendLiteral(u" to ");
-            AppendASCIItoUTF16(scheme.get(), message);
+            AppendASCIItoUTF16(scheme, message);
             message.AppendLiteral(u"://");
-            AppendASCIItoUTF16(mapping->AlternateHost().get(), message);
+            AppendASCIItoUTF16(mapping->AlternateHost(), message);
             message.AppendLiteral(u":");
             message.AppendInt(mapping->AlternatePort());
             consoleService->LogStringMessage(message.get());
         }
 
         LOG(("nsHttpChannel %p Using connection info from altsvc mapping", this));
         mapping->GetConnectionInfo(getter_AddRefs(mConnectionInfo), proxyInfo, originAttributes);
         Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, true);
--- a/netwerk/streamconv/converters/nsTXTToHTMLConv.cpp
+++ b/netwerk/streamconv/converters/nsTXTToHTMLConv.cpp
@@ -142,19 +142,18 @@ nsTXTToHTMLConv::OnDataAvailable(nsIRequ
     if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
 
     do {
         uint32_t read = 0;
         // XXX readSegments, to avoid the first copy?
         rv = aInStream->Read(buffer.get(), aCount-amtRead, &read);
         if (NS_FAILED(rv)) return rv;
 
-        buffer[read] = '\0';
         // XXX charsets?? non-latin1 characters?? utf-16??
-        AppendASCIItoUTF16(buffer.get(), mBuffer);
+        AppendASCIItoUTF16(mozilla::MakeSpan(buffer.get(), read), mBuffer);
         amtRead += read;
 
         int32_t front = -1, back = -1, tokenLoc = -1, cursor = 0;
 
         while ( (tokenLoc = FindToken(cursor, &mToken)) > -1) {
             if (mToken->prepend) {
                 front = mBuffer.RFindCharInSet(TOKEN_DELIMITERS, tokenLoc);
                 front++;
--- a/parser/html/nsHtml5String.cpp
+++ b/parser/html/nsHtml5String.cpp
@@ -165,18 +165,17 @@ nsHtml5String::FromLiteral(const char* a
   // nsAttrValue::GetStringBuffer, so that it doesn't need to reallocate and
   // copy.
   RefPtr<nsStringBuffer> buffer(
     nsStringBuffer::Alloc((length + 1) * sizeof(char16_t)));
   if (!buffer) {
     MOZ_CRASH("Out of memory.");
   }
   char16_t* data = reinterpret_cast<char16_t*>(buffer->Data());
-  LossyConvertEncoding8to16 converter(data);
-  converter.write(aLiteral, length);
+  ConvertLatin1toUTF16(MakeSpan(aLiteral, length), MakeSpan(data, length));
   data[length] = 0;
   return nsHtml5String(reinterpret_cast<uintptr_t>(buffer.forget().take()) |
                        eStringBuffer);
 }
 
 // static
 nsHtml5String
 nsHtml5String::FromString(const nsAString& aString)
--- a/parser/htmlparser/nsExpatDriver.cpp
+++ b/parser/htmlparser/nsExpatDriver.cpp
@@ -26,16 +26,17 @@
 #include "nsUnicharInputStream.h"
 #include "nsContentUtils.h"
 #include "NullPrincipal.h"
 
 #include "mozilla/Logging.h"
 
 using mozilla::fallible;
 using mozilla::LogLevel;
+using mozilla::MakeStringSpan;
 
 #define kExpatSeparatorChar 0xFFFF
 
 static const char16_t kUTF16[] = { 'U', 'T', 'F', '-', '1', '6', '\0' };
 
 static mozilla::LazyLogModule gExpatDriverLog("expatdriver");
 
 /***************************** EXPAT CALL BACKS ******************************/
@@ -572,21 +573,21 @@ nsExpatDriver::HandleExternalEntityRef(c
   // Load the external entity into a buffer.
   nsCOMPtr<nsIInputStream> in;
   nsAutoString absURL;
   nsresult rv = OpenInputStreamFromExternalDTD(publicId, systemId, base,
                                                getter_AddRefs(in), absURL);
   if (NS_FAILED(rv)) {
 #ifdef DEBUG
     nsCString message("Failed to open external DTD: publicId \"");
-    AppendUTF16toUTF8(publicId, message);
+    AppendUTF16toUTF8(MakeStringSpan(publicId), message);
     message += "\" systemId \"";
-    AppendUTF16toUTF8(systemId, message);
+    AppendUTF16toUTF8(MakeStringSpan(systemId), message);
     message += "\" base \"";
-    AppendUTF16toUTF8(base, message);
+    AppendUTF16toUTF8(MakeStringSpan(base), message);
     message += "\" URL \"";
     AppendUTF16toUTF8(absURL, message);
     message += "\"";
     NS_WARNING(message.get());
 #endif
     return 1;
   }
 
--- a/rdf/base/nsRDFContentSink.cpp
+++ b/rdf/base/nsRDFContentSink.cpp
@@ -797,17 +797,17 @@ RDFContentSinkImpl::GetIdAboutAttribute(
             // http://www.w3.org/TR/REC-xml#NT-Nmtoken), as per
             // 6.21. If we wanted to, this would be where to do it.
 
             // Construct an in-line resource whose URI is the
             // document's URI plus the XML name specified in the ID
             // attribute.
             nsAutoCString name;
             nsAutoCString ref('#');
-            AppendUTF16toUTF8(aAttributes[1], ref);
+            AppendUTF16toUTF8(MakeStringSpan(aAttributes[1]), ref);
 
             rv = mDocumentURL->Resolve(ref, name);
             if (NS_FAILED(rv)) return rv;
 
             return gRDFService->GetResource(name, aResource);
         }
         else if (localName == nsGkAtoms::nodeID) {
             nodeID.Assign(aAttributes[1]);
--- a/security/manager/ssl/nsNSSCertHelper.cpp
+++ b/security/manager/ssl/nsNSSCertHelper.cpp
@@ -935,17 +935,17 @@ AppendBMPtoUTF16(const UniquePLArenaPool
      be sufficient to just swap bytes, or do nothing */
   unsigned int utf8ValLen = len * 3 + 1;
   unsigned char* utf8Val =
     (unsigned char*)PORT_ArenaZAlloc(arena.get(), utf8ValLen);
   if (!PORT_UCS2_UTF8Conversion(
         false, data, len, utf8Val, utf8ValLen, &utf8ValLen)) {
     return NS_ERROR_FAILURE;
   }
-  AppendUTF8toUTF16((char*)utf8Val, text);
+  AppendUTF8toUTF16(MakeSpan(reinterpret_cast<char*>(utf8Val), utf8ValLen), text);
   return NS_OK;
 }
 
 static nsresult
 ProcessBMPString(SECItem* extData, nsAString& text)
 {
   UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
   if (!arena) {
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -373,17 +373,17 @@ nsNSSCertificate::GetDisplayName(nsAStri
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSCertificate::GetEmailAddress(nsAString& aEmailAddress)
 {
   if (mCert->emailAddr) {
-    CopyUTF8toUTF16(mCert->emailAddr, aEmailAddress);
+    CopyUTF8toUTF16(MakeStringSpan(mCert->emailAddr), aEmailAddress);
   } else {
     nsresult rv;
     nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
     if (NS_FAILED(rv) || !nssComponent) {
       return NS_ERROR_FAILURE;
     }
     nssComponent->GetPIPNSSBundleString("CertNoEmailAddress", aEmailAddress);
   }
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -2131,25 +2131,25 @@ fn static_assert() {
         };
 
         let mut refptr = unsafe {
             UniqueRefPtr::from_addrefed(
                 Gecko_NewGridTemplateAreasValue(v.0.areas.len() as u32, v.0.strings.len() as u32, v.0.width))
         };
 
         for (servo, gecko) in v.0.areas.into_iter().zip(refptr.mNamedAreas.iter_mut()) {
-            gecko.mName.assign_utf8(&*servo.name);
+            gecko.mName.assign_str(&*servo.name);
             gecko.mColumnStart = servo.columns.start;
             gecko.mColumnEnd = servo.columns.end;
             gecko.mRowStart = servo.rows.start;
             gecko.mRowEnd = servo.rows.end;
         }
 
         for (servo, gecko) in v.0.strings.into_iter().zip(refptr.mTemplates.iter_mut()) {
-            gecko.assign_utf8(&*servo);
+            gecko.assign_str(&*servo);
         }
 
         self.gecko.mGridTemplateAreas.set_move(refptr.get())
     }
 
     pub fn copy_grid_template_areas_from(&mut self, other: &Self) {
         unsafe { self.gecko.mGridTemplateAreas.set(&other.gecko.mGridTemplateAreas) }
     }
@@ -4144,18 +4144,18 @@ fn static_assert() {
         use gecko_bindings::bindings::Gecko_NewStyleQuoteValues;
         use gecko_bindings::sugar::refptr::UniqueRefPtr;
 
         let mut refptr = unsafe {
             UniqueRefPtr::from_addrefed(Gecko_NewStyleQuoteValues(other.0.len() as u32))
         };
 
         for (servo, gecko) in other.0.into_iter().zip(refptr.mQuotePairs.iter_mut()) {
-            gecko.first.assign_utf8(&servo.0);
-            gecko.second.assign_utf8(&servo.1);
+            gecko.first.assign_str(&servo.0);
+            gecko.second.assign_str(&servo.1);
         }
 
         self.gecko.mQuotes.set_move(refptr.get())
     }
 
     pub fn copy_quotes_from(&mut self, other: &Self) {
         unsafe { self.gecko.mQuotes.set(&other.gecko.mQuotes); }
     }
@@ -4734,17 +4734,17 @@ fn static_assert() {
                 };
 
                 (shape | fill, keyword.shape.char(keyword.fill))
             },
             TextEmphasisStyle::String(ref s) => {
                 (structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING, &**s)
             },
         };
-        self.gecko.mTextEmphasisStyleString.assign_utf8(s);
+        self.gecko.mTextEmphasisStyleString.assign_str(s);
         self.gecko.mTextEmphasisStyle = te as u8;
     }
 
     pub fn copy_text_emphasis_style_from(&mut self, other: &Self) {
         self.clear_text_emphasis_style_if_string();
         if other.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 {
             self.gecko.mTextEmphasisStyleString
                       .assign(&*other.gecko.mTextEmphasisStyleString)
@@ -4835,17 +4835,17 @@ fn static_assert() {
         use gecko_bindings::structs::nsStyleTextOverflowSide;
         use values::specified::text::TextOverflowSide;
 
         fn set(side: &mut nsStyleTextOverflowSide, value: &TextOverflowSide) {
             let ty = match *value {
                 TextOverflowSide::Clip => structs::NS_STYLE_TEXT_OVERFLOW_CLIP,
                 TextOverflowSide::Ellipsis => structs::NS_STYLE_TEXT_OVERFLOW_ELLIPSIS,
                 TextOverflowSide::String(ref s) => {
-                    side.mString.assign_utf8(s);
+                    side.mString.assign_str(s);
                     structs::NS_STYLE_TEXT_OVERFLOW_STRING
                 }
             };
             side.mType = ty as u8;
         }
 
         self.clear_overflow_sides_if_string();
         self.gecko.mTextOverflow.mLogicalDirections = v.sides_are_logical;
@@ -5457,17 +5457,17 @@ clip-path
         ) {
             debug_assert!(content_type == eStyleContentType_Counter ||
                           content_type == eStyleContentType_Counters);
             let counter_func = unsafe {
                 bindings::Gecko_SetCounterFunction(data, content_type).as_mut().unwrap()
             };
             counter_func.mIdent.assign(name.0.as_slice());
             if content_type == eStyleContentType_Counters {
-                counter_func.mSeparator.assign_utf8(sep);
+                counter_func.mSeparator.assign_str(sep);
             }
             style.to_gecko_value(&mut counter_func.mCounterStyle, device);
         }
 
         match v {
             Content::None |
             Content::Normal => {
                 // Ensure destructors run, otherwise we could leak.
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -2886,17 +2886,17 @@ pub extern "C" fn Servo_DeclarationBlock
 pub extern "C" fn Servo_DeclarationBlock_GetNthProperty(
     declarations: RawServoDeclarationBlockBorrowed,
     index: u32,
     result: *mut nsAString,
 ) -> bool {
     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
         if let Some(decl) = decls.declarations().get(index as usize) {
             let result = unsafe { result.as_mut().unwrap() };
-            result.assign_utf8(&decl.id().name());
+            result.assign_str(&decl.id().name());
             true
         } else {
             false
         }
     })
 }
 
 macro_rules! get_property_id_from_property {
--- a/servo/support/gecko/nsstring/Cargo.toml
+++ b/servo/support/gecko/nsstring/Cargo.toml
@@ -5,9 +5,9 @@ authors = ["nobody@mozilla.com"]
 license = "MPL-2.0"
 description = "Rust bindings to xpcom string types"
 
 [features]
 gecko_debug = []
 
 [dependencies]
 bitflags = "1.0"
-
+encoding_rs = "0.7.2"
new file mode 100644
--- /dev/null
+++ b/servo/support/gecko/nsstring/src/conversions.rs
@@ -0,0 +1,671 @@
+/* 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/. */
+
+extern crate encoding_rs;
+
+use super::nsAString;
+use super::nsACString;
+use super::nsCStringLike;
+use super::Latin1StringLike;
+use super::Gecko_FallibleAssignCString;
+
+use conversions::encoding_rs::mem::*;
+use conversions::encoding_rs::Encoding;
+
+/// Required math stated in the docs of
+/// `convert_utf16_to_utf8()`.
+#[inline(always)]
+fn times_three_plus_one(a: usize) -> Option<usize> {
+    a.checked_mul(3)?.checked_add(1)
+}
+
+#[inline(always)]
+fn identity(a: usize) -> Option<usize> {
+    Some(a)
+}
+
+#[inline(always)]
+fn plus_one(a: usize) -> Option<usize> {
+    a.checked_add(1)
+}
+
+/// A conversion where the number of code units in the output is potentially
+/// smaller than the number of code units in the input.
+///
+/// Takes the name of the method to be generated, the name of the conversion
+/// function and the type of the input slice.
+///
+/// `$name` is the name of the function to generate
+/// `$convert` is the underlying `encoding_rs::mem` function to use
+/// `$other_ty` is the type of the input slice
+/// `$math` is the worst-case length math that `$convert` expects
+macro_rules! shrinking_conversion {
+    ($name:ident,
+     $convert:ident,
+     $other_ty:ty,
+     $math:ident) => (
+        fn $name(&mut self, other: $other_ty, old_len: usize) -> Result<(), ()> {
+            let written = {
+                let needed = $math(other.len()).ok_or(())?;
+                let buffer = unsafe {
+                    self.fallible_maybe_expand_capacity(old_len.checked_add(needed).ok_or(())?)?
+                };
+                $convert(other, &mut buffer[old_len..])
+            };
+            unsafe {
+                // TODO: Shrink buffer
+                self.fallible_set_length((old_len + written) as u32)?;
+            }
+            Ok(())
+        }
+     )
+}
+
+/// A conversion where the number of code units in the output is always equal
+/// to the number of code units in the input.
+///
+/// Takes the name of the method to be generated, the name of the conversion
+/// function and the type of the input slice.
+///
+/// `$name` is the name of the function to generate
+/// `$convert` is the underlying `encoding_rs::mem` function to use
+/// `$other_ty` is the type of the input slice
+macro_rules! constant_conversion {
+    ($name:ident,
+     $convert:ident,
+     $other_ty:ty) => (
+        fn $name(&mut self, other: $other_ty, old_len: usize) -> Result<(), ()> {
+            let needed = old_len.checked_add(other.len()).ok_or(())?;
+            {
+                let buffer = unsafe {
+                    self.fallible_maybe_expand_capacity(needed)?
+                };
+                $convert(other, &mut buffer[old_len..])
+            }
+            unsafe {
+                // Truncation to u32 is OK, because `fallible_maybe_expand_capacity()`
+                // would have failed if not.
+                self.fallible_set_length(needed as u32)?;
+            }
+            Ok(())
+        }
+     )
+}
+
+/// An intermediate check for avoiding a copy and having an `nsStringBuffer`
+/// refcount increment instead when both `self` and `other` are `nsACString`s,
+/// `other` is entirely ASCII and all old data in `self` is discarded.
+///
+/// `$name` is the name of the function to generate
+/// `$impl` is the underlying conversion that takes a slice and that is used
+///         when we can't just adopt the incoming buffer as-is
+/// `$string_like` is the kind of input taken
+macro_rules! ascii_copy_avoidance {
+    ($name:ident,
+     $impl:ident,
+     $string_like:ident) => (
+        fn $name<T: $string_like + ?Sized>(&mut self, other: &T, old_len: usize) -> Result<(), ()> {
+            let adapter = other.adapt();
+            let other_slice = adapter.as_ref();
+            let num_ascii = if adapter.is_abstract() && old_len == 0 {
+                let up_to = Encoding::ascii_valid_up_to(other_slice);
+                if up_to == other_slice.len() {
+                    // Calling something whose argument can be obtained from
+                    // the adapter rather than an nsStringLike avoids a huge
+                    // lifetime mess by keeping nsStringLike and
+                    // Latin1StringLike free of lifetime interdependencies.
+                    if unsafe { Gecko_FallibleAssignCString(self, other.adapt().as_ptr()) } {
+                        return Ok(());
+                    } else {
+                        return Err(());
+                    }
+                }
+                Some(up_to)
+            } else {
+                None
+            };
+            self.$impl(other_slice, old_len, num_ascii)
+        }
+    )
+}
+
+impl nsAString {
+    // Valid UTF-8 to UTF-16
+
+    // Documentation says the destination buffer needs to have
+    // as many code units as the input.
+    shrinking_conversion!(
+        fallible_append_str_impl,
+        convert_str_to_utf16,
+        &str,
+        identity
+    );
+
+    /// Convert a valid UTF-8 string into valid UTF-16 and replace the content
+    /// of this string with the conversion result.
+    pub fn assign_str(&mut self, other: &str) {
+        self.fallible_append_str_impl(other, 0)
+            .expect("Out of memory");
+    }
+
+    /// Convert a valid UTF-8 string into valid UTF-16 and fallibly replace the
+    /// content of this string with the conversion result.
+    pub fn fallible_assign_str(&mut self, other: &str) -> Result<(), ()> {
+        self.fallible_append_str_impl(other, 0)
+    }
+
+    /// Convert a valid UTF-8 string into valid UTF-16 and append the conversion
+    /// to this string.
+    pub fn append_str(&mut self, other: &str) {
+        let len = self.len();
+        self.fallible_append_str_impl(other, len)
+            .expect("Out of memory");
+    }
+
+    /// Convert a valid UTF-8 string into valid UTF-16 and fallibly append the
+    /// conversion to this string.
+    pub fn fallible_append_str(&mut self, other: &str) -> Result<(), ()> {
+        let len = self.len();
+        self.fallible_append_str_impl(other, len)
+    }
+
+    // Potentially-invalid UTF-8 to UTF-16
+
+    // Documentation says the destination buffer needs to have
+    // one more code unit than the input.
+    shrinking_conversion!(
+        fallible_append_utf8_impl,
+        convert_utf8_to_utf16,
+        &[u8],
+        plus_one
+    );
+
+    /// Convert a potentially-invalid UTF-8 string into valid UTF-16
+    /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and
+    /// replace the content of this string with the conversion result.
+    pub fn assign_utf8(&mut self, other: &[u8]) {
+        self.fallible_append_utf8_impl(other, 0)
+            .expect("Out of memory");
+    }
+
+    /// Convert a potentially-invalid UTF-8 string into valid UTF-16
+    /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and
+    /// fallibly replace the content of this string with the conversion result.
+    pub fn fallible_assign_utf8(&mut self, other: &[u8]) -> Result<(), ()> {
+        self.fallible_append_utf8_impl(other, 0)
+    }
+
+    /// Convert a potentially-invalid UTF-8 string into valid UTF-16
+    /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and
+    /// append the conversion result to this string.
+    pub fn append_utf8(&mut self, other: &[u8]) {
+        let len = self.len();
+        self.fallible_append_utf8_impl(other, len)
+            .expect("Out of memory");
+    }
+
+    /// Convert a potentially-invalid UTF-8 string into valid UTF-16
+    /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and
+    /// fallibly append the conversion result to this string.
+    pub fn fallible_append_utf8(&mut self, other: &[u8]) -> Result<(), ()> {
+        let len = self.len();
+        self.fallible_append_utf8_impl(other, len)
+    }
+
+    // Latin1 to UTF-16
+
+    constant_conversion!(fallible_append_latin1_impl, convert_latin1_to_utf16, &[u8]);
+
+    /// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!)
+    /// into UTF-16 and replace the content of this string with the conversion result.
+    pub fn assign_latin1(&mut self, other: &[u8]) {
+        self.fallible_append_latin1_impl(other, 0)
+            .expect("Out of memory");
+    }
+
+    /// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!)
+    /// into UTF-16 and fallibly replace the content of this string with the
+    /// conversion result.
+    pub fn fallible_assign_latin1(&mut self, other: &[u8]) -> Result<(), ()> {
+        self.fallible_append_latin1_impl(other, 0)
+    }
+
+    /// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!)
+    /// into UTF-16 and append the conversion result to this string.
+    pub fn append_latin1(&mut self, other: &[u8]) {
+        let len = self.len();
+        self.fallible_append_latin1_impl(other, len)
+            .expect("Out of memory");
+    }
+
+    /// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!)
+    /// into UTF-16 and fallibly append the conversion result to this string.
+    pub fn fallible_append_latin1(&mut self, other: &[u8]) -> Result<(), ()> {
+        let len = self.len();
+        self.fallible_append_latin1_impl(other, len)
+    }
+}
+
+impl nsACString {
+    // UTF-16 to UTF-8
+
+    fn fallible_append_utf16_to_utf8_impl(
+        &mut self,
+        other: &[u16],
+        old_len: usize,
+    ) -> Result<(), ()> {
+        // We first size the buffer for ASCII if the first code unit is ASCII. If that turns out not to
+        // be enough, we size for the worst case given the length of the remaining input at that point.
+        // Lexical lifetimes make this a bit messy.
+        let mut written = 0;
+        {
+            if let Some(first) = other.first() {
+                let (needed, filled, num_ascii) = if *first < 0x80 {
+                    let buffer = unsafe {
+                        self.fallible_maybe_expand_capacity(old_len
+                            .checked_add(other.len())
+                            .ok_or(())?)?
+                    };
+                    let num_ascii = copy_basic_latin_to_ascii(other, &mut buffer[old_len..]);
+                    let filled = old_len + num_ascii;
+                    let available = buffer.len() - filled;
+                    let left = other.len() - num_ascii;
+                    let needed = times_three_plus_one(left).ok_or(())?;
+                    if needed <= available {
+                        written = num_ascii +
+                            convert_utf16_to_utf8(&other[num_ascii..], &mut buffer[filled..]);
+                        (0, 0, 0)
+                    } else {
+                        (needed, filled, num_ascii)
+                    }
+                } else {
+                    let needed = times_three_plus_one(other.len()).ok_or(())?;
+                    (needed, old_len, 0)
+                };
+                if needed != 0 {
+                    let buffer = unsafe {
+                        self.fallible_maybe_expand_capacity(filled.checked_add(needed).ok_or(())?)?
+                    };
+                    written = num_ascii +
+                        convert_utf16_to_utf8(&other[num_ascii..], &mut buffer[filled..]);
+                }
+            } else {
+                return Ok(());
+            }
+        }
+        unsafe {
+            // TODO: Shrink buffer
+            self.fallible_set_length((old_len + written) as u32)?;
+        }
+        Ok(())
+    }
+
+    /// Convert a potentially-invalid UTF-16 string into valid UTF-8
+    /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and
+    /// replace the content of this string with the conversion result.
+    pub fn assign_utf16_to_utf8(&mut self, other: &[u16]) {
+        self.fallible_append_utf16_to_utf8_impl(other, 0)
+            .expect("Out of memory");
+    }
+
+    /// Convert a potentially-invalid UTF-16 string into valid UTF-8
+    /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and
+    /// fallibly replace the content of this string with the conversion result.
+    pub fn fallible_assign_utf16_to_utf8(&mut self, other: &[u16]) -> Result<(), ()> {
+        self.fallible_append_utf16_to_utf8_impl(other, 0)
+    }
+
+    /// Convert a potentially-invalid UTF-16 string into valid UTF-8
+    /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and
+    /// append the conversion result to this string.
+    pub fn append_utf16_to_utf8(&mut self, other: &[u16]) {
+        let len = self.len();
+        self.fallible_append_utf16_to_utf8_impl(other, len)
+            .expect("Out of memory");
+    }
+
+    /// Convert a potentially-invalid UTF-16 string into valid UTF-8
+    /// (replacing invalid sequences with the REPLACEMENT CHARACTER) and
+    /// fallibly append the conversion result to this string.
+    pub fn fallible_append_utf16_to_utf8(&mut self, other: &[u16]) -> Result<(), ()> {
+        let len = self.len();
+        self.fallible_append_utf16_to_utf8_impl(other, len)
+    }
+
+    // UTF-16 to Latin1
+
+    constant_conversion!(
+        fallible_append_utf16_to_latin1_lossy_impl,
+        convert_utf16_to_latin1_lossy,
+        &[u16]
+    );
+
+    /// Convert a UTF-16 string whose all code points are below U+0100 into
+    /// a Latin1 (scalar value is byte value; not windows-1252!) string and
+    /// replace the content of this string with the conversion result.
+    ///
+    /// # Panics
+    ///
+    /// If the input contains code points above U+00FF or is not valid UTF-16,
+    /// panics in debug mode and produces garbage in a memory-safe way in
+    /// release builds. The nature of the garbage may differ based on CPU
+    /// architecture and must not be relied upon.
+    pub fn assign_utf16_to_latin1_lossy(&mut self, other: &[u16]) {
+        self.fallible_append_utf16_to_latin1_lossy_impl(other, 0)
+            .expect("Out of memory");
+    }
+
+    /// Convert a UTF-16 string whose all code points are below U+0100 into
+    /// a Latin1 (scalar value is byte value; not windows-1252!) string and
+    /// fallibly replace the content of this string with the conversion result.
+    ///
+    /// # Panics
+    ///
+    /// If the input contains code points above U+00FF or is not valid UTF-16,
+    /// panics in debug mode and produces garbage in a memory-safe way in
+    /// release builds. The nature of the garbage may differ based on CPU
+    /// architecture and must not be relied upon.
+    pub fn fallible_assign_utf16_to_latin1_lossy(&mut self, other: &[u16]) -> Result<(), ()> {
+        self.fallible_append_utf16_to_latin1_lossy_impl(other, 0)
+    }
+
+    /// Convert a UTF-16 string whose all code points are below U+0100 into
+    /// a Latin1 (scalar value is byte value; not windows-1252!) string and
+    /// append the conversion result to this string.
+    ///
+    /// # Panics
+    ///
+    /// If the input contains code points above U+00FF or is not valid UTF-16,
+    /// panics in debug mode and produces garbage in a memory-safe way in
+    /// release builds. The nature of the garbage may differ based on CPU
+    /// architecture and must not be relied upon.
+    pub fn append_utf16_to_latin1_lossy(&mut self, other: &[u16]) {
+        let len = self.len();
+        self.fallible_append_utf16_to_latin1_lossy_impl(other, len)
+            .expect("Out of memory");
+    }
+
+    /// Convert a UTF-16 string whose all code points are below U+0100 into
+    /// a Latin1 (scalar value is byte value; not windows-1252!) string and
+    /// fallibly append the conversion result to this string.
+    ///
+    /// # Panics
+    ///
+    /// If the input contains code points above U+00FF or is not valid UTF-16,
+    /// panics in debug mode and produces garbage in a memory-safe way in
+    /// release builds. The nature of the garbage may differ based on CPU
+    /// architecture and must not be relied upon.
+    pub fn fallible_append_utf16_to_latin1_lossy(&mut self, other: &[u16]) -> Result<(), ()> {
+        let len = self.len();
+        self.fallible_append_utf16_to_latin1_lossy_impl(other, len)
+    }
+
+    // UTF-8 to Latin1
+
+    ascii_copy_avoidance!(
+        fallible_append_utf8_to_latin1_lossy_check,
+        fallible_append_utf8_to_latin1_lossy_impl,
+        nsCStringLike
+    );
+
+    fn fallible_append_utf8_to_latin1_lossy_impl(
+        &mut self,
+        other: &[u8],
+        old_len: usize,
+        maybe_num_ascii: Option<usize>,
+    ) -> Result<(), ()> {
+        let num_ascii = maybe_num_ascii.unwrap_or(0);
+        // This may overflow, but if overflow happens here, an overflow also happens where checked.
+        let old_len_plus_num_ascii = old_len + num_ascii;
+        let written = {
+            let buffer = unsafe {
+                self.fallible_maybe_expand_capacity(old_len.checked_add(other.len()).ok_or(())?)?
+            };
+            if num_ascii != 0 {
+                (&mut buffer[old_len..old_len_plus_num_ascii]).copy_from_slice(&other[..num_ascii]);
+            }
+            convert_utf8_to_latin1_lossy(&other[num_ascii..], &mut buffer[old_len_plus_num_ascii..])
+        };
+        unsafe {
+            // TODO: Shrink buffer
+            self.fallible_set_length((old_len_plus_num_ascii + written) as u32)?;
+        }
+        Ok(())
+    }
+
+    /// Convert a UTF-8 string whose all code points are below U+0100 into
+    /// a Latin1 (scalar value is byte value; not windows-1252!) string and
+    /// replace the content of this string with the conversion result.
+    ///
+    /// # Panics
+    ///
+    /// If the input contains code points above U+00FF or is not valid UTF-8,
+    /// panics in debug mode and produces garbage in a memory-safe way in
+    /// release builds. The nature of the garbage may differ based on CPU
+    /// architecture and must not be relied upon.
+    pub fn assign_utf8_to_latin1_lossy<T: nsCStringLike + ?Sized>(&mut self, other: &T) {
+        self.fallible_append_utf8_to_latin1_lossy_check(other, 0)
+            .expect("Out of memory");
+    }
+
+    /// Convert a UTF-8 string whose all code points are below U+0100 into
+    /// a Latin1 (scalar value is byte value; not windows-1252!) string and
+    /// fallibly replace the content of this string with the conversion result.
+    ///
+    /// # Panics
+    ///
+    /// If the input contains code points above U+00FF or is not valid UTF-8,
+    /// panics in debug mode and produces garbage in a memory-safe way in
+    /// release builds. The nature of the garbage may differ based on CPU
+    /// architecture and must not be relied upon.
+    pub fn fallible_assign_utf8_to_latin1_lossy<T: nsCStringLike + ?Sized>(
+        &mut self,
+        other: &T,
+    ) -> Result<(), ()> {
+        self.fallible_append_utf8_to_latin1_lossy_check(other, 0)
+    }
+
+    /// Convert a UTF-8 string whose all code points are below U+0100 into
+    /// a Latin1 (scalar value is byte value; not windows-1252!) string and
+    /// append the conversion result to this string.
+    ///
+    /// # Panics
+    ///
+    /// If the input contains code points above U+00FF or is not valid UTF-8,
+    /// panics in debug mode and produces garbage in a memory-safe way in
+    /// release builds. The nature of the garbage may differ based on CPU
+    /// architecture and must not be relied upon.
+    pub fn append_utf8_to_latin1_lossy<T: nsCStringLike + ?Sized>(&mut self, other: &T) {
+        let len = self.len();
+        self.fallible_append_utf8_to_latin1_lossy_check(other, len)
+            .expect("Out of memory");
+    }
+
+    /// Convert a UTF-8 string whose all code points are below U+0100 into
+    /// a Latin1 (scalar value is byte value; not windows-1252!) string and
+    /// fallibly append the conversion result to this string.
+    ///
+    /// # Panics
+    ///
+    /// If the input contains code points above U+00FF or is not valid UTF-8,
+    /// panics in debug mode and produces garbage in a memory-safe way in
+    /// release builds. The nature of the garbage may differ based on CPU
+    /// architecture and must not be relied upon.
+    pub fn fallible_append_utf8_to_latin1_lossy<T: nsCStringLike + ?Sized>(
+        &mut self,
+        other: &T,
+    ) -> Result<(), ()> {
+        let len = self.len();
+        self.fallible_append_utf8_to_latin1_lossy_check(other, len)
+    }
+
+    // Latin1 to UTF-8 CString
+
+    ascii_copy_avoidance!(
+        fallible_append_latin1_to_utf8_check,
+        fallible_append_latin1_to_utf8_impl,
+        Latin1StringLike
+    );
+
+    fn fallible_append_latin1_to_utf8_impl(
+        &mut self,
+        other: &[u8],
+        old_len: usize,
+        maybe_num_ascii: Option<usize>,
+    ) -> Result<(), ()> {
+        // We first size the buffer for ASCII. If that turns out not to be enough, we size for the worst
+        // case given the length of the remaining input at that point. Lexical lifetimes make this a bit
+        // messy.
+        let mut written = 0;
+        {
+            let (needed, filled, num_ascii) = {
+                if let Some(num_ascii) = maybe_num_ascii {
+                    let filled = old_len + num_ascii;
+                    let left = other.len() - num_ascii;
+                    let needed = left.checked_mul(2).ok_or(())?;
+                    (needed, filled, num_ascii)
+                } else {
+                    let buffer = unsafe {
+                        self.fallible_maybe_expand_capacity(old_len
+                            .checked_add(other.len())
+                            .ok_or(())?)?
+                    };
+                    let num_ascii = copy_ascii_to_ascii(other, &mut buffer[old_len..]);
+                    let filled = old_len + num_ascii;
+                    let available = buffer.len() - filled;
+                    let left = other.len() - num_ascii;
+                    let needed = left.checked_mul(2).ok_or(())?;
+                    if needed <= available {
+                        written = num_ascii +
+                            convert_latin1_to_utf8(&other[num_ascii..], &mut buffer[filled..]);
+                        (0, 0, 0)
+                    } else {
+                        (needed, filled, num_ascii)
+                    }
+                }
+            };
+            if needed != 0 {
+                let buffer = unsafe {
+                    self.fallible_maybe_expand_capacity(filled.checked_add(needed).ok_or(())?)?
+                };
+                written =
+                    num_ascii + convert_latin1_to_utf8(&other[num_ascii..], &mut buffer[filled..]);
+            }
+        }
+        unsafe {
+            // TODO: Shrink buffer
+            self.fallible_set_length((old_len + written) as u32)?;
+        }
+        Ok(())
+    }
+
+    /// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!)
+    /// into UTF-8 and replace the content of this string with the conversion result.
+    pub fn assign_latin1_to_utf8<T: Latin1StringLike + ?Sized>(&mut self, other: &T) {
+        self.fallible_append_latin1_to_utf8_check(other, 0)
+            .expect("Out of memory");
+    }
+
+    /// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!)
+    /// into UTF-8 and fallibly replace the content of this string with the
+    /// conversion result.
+    pub fn fallible_assign_latin1_to_utf8<T: Latin1StringLike + ?Sized>(
+        &mut self,
+        other: &T,
+    ) -> Result<(), ()> {
+        self.fallible_append_latin1_to_utf8_check(other, 0)
+    }
+
+    /// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!)
+    /// into UTF-8 and append the conversion result to this string.
+    pub fn append_latin1_to_utf8<T: Latin1StringLike + ?Sized>(&mut self, other: &T) {
+        let len = self.len();
+        self.fallible_append_latin1_to_utf8_check(other, len)
+            .expect("Out of memory");
+    }
+
+    /// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!)
+    /// into UTF-8 and fallibly append the conversion result to this string.
+    pub fn fallible_append_latin1_to_utf8<T: Latin1StringLike + ?Sized>(
+        &mut self,
+        other: &T,
+    ) -> Result<(), ()> {
+        let len = self.len();
+        self.fallible_append_latin1_to_utf8_check(other, len)
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn nsstring_fallible_append_utf8_impl(
+    this: *mut nsAString,
+    other: *const u8,
+    other_len: usize,
+    old_len: usize,
+) -> bool {
+    let other_slice = ::std::slice::from_raw_parts(other, other_len);
+    (*this)
+        .fallible_append_utf8_impl(other_slice, old_len)
+        .is_ok()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn nsstring_fallible_append_latin1_impl(
+    this: *mut nsAString,
+    other: *const u8,
+    other_len: usize,
+    old_len: usize,
+) -> bool {
+    let other_slice = ::std::slice::from_raw_parts(other, other_len);
+    (*this)
+        .fallible_append_latin1_impl(other_slice, old_len)
+        .is_ok()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn nscstring_fallible_append_utf16_to_utf8_impl(
+    this: *mut nsACString,
+    other: *const u16,
+    other_len: usize,
+    old_len: usize,
+) -> bool {
+    let other_slice = ::std::slice::from_raw_parts(other, other_len);
+    (*this)
+        .fallible_append_utf16_to_utf8_impl(other_slice, old_len)
+        .is_ok()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn nscstring_fallible_append_utf16_to_latin1_lossy_impl(
+    this: *mut nsACString,
+    other: *const u16,
+    other_len: usize,
+    old_len: usize,
+) -> bool {
+    let other_slice = ::std::slice::from_raw_parts(other, other_len);
+    (*this)
+        .fallible_append_utf16_to_latin1_lossy_impl(other_slice, old_len)
+        .is_ok()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn nscstring_fallible_append_utf8_to_latin1_lossy_check(
+    this: *mut nsACString,
+    other: *const nsACString,
+    old_len: usize,
+) -> bool {
+    (*this)
+        .fallible_append_utf8_to_latin1_lossy_check(&*other, old_len)
+        .is_ok()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn nscstring_fallible_append_latin1_to_utf8_check(
+    this: *mut nsACString,
+    other: *const nsACString,
+    old_len: usize,
+) -> bool {
+    (*this)
+        .fallible_append_latin1_to_utf8_check(&*other, old_len)
+        .is_ok()
+}
--- a/servo/support/gecko/nsstring/src/lib.rs
+++ b/servo/support/gecko/nsstring/src/lib.rs
@@ -123,16 +123,25 @@ use std::fmt;
 use std::marker::PhantomData;
 use std::mem;
 use std::ops::{Deref, DerefMut};
 use std::os::raw::c_void;
 use std::slice;
 use std::str;
 use std::u32;
 
+mod conversions;
+
+pub use self::conversions::nsstring_fallible_append_utf8_impl;
+pub use self::conversions::nsstring_fallible_append_latin1_impl;
+pub use self::conversions::nscstring_fallible_append_utf16_to_utf8_impl;
+pub use self::conversions::nscstring_fallible_append_utf16_to_latin1_lossy_impl;
+pub use self::conversions::nscstring_fallible_append_utf8_to_latin1_lossy_check;
+pub use self::conversions::nscstring_fallible_append_latin1_to_utf8_check;
+
 ///////////////////////////////////
 // Internal Implementation Flags //
 ///////////////////////////////////
 
 mod data_flags {
     bitflags! {
         // While this has the same layout as u16, it cannot be passed
         // over FFI safely as a u16.
@@ -163,16 +172,91 @@ mod class_flags {
 
 use class_flags::ClassFlags;
 use data_flags::DataFlags;
 
 ////////////////////////////////////
 // Generic String Bindings Macros //
 ////////////////////////////////////
 
+macro_rules! string_like {
+    {
+        char_t = $char_t: ty;
+
+        AString = $AString: ident;
+        String = $String: ident;
+        Str = $Str: ident;
+
+        StringLike = $StringLike: ident;
+        StringAdapter = $StringAdapter: ident;
+    } => {
+        /// This trait is implemented on types which are `ns[C]String`-like, in
+        /// that they can at very low cost be converted to a borrowed
+        /// `&nsA[C]String`. Unfortunately, the intermediate type
+        /// `ns[C]StringAdapter` is required as well due to types like `&[u8]`
+        /// needing to be (cheaply) wrapped in a `nsCString` on the stack to
+        /// create the `&nsACString`.
+        ///
+        /// This trait is used to DWIM when calling the methods on
+        /// `nsA[C]String`.
+        pub trait $StringLike {
+            fn adapt(&self) -> $StringAdapter;
+        }
+
+        impl<'a, T: $StringLike + ?Sized> $StringLike for &'a T {
+            fn adapt(&self) -> $StringAdapter {
+                <T as $StringLike>::adapt(*self)
+            }
+        }
+
+        impl<'a, T> $StringLike for borrow::Cow<'a, T>
+            where T: $StringLike + borrow::ToOwned + ?Sized {
+            fn adapt(&self) -> $StringAdapter {
+                <T as $StringLike>::adapt(self.as_ref())
+            }
+        }
+
+        impl $StringLike for $AString {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Abstract(self)
+            }
+        }
+
+        impl<'a> $StringLike for $Str<'a> {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Abstract(self)
+            }
+        }
+
+        impl $StringLike for $String {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Abstract(self)
+            }
+        }
+
+        impl $StringLike for [$char_t] {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Borrowed($Str::from(self))
+            }
+        }
+
+        impl $StringLike for Vec<$char_t> {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Borrowed($Str::from(&self[..]))
+            }
+        }
+
+        impl $StringLike for Box<[$char_t]> {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Borrowed($Str::from(&self[..]))
+            }
+        }
+    }
+}
+
 macro_rules! define_string_types {
     {
         char_t = $char_t: ty;
 
         AString = $AString: ident;
         String = $String: ident;
         Str = $Str: ident;
 
@@ -182,16 +266,17 @@ macro_rules! define_string_types {
         StringRepr = $StringRepr: ident;
 
         drop = $drop: ident;
         assign = $assign: ident, $fallible_assign: ident;
         take_from = $take_from: ident, $fallible_take_from: ident;
         append = $append: ident, $fallible_append: ident;
         set_length = $set_length: ident, $fallible_set_length: ident;
         begin_writing = $begin_writing: ident, $fallible_begin_writing: ident;
+        fallible_maybe_expand_capacity = $fallible_maybe_expand_capacity: ident;
     } => {
         /// The representation of a ns[C]String type in C++. This type is
         /// used internally by our definition of ns[C]String to ensure layout
         /// compatibility with the C++ ns[C]String type.
         ///
         /// This type may also be used in place of a C++ ns[C]String inside of
         /// struct definitions which are shared with C++, as it has identical
         /// layout to our ns[C]String type.
@@ -371,16 +456,43 @@ macro_rules! define_string_types {
                             Err(())
                         } else {
                             Ok(slice::from_raw_parts_mut(ptr, len))
                         }
                     }
                 }
             }
 
+            /// Unshares the buffer of the string, sets the capacity to the
+            /// allocation size resulting from rounding up `len`. Set the
+            /// length of the string to the rounded-up capacity and returns
+            /// the buffer as a mutable slice.
+            ///
+            /// Fails also if the new length doesn't fit in 32 bits.
+            ///
+            /// # Safety
+            ///
+            /// Unsafe because of exposure of uninitialized memory.
+            unsafe fn fallible_maybe_expand_capacity(&mut self, len: usize) -> Result<&mut [$char_t], ()> {
+                if len == 0 {
+                    self.fallible_set_length(0)?;
+                    // Use an arbitrary non-null value as the pointer
+                    Ok(slice::from_raw_parts_mut(0x1 as *mut $char_t, 0))
+                } else if len > u32::max_value() as usize {
+                    Err(())
+                } else {
+                    let mut len32 = len as u32;
+                    let ptr = $fallible_maybe_expand_capacity(self, &mut len32);
+                    if ptr.is_null() {
+                        Err(())
+                    } else {
+                        Ok(slice::from_raw_parts_mut(ptr, len32 as usize))
+                    }
+                }
+            }
         }
 
         impl Deref for $AString {
             type Target = [$char_t];
             fn deref(&self) -> &[$char_t] {
                 unsafe {
                     // All $AString values point to a struct prefix which is
                     // identical to $StringRepr, this we can transmute `self`
@@ -713,76 +825,35 @@ macro_rules! define_string_types {
             fn deref(&self) -> &$AString {
                 match *self {
                     $StringAdapter::Borrowed(ref s) => s,
                     $StringAdapter::Abstract(ref s) => s,
                 }
             }
         }
 
-        /// This trait is implemented on types which are `ns[C]String`-like, in
-        /// that they can at very low cost be converted to a borrowed
-        /// `&nsA[C]String`. Unfortunately, the intermediate type
-        /// `ns[C]StringAdapter` is required as well due to types like `&[u8]`
-        /// needing to be (cheaply) wrapped in a `nsCString` on the stack to
-        /// create the `&nsACString`.
-        ///
-        /// This trait is used to DWIM when calling the methods on
-        /// `nsA[C]String`.
-        pub trait $StringLike {
-            fn adapt(&self) -> $StringAdapter;
-        }
-
-        impl<'a, T: $StringLike + ?Sized> $StringLike for &'a T {
-            fn adapt(&self) -> $StringAdapter {
-                <T as $StringLike>::adapt(*self)
-            }
-        }
-
-        impl<'a, T> $StringLike for borrow::Cow<'a, T>
-            where T: $StringLike + borrow::ToOwned + ?Sized {
-            fn adapt(&self) -> $StringAdapter {
-                <T as $StringLike>::adapt(self.as_ref())
+        impl<'a> $StringAdapter<'a> {
+            #[allow(dead_code)]
+            fn is_abstract(&self) -> bool {
+                match *self {
+                    $StringAdapter::Borrowed(_) => false,
+                    $StringAdapter::Abstract(_) => true,
+                }
             }
         }
 
-        impl $StringLike for $AString {
-            fn adapt(&self) -> $StringAdapter {
-                $StringAdapter::Abstract(self)
-            }
-        }
-
-        impl<'a> $StringLike for $Str<'a> {
-            fn adapt(&self) -> $StringAdapter {
-                $StringAdapter::Abstract(self)
-            }
-        }
+        string_like! {
+            char_t = $char_t;
 
-        impl $StringLike for $String {
-            fn adapt(&self) -> $StringAdapter {
-                $StringAdapter::Abstract(self)
-            }
-        }
+            AString = $AString;
+            String = $String;
+            Str = $Str;
 
-        impl $StringLike for [$char_t] {
-            fn adapt(&self) -> $StringAdapter {
-                $StringAdapter::Borrowed($Str::from(self))
-            }
-        }
-
-        impl $StringLike for Vec<$char_t> {
-            fn adapt(&self) -> $StringAdapter {
-                $StringAdapter::Borrowed($Str::from(&self[..]))
-            }
-        }
-
-        impl $StringLike for Box<[$char_t]> {
-            fn adapt(&self) -> $StringAdapter {
-                $StringAdapter::Borrowed($Str::from(&self[..]))
-            }
+            StringLike = $StringLike;
+            StringAdapter = $StringAdapter;
         }
     }
 }
 
 ///////////////////////////////////////////
 // Bindings for nsCString (u8 char type) //
 ///////////////////////////////////////////
 
@@ -799,43 +870,20 @@ define_string_types! {
     StringRepr = nsCStringRepr;
 
     drop = Gecko_FinalizeCString;
     assign = Gecko_AssignCString, Gecko_FallibleAssignCString;
     take_from = Gecko_TakeFromCString, Gecko_FallibleTakeFromCString;
     append = Gecko_AppendCString, Gecko_FallibleAppendCString;
     set_length = Gecko_SetLengthCString, Gecko_FallibleSetLengthCString;
     begin_writing = Gecko_BeginWritingCString, Gecko_FallibleBeginWritingCString;
+    fallible_maybe_expand_capacity = Gecko_FallibleMaybeExpandCapacityCString;
 }
 
 impl nsACString {
-    pub fn assign_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) {
-        self.truncate();
-        self.append_utf16(other);
-    }
-
-    pub fn fallible_assign_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
-        self.truncate();
-        self.fallible_append_utf16(other)
-    }
-
-    pub fn append_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) {
-        unsafe {
-            Gecko_AppendUTF16toCString(self, other.adapt().as_ptr());
-        }
-    }
-
-    pub fn fallible_append_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
-        if unsafe { Gecko_FallibleAppendUTF16toCString(self, other.adapt().as_ptr()) } {
-            Ok(())
-        } else {
-            Err(())
-        }
-    }
-
     pub unsafe fn as_str_unchecked(&self) -> &str {
         str::from_utf8_unchecked(self)
     }
 }
 
 impl<'a> From<&'a str> for nsCStr<'a> {
     fn from(s: &'a str) -> nsCStr<'a> {
         s.as_bytes().into()
@@ -911,16 +959,33 @@ impl nsCStringLike for String {
 }
 
 impl nsCStringLike for Box<str> {
     fn adapt(&self) -> nsCStringAdapter {
         nsCStringAdapter::Borrowed(nsCStr::from(&self[..]))
     }
 }
 
+/// This trait is implemented on types which are Latin1 `nsCString`-like,
+/// in that they can at very low cost be converted to a borrowed
+/// `&nsACString` and do not denote UTF-8ness in the Rust type system.
+///
+/// This trait is used to DWIM when calling the methods on
+/// `nsACString`.
+string_like! {
+    char_t = u8;
+
+    AString = nsACString;
+    String = nsCString;
+    Str = nsCStr;
+
+    StringLike = Latin1StringLike;
+    StringAdapter = nsCStringAdapter;
+}
+
 ///////////////////////////////////////////
 // Bindings for nsString (u16 char type) //
 ///////////////////////////////////////////
 
 define_string_types! {
     char_t = u16;
 
     AString = nsAString;
@@ -933,42 +998,17 @@ define_string_types! {
     StringRepr = nsStringRepr;
 
     drop = Gecko_FinalizeString;
     assign = Gecko_AssignString, Gecko_FallibleAssignString;
     take_from = Gecko_TakeFromString, Gecko_FallibleTakeFromString;
     append = Gecko_AppendString, Gecko_FallibleAppendString;
     set_length = Gecko_SetLengthString, Gecko_FallibleSetLengthString;
     begin_writing = Gecko_BeginWritingString, Gecko_FallibleBeginWritingString;
-}
-
-impl nsAString {
-    pub fn assign_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) {
-        self.truncate();
-        self.append_utf8(other);
-    }
-
-    pub fn fallible_assign_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
-        self.truncate();
-        self.fallible_append_utf8(other)
-    }
-
-    pub fn append_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) {
-        unsafe {
-            Gecko_AppendUTF8toString(self, other.adapt().as_ptr());
-        }
-    }
-
-    pub fn fallible_append_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
-        if unsafe { Gecko_FallibleAppendUTF8toString(self, other.adapt().as_ptr()) } {
-            Ok(())
-        } else {
-            Err(())
-        }
-    }
+    fallible_maybe_expand_capacity = Gecko_FallibleMaybeExpandCapacityString;
 }
 
 // NOTE: The From impl for a string slice for nsString produces a <'static>
 // lifetime, as it allocates.
 impl<'a> From<&'a str> for nsString {
     fn from(s: &'a str) -> nsString {
         s.encode_utf16().collect::<Vec<u16>>().into()
     }
@@ -980,17 +1020,17 @@ impl<'a> From<&'a String> for nsString {
     }
 }
 
 // Support for the write!() macro for writing to nsStrings
 impl fmt::Write for nsAString {
     fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
         // Directly invoke gecko's routines for appending utf8 strings to
         // nsAString values, to avoid as much overhead as possible
-        self.append_utf8(s);
+        self.append_str(s);
         Ok(())
     }
 }
 
 impl fmt::Display for nsAString {
     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
         fmt::Display::fmt(&String::from_utf16_lossy(&self[..]), f)
     }
@@ -1024,35 +1064,31 @@ extern "C" {
     fn Gecko_AppendCString(this: *mut nsACString, other: *const nsACString);
     fn Gecko_SetLengthCString(this: *mut nsACString, length: u32);
     fn Gecko_BeginWritingCString(this: *mut nsACString) -> *mut u8;
     fn Gecko_FallibleAssignCString(this: *mut nsACString, other: *const nsACString) -> bool;
     fn Gecko_FallibleTakeFromCString(this: *mut nsACString, other: *mut nsACString) -> bool;
     fn Gecko_FallibleAppendCString(this: *mut nsACString, other: *const nsACString) -> bool;
     fn Gecko_FallibleSetLengthCString(this: *mut nsACString, length: u32) -> bool;
     fn Gecko_FallibleBeginWritingCString(this: *mut nsACString) -> *mut u8;
+    fn Gecko_FallibleMaybeExpandCapacityCString(this: *mut nsACString, length: *mut u32) -> *mut u8;
 
     fn Gecko_FinalizeString(this: *mut nsAString);
 
     fn Gecko_AssignString(this: *mut nsAString, other: *const nsAString);
     fn Gecko_TakeFromString(this: *mut nsAString, other: *mut nsAString);
     fn Gecko_AppendString(this: *mut nsAString, other: *const nsAString);
     fn Gecko_SetLengthString(this: *mut nsAString, length: u32);
     fn Gecko_BeginWritingString(this: *mut nsAString) -> *mut u16;
     fn Gecko_FallibleAssignString(this: *mut nsAString, other: *const nsAString) -> bool;
     fn Gecko_FallibleTakeFromString(this: *mut nsAString, other: *mut nsAString) -> bool;
     fn Gecko_FallibleAppendString(this: *mut nsAString, other: *const nsAString) -> bool;
     fn Gecko_FallibleSetLengthString(this: *mut nsAString, length: u32) -> bool;
     fn Gecko_FallibleBeginWritingString(this: *mut nsAString) -> *mut u16;
-
-    // Gecko implementation in nsReadableUtils.cpp
-    fn Gecko_AppendUTF16toCString(this: *mut nsACString, other: *const nsAString);
-    fn Gecko_AppendUTF8toString(this: *mut nsAString, other: *const nsACString);
-    fn Gecko_FallibleAppendUTF16toCString(this: *mut nsACString, other: *const nsAString) -> bool;
-    fn Gecko_FallibleAppendUTF8toString(this: *mut nsAString, other: *const nsACString) -> bool;
+    fn Gecko_FallibleMaybeExpandCapacityString(this: *mut nsAString, length: *mut u32) -> *mut u16;
 }
 
 //////////////////////////////////////
 // Repr Validation Helper Functions //
 //////////////////////////////////////
 
 pub mod test_helpers {
     //! This module only exists to help with ensuring that the layout of the
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -681,17 +681,17 @@ nsWindowWatcher::OpenWindowInternal(mozI
     if (NS_FAILED(rv)) {
       return rv;
     }
     uriToLoad->SchemeIs("chrome", &uriToLoadIsChrome);
   }
 
   bool nameSpecified = false;
   if (aName) {
-    CopyUTF8toUTF16(aName, name);
+    CopyUTF8toUTF16(MakeStringSpan(aName), name);
     nameSpecified = true;
   } else {
     name.SetIsVoid(true);
   }
 
   if (aFeatures) {
     features.Assign(aFeatures);
     features.StripWhitespace();
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -2228,17 +2228,17 @@ ShowProfileManager(nsIToolkitProfileServ
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = lock->GetLocalDirectory(getter_AddRefs(profLD));
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = ioParamBlock->GetString(0, &profileNamePtr);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      CopyUTF16toUTF8(profileNamePtr, profileName);
+      CopyUTF16toUTF8(MakeStringSpan(profileNamePtr), profileName);
       free(profileNamePtr);
 
       lock->Unlock();
     }
   }
 
   SaveFileToEnv("XRE_PROFILE_PATH", profD);
   SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD);
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -2800,17 +2800,17 @@ NS_IMETHODIMP nsExternalHelperAppService
   nsAutoCString fileExt;
   if (!fileName.IsEmpty())
   {
     int32_t len = fileName.Length(); 
     for (int32_t i = len; i >= 0; i--) 
     {
       if (fileName[i] == char16_t('.'))
       {
-        CopyUTF16toUTF8(fileName.get() + i + 1, fileExt);
+        CopyUTF16toUTF8(Substring(fileName, i + 1), fileExt);
         break;
       }
     }
   }
 
   if (fileExt.IsEmpty())
     return NS_ERROR_FAILURE;
 
--- a/widget/gtk/IMContextWrapper.cpp
+++ b/widget/gtk/IMContextWrapper.cpp
@@ -1724,17 +1724,17 @@ IMContextWrapper::GetCompositionString(G
                                        nsAString& aCompositionString)
 {
     gchar *preedit_string;
     gint cursor_pos;
     PangoAttrList *feedback_list;
     gtk_im_context_get_preedit_string(aContext, &preedit_string,
                                       &feedback_list, &cursor_pos);
     if (preedit_string && *preedit_string) {
-        CopyUTF8toUTF16(preedit_string, aCompositionString);
+        CopyUTF8toUTF16(MakeStringSpan(preedit_string), aCompositionString);
     } else {
         aCompositionString.Truncate();
     }
 
     MOZ_LOG(gGtkIMLog, LogLevel::Info,
         ("0x%p GetCompositionString(aContext=0x%p), "
          "aCompositionString=\"%s\"",
          this, aContext, preedit_string));
--- a/widget/gtk/nsDeviceContextSpecG.cpp
+++ b/widget/gtk/nsDeviceContextSpecG.cpp
@@ -416,17 +416,17 @@ nsPrinterEnumeratorGTK::InitPrintSetting
   nsresult rv = aPrintSettings->GetToFileName(filename);
   if (NS_FAILED(rv) || filename.IsEmpty()) {
     const char* path = PR_GetEnv("PWD");
     if (!path) {
       path = PR_GetEnv("HOME");
     }
 
     if (path) {
-      CopyUTF8toUTF16(path, filename);
+      CopyUTF8toUTF16(MakeStringSpan(path), filename);
       filename.AppendLiteral("/mozilla.pdf");
     } else {
       filename.AssignLiteral("mozilla.pdf");
     }
 
     DO_PR_DEBUG_LOG(("Setting default filename to '%s'\n",
                      NS_ConvertUTF16toUTF8(filename).get()));
     aPrintSettings->SetToFileName(filename);
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -3241,21 +3241,18 @@ case _value: eventName.AssignLiteral(_na
     _ASSIGN_eventName(eReadyStateChange,"eReadyStateChange");
     _ASSIGN_eventName(eXULBroadcast, "eXULBroadcast");
     _ASSIGN_eventName(eXULCommandUpdate, "eXULCommandUpdate");
 
 #undef _ASSIGN_eventName
 
   default:
     {
-      char buf[32];
-
-      SprintfLiteral(buf,"UNKNOWN: %d",aGuiEvent->mMessage);
-
-      CopyASCIItoUTF16(buf, eventName);
+      eventName.AssignLiteral("UNKNOWN: ");
+      eventName.AppendInt(aGuiEvent->mMessage);
     }
     break;
   }
 
   return nsAutoString(eventName);
 }
 //////////////////////////////////////////////////////////////
 //
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -1630,17 +1630,17 @@ CycleCollectedJSRuntime::ErrorIntercepto
   // work, we want to avoid that complex use case.
   // Fortunately, we have already checked above that `exn` is an exception object,
   // so nothing such should happen.
   nsContentUtils::ExtractErrorValues(cx, value, details.mFilename, &details.mLine, &details.mColumn, details.mMessage);
 
   nsAutoCString stack;
   JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, /* showArgs = */ false, /* showLocals = */ false, /* showThisProps = */ false);
   stack.Append(buf.get());
-  CopyUTF8toUTF16(buf.get(), details.mStack);
+  CopyUTF8toUTF16(mozilla::MakeStringSpan(buf.get()), details.mStack);
 
   mThrownError.emplace(Move(details));
 }
 
 void
 CycleCollectedJSRuntime::ClearRecentDevError()
 {
   mErrorInterceptor.mThrownError.reset();
--- a/xpcom/base/nsAlgorithm.h
+++ b/xpcom/base/nsAlgorithm.h
@@ -54,22 +54,9 @@ NS_COUNT(InputIterator& aFirst, const In
   uint32_t result = 0;
   for (; aFirst != aLast; ++aFirst)
     if (*aFirst == aValue) {
       ++result;
     }
   return result;
 }
 
-template <class InputIterator, class OutputIterator>
-inline OutputIterator&
-copy_string(const InputIterator& aFirst, const InputIterator& aLast,
-            OutputIterator& aResult)
-{
-  typedef nsCharSourceTraits<InputIterator> source_traits;
-  typedef nsCharSinkTraits<OutputIterator>  sink_traits;
-
-  sink_traits::write(aResult, source_traits::read(aFirst),
-                     source_traits::readable_distance(aFirst, aLast));
-  return aResult;
-}
-
 #endif // !defined(nsAlgorithm_h___)
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -199,28 +199,22 @@ struct AtomTableKey
     : mUTF16String(aUTF16String)
     , mUTF8String(nullptr)
     , mLength(aLength)
   {
     mHash = HashString(mUTF16String, mLength);
     *aHashOut = mHash;
   }
 
-  AtomTableKey(const char* aUTF8String, uint32_t aLength, uint32_t* aHashOut)
+  AtomTableKey(const char* aUTF8String, uint32_t aLength, uint32_t* aHashOut, bool* aErr)
     : mUTF16String(nullptr)
     , mUTF8String(aUTF8String)
     , mLength(aLength)
   {
-    bool err;
-    mHash = HashUTF8AsUTF16(mUTF8String, mLength, &err);
-    if (err) {
-      mUTF8String = nullptr;
-      mLength = 0;
-      mHash = 0;
-    }
+    mHash = HashUTF8AsUTF16(mUTF8String, mLength, aErr);
     *aHashOut = mHash;
   }
 
   const char16_t* mUTF16String;
   const char* mUTF8String;
   uint32_t mLength;
   uint32_t mHash;
 };
@@ -336,20 +330,21 @@ AtomTableGetHash(const void* aKey)
 
 static bool
 AtomTableMatchKey(const PLDHashEntryHdr* aEntry, const void* aKey)
 {
   const AtomTableEntry* he = static_cast<const AtomTableEntry*>(aEntry);
   const AtomTableKey* k = static_cast<const AtomTableKey*>(aKey);
 
   if (k->mUTF8String) {
+    bool err = false;
     return
-      CompareUTF8toUTF16(nsDependentCSubstring(k->mUTF8String,
+      (CompareUTF8toUTF16(nsDependentCSubstring(k->mUTF8String,
                                                k->mUTF8String + k->mLength),
-                         nsDependentAtomString(he->mAtom)) == 0;
+                         nsDependentAtomString(he->mAtom), &err) == 0) && !err;
   }
 
   return he->mAtom->Equals(k->mUTF16String, k->mLength);
 }
 
 void
 nsAtomTable::AtomTableClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
 {
@@ -696,17 +691,26 @@ NS_Atomize(const char* aUTF8String)
   MOZ_ASSERT(gAtomTable);
   return gAtomTable->Atomize(nsDependentCString(aUTF8String));
 }
 
 already_AddRefed<nsAtom>
 nsAtomTable::Atomize(const nsACString& aUTF8String)
 {
   uint32_t hash;
-  AtomTableKey key(aUTF8String.Data(), aUTF8String.Length(), &hash);
+  bool err;
+  AtomTableKey key(aUTF8String.Data(), aUTF8String.Length(), &hash, &err);
+  if (MOZ_UNLIKELY(err)) {
+    MOZ_ASSERT_UNREACHABLE("Tried to atomize invalid UTF-8.");
+    // The input was invalid UTF-8. Let's replace the errors with U+FFFD
+    // and atomize the result.
+    nsString str;
+    CopyUTF8toUTF16(aUTF8String, str);
+    return Atomize(str);
+  }
   nsAtomSubTable& table = SelectSubTable(key);
   MutexAutoLock lock(table.mLock);
   AtomTableEntry* he = table.Add(key);
 
   if (he->mAtom) {
     RefPtr<nsAtom> atom = he->mAtom;
 
     return atom.forget();
--- a/xpcom/ds/nsVariant.cpp
+++ b/xpcom/ds/nsVariant.cpp
@@ -824,17 +824,17 @@ nsDiscriminatedUnion::ConvertToAString(n
       return NS_OK;
     case nsIDataType::VTYPE_CSTRING:
       CopyASCIItoUTF16(*u.mCStringValue, aResult);
       return NS_OK;
     case nsIDataType::VTYPE_UTF8STRING:
       CopyUTF8toUTF16(*u.mUTF8StringValue, aResult);
       return NS_OK;
     case nsIDataType::VTYPE_CHAR_STR:
-      CopyASCIItoUTF16(u.str.mStringValue, aResult);
+      CopyASCIItoUTF16(mozilla::MakeStringSpan(u.str.mStringValue), aResult);
       return NS_OK;
     case nsIDataType::VTYPE_WCHAR_STR:
       aResult.Assign(u.wstr.mWStringValue);
       return NS_OK;
     case nsIDataType::VTYPE_STRING_SIZE_IS:
       CopyASCIItoUTF16(nsDependentCString(u.str.mStringValue,
                                           u.str.mStringLength),
                        aResult);
@@ -919,17 +919,17 @@ nsDiscriminatedUnion::ConvertToAUTF8Stri
       return NS_OK;
     case nsIDataType::VTYPE_CHAR_STR:
       // XXX Extra copy, can be removed if we're sure CHAR_STR can
       //     only contain ASCII.
       CopyUTF16toUTF8(NS_ConvertASCIItoUTF16(u.str.mStringValue),
                       aResult);
       return NS_OK;
     case nsIDataType::VTYPE_WCHAR_STR:
-      CopyUTF16toUTF8(u.wstr.mWStringValue, aResult);
+      CopyUTF16toUTF8(mozilla::MakeStringSpan(u.wstr.mWStringValue), aResult);
       return NS_OK;
     case nsIDataType::VTYPE_STRING_SIZE_IS:
       // XXX Extra copy, can be removed if we're sure CHAR_STR can
       //     only contain ASCII.
       CopyUTF16toUTF8(NS_ConvertASCIItoUTF16(
         nsDependentCString(u.str.mStringValue,
                            u.str.mStringLength)), aResult);
       return NS_OK;
--- a/xpcom/glue/FileUtils.cpp
+++ b/xpcom/glue/FileUtils.cpp
@@ -167,17 +167,17 @@ mozilla::PathString
 mozilla::GetLibraryName(mozilla::pathstr_t aDirectory, const char* aLib)
 {
 #ifdef XP_WIN
   nsAutoString fullName;
   if (aDirectory) {
     fullName.Assign(aDirectory);
     fullName.Append('\\');
   }
-  AppendUTF8toUTF16(aLib, fullName);
+  AppendUTF8toUTF16(MakeStringSpan(aLib), fullName);
   if (!strstr(aLib, ".dll")) {
     fullName.AppendLiteral(".dll");
   }
   return fullName;
 #else
   char* temp = PR_GetLibraryName(aDirectory, aLib);
   if (!temp) {
     return EmptyCString();
--- a/xpcom/string/moz.build
+++ b/xpcom/string/moz.build
@@ -44,21 +44,16 @@ UNIFIED_SOURCES += [
     'nsStringComparator.cpp',
     'nsStringObsolete.cpp',
     'nsSubstring.cpp',
     'nsTextFormatter.cpp',
     'nsTSubstringTuple.cpp',
     'precompiled_templates.cpp',
 ]
 
-# Are we targeting x86 or x86-64?  If so, compile the SSE2 functions for
-# nsUTF8Utils.cpp and nsReadableUtils.cpp.
-if CONFIG['INTEL_ARCHITECTURE']:
-    SOURCES += ['nsUTF8UtilsSSE2.cpp']
-    SOURCES['nsUTF8UtilsSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
-    SOURCES += ['nsReadableUtilsSSE2.cpp']
-    SOURCES['nsReadableUtilsSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
-
-if CONFIG['BUILD_ARM_NEON'] or CONFIG['CPU_ARCH'] == 'aarch64':
-    SOURCES += ['nsUTF8UtilsNEON.cpp']
-    SOURCES['nsUTF8UtilsNEON.cpp'].flags += CONFIG['NEON_FLAGS']
+# MSVC 2017 has a bug that incorrectly generates C5037 warning which
+# hits the template string code. We need to disable this warning as a
+# workaround. See https://developercommunity.visualstudio.com/
+# content/problem/81223/incorrect-error-c5037-with-permissive.html
+if CONFIG['CC_TYPE'] in ('msvc', 'clang-cl'):
+    CXXFLAGS += ['-wd5037']
 
 FINAL_LIBRARY = 'xul'
--- a/xpcom/string/nsReadableUtils.cpp
+++ b/xpcom/string/nsReadableUtils.cpp
@@ -1,793 +1,244 @@
 /* -*- 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 "nsReadableUtils.h"
-#include "nsReadableUtilsImpl.h"
 
 #include <algorithm>
 
 #include "mozilla/CheckedInt.h"
 
 #include "nscore.h"
 #include "nsMemory.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsUTF8Utils.h"
 
-using mozilla::IsASCII;
-
-/**
- * Fallback implementation for finding the first non-ASCII character in a
- * UTF-16 string.
- */
-static inline int32_t
-FirstNonASCIIUnvectorized(const char16_t* aBegin, const char16_t* aEnd)
-{
-  typedef mozilla::NonASCIIParameters<sizeof(size_t)> p;
-  const size_t kMask = p::mask();
-  const uintptr_t kAlignMask = p::alignMask();
-  const size_t kNumUnicharsPerWord = p::numUnicharsPerWord();
-
-  const char16_t* idx = aBegin;
-
-  // Align ourselves to a word boundary.
-  for (; idx != aEnd && ((uintptr_t(idx) & kAlignMask) != 0); idx++) {
-    if (!IsASCII(*idx)) {
-      return idx - aBegin;
-    }
-  }
-
-  // Check one word at a time.
-  const char16_t* wordWalkEnd = mozilla::aligned(aEnd, kAlignMask);
-  for (; idx != wordWalkEnd; idx += kNumUnicharsPerWord) {
-    const size_t word = *reinterpret_cast<const size_t*>(idx);
-    if (word & kMask) {
-      return idx - aBegin;
-    }
-  }
-
-  // Take care of the remainder one character at a time.
-  for (; idx != aEnd; idx++) {
-    if (!IsASCII(*idx)) {
-      return idx - aBegin;
-    }
-  }
-
-  return -1;
-}
-
-/*
- * This function returns -1 if all characters in str are ASCII characters.
- * Otherwise, it returns a value less than or equal to the index of the first
- * ASCII character in str. For example, if first non-ASCII character is at
- * position 25, it may return 25, 24, or 16. But it guarantees
- * there are only ASCII characters before returned value.
- */
-static inline int32_t
-FirstNonASCII(const char16_t* aBegin, const char16_t* aEnd)
-{
-#ifdef MOZILLA_MAY_SUPPORT_SSE2
-  if (mozilla::supports_sse2()) {
-    return mozilla::SSE2::FirstNonASCII(aBegin, aEnd);
-  }
-#endif
-
-  return FirstNonASCIIUnvectorized(aBegin, aEnd);
-}
-
-void
-LossyCopyUTF16toASCII(const nsAString& aSource, nsACString& aDest)
-{
-  aDest.Truncate();
-  LossyAppendUTF16toASCII(aSource, aDest);
-}
-
-void
-CopyASCIItoUTF16(const nsACString& aSource, nsAString& aDest)
-{
-  if (!CopyASCIItoUTF16(aSource, aDest, mozilla::fallible)) {
-    // Note that this may wildly underestimate the allocation that failed, as
-    // we report the length of aSource as UTF-16 instead of UTF-8.
-    aDest.AllocFailed(aDest.Length() + aSource.Length());
-  }
-}
-
-bool
-CopyASCIItoUTF16(const nsACString& aSource, nsAString& aDest,
-                 const mozilla::fallible_t& aFallible)
-{
-  aDest.Truncate();
-  return AppendASCIItoUTF16(aSource, aDest, aFallible);
-}
-
-void
-LossyCopyUTF16toASCII(const char16ptr_t aSource, nsACString& aDest)
-{
-  aDest.Truncate();
-  if (aSource) {
-    LossyAppendUTF16toASCII(nsDependentString(aSource), aDest);
-  }
-}
-
-void
-CopyASCIItoUTF16(const char* aSource, nsAString& aDest)
-{
-  aDest.Truncate();
-  if (aSource) {
-    AppendASCIItoUTF16(nsDependentCString(aSource), aDest);
-  }
-}
-
-void
-CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
-{
-  if (!CopyUTF16toUTF8(aSource, aDest, mozilla::fallible)) {
-    // Note that this may wildly underestimate the allocation that failed, as
-    // we report the length of aSource as UTF-16 instead of UTF-8.
-    aDest.AllocFailed(aDest.Length() + aSource.Length());
-  }
-}
-
-bool
-CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
-                const mozilla::fallible_t& aFallible)
-{
-  aDest.Truncate();
-  if (!AppendUTF16toUTF8(aSource, aDest, aFallible)) {
-    return false;
-  }
-  return true;
-}
-
-void
-CopyUTF8toUTF16(const nsACString& aSource, nsAString& aDest)
-{
-  aDest.Truncate();
-  AppendUTF8toUTF16(aSource, aDest);
-}
-
-void
-CopyUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest)
-{
-  aDest.Truncate();
-  AppendUTF16toUTF8(aSource, aDest);
-}
-
-void
-CopyUTF8toUTF16(const char* aSource, nsAString& aDest)
-{
-  aDest.Truncate();
-  AppendUTF8toUTF16(aSource, aDest);
-}
-
-void
-LossyAppendUTF16toASCII(const nsAString& aSource, nsACString& aDest)
-{
-  uint32_t old_dest_length = aDest.Length();
-  aDest.SetLength(old_dest_length + aSource.Length());
-
-  nsAString::const_iterator fromBegin, fromEnd;
-
-  nsACString::iterator dest;
-  aDest.BeginWriting(dest);
-
-  dest.advance(old_dest_length);
-
-  // right now, this won't work on multi-fragment destinations
-  LossyConvertEncoding16to8 converter(dest.get());
-
-  copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
-              converter);
-}
-
-void
-AppendASCIItoUTF16(const nsACString& aSource, nsAString& aDest)
-{
-  if (!AppendASCIItoUTF16(aSource, aDest, mozilla::fallible)) {
-    aDest.AllocFailed(aDest.Length() + aSource.Length());
-  }
-}
-
-bool
-AppendASCIItoUTF16(const nsACString& aSource, nsAString& aDest,
-                   const mozilla::fallible_t& aFallible)
-{
-  uint32_t old_dest_length = aDest.Length();
-  if (!aDest.SetLength(old_dest_length + aSource.Length(),
-                       aFallible)) {
-    return false;
-  }
-
-  nsACString::const_iterator fromBegin, fromEnd;
-
-  nsAString::iterator dest;
-  aDest.BeginWriting(dest);
-
-  dest.advance(old_dest_length);
-
-  // right now, this won't work on multi-fragment destinations
-  LossyConvertEncoding8to16 converter(dest.get());
-
-  copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
-              converter);
-  return true;
-}
-
-void
-LossyAppendUTF16toASCII(const char16ptr_t aSource, nsACString& aDest)
-{
-  if (aSource) {
-    LossyAppendUTF16toASCII(nsDependentString(aSource), aDest);
-  }
-}
-
-bool
-AppendASCIItoUTF16(const char* aSource, nsAString& aDest, const mozilla::fallible_t& aFallible)
-{
-  if (aSource) {
-    return AppendASCIItoUTF16(nsDependentCString(aSource), aDest, aFallible);
-  }
-
-  return true;
-}
-
-void
-AppendASCIItoUTF16(const char* aSource, nsAString& aDest)
-{
-  if (aSource) {
-    AppendASCIItoUTF16(nsDependentCString(aSource), aDest);
-  }
-}
-
-void
-AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
-{
-  if (!AppendUTF16toUTF8(aSource, aDest, mozilla::fallible)) {
-    // Note that this may wildly underestimate the allocation that failed, as
-    // we report the length of aSource as UTF-16 instead of UTF-8.
-    aDest.AllocFailed(aDest.Length() + aSource.Length());
-  }
-}
-
-bool
-AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
-                  const mozilla::fallible_t& aFallible)
-{
-  // At 16 characters analysis showed better performance of both the all ASCII
-  // and non-ASCII cases, so we limit calling |FirstNonASCII| to strings of
-  // that length.
-  const nsAString::size_type kFastPathMinLength = 16;
-
-  int32_t firstNonASCII = 0;
-  if (aSource.Length() >= kFastPathMinLength) {
-    firstNonASCII = FirstNonASCII(aSource.BeginReading(), aSource.EndReading());
-  }
-
-  if (firstNonASCII == -1) {
-    // This is all ASCII, we can use the more efficient lossy append.
-    mozilla::CheckedInt<nsACString::size_type> new_length(aSource.Length());
-    new_length += aDest.Length();
-
-    if (!new_length.isValid() ||
-        !aDest.SetCapacity(new_length.value(), aFallible)) {
-      return false;
-    }
-
-    LossyAppendUTF16toASCII(aSource, aDest);
-    return true;
-  }
-
-  nsAString::const_iterator source_start, source_end;
-  CalculateUTF8Size calculator;
-  aSource.BeginReading(source_start);
-  aSource.EndReading(source_end);
-
-  // Skip the characters that we know are single byte.
-  source_start.advance(firstNonASCII);
-
-  copy_string(source_start,
-              source_end, calculator);
-
-  // Include the ASCII characters that were skipped in the count.
-  size_t count = calculator.Size() + firstNonASCII;
-
-  if (count) {
-    auto old_dest_length = aDest.Length();
-    // Grow the buffer if we need to.
-    mozilla::CheckedInt<nsACString::size_type> new_length(count);
-    new_length += old_dest_length;
-
-    if (!new_length.isValid() ||
-        !aDest.SetLength(new_length.value(), aFallible)) {
-      return false;
-    }
-
-    // All ready? Time to convert
-
-    nsAString::const_iterator ascii_end;
-    aSource.BeginReading(ascii_end);
-
-    if (firstNonASCII >= static_cast<int32_t>(kFastPathMinLength)) {
-      // Use the more efficient lossy converter for the ASCII portion.
-      LossyConvertEncoding16to8 lossy_converter(
-          aDest.BeginWriting() + old_dest_length);
-      nsAString::const_iterator ascii_start;
-      aSource.BeginReading(ascii_start);
-      ascii_end.advance(firstNonASCII);
-
-      copy_string(ascii_start, ascii_end, lossy_converter);
-    } else {
-      // Not using the lossy shortcut, we need to include the leading ASCII
-      // chars.
-      firstNonASCII = 0;
-    }
-
-    ConvertUTF16toUTF8 converter(
-        aDest.BeginWriting() + old_dest_length + firstNonASCII);
-    copy_string(ascii_end,
-                aSource.EndReading(source_end), converter);
-
-    NS_ASSERTION(converter.Size() == count - firstNonASCII,
-                 "Unexpected disparity between CalculateUTF8Size and "
-                 "ConvertUTF16toUTF8");
-  }
-
-  return true;
-}
-
-void
-AppendUTF8toUTF16(const nsACString& aSource, nsAString& aDest)
-{
-  if (!AppendUTF8toUTF16(aSource, aDest, mozilla::fallible)) {
-    aDest.AllocFailed(aDest.Length() + aSource.Length());
-  }
-}
-
-bool
-AppendUTF8toUTF16(const nsACString& aSource, nsAString& aDest,
-                  const mozilla::fallible_t& aFallible)
-{
-  nsACString::const_iterator source_start, source_end;
-  CalculateUTF8Length calculator;
-  copy_string(aSource.BeginReading(source_start),
-              aSource.EndReading(source_end), calculator);
-
-  uint32_t count = calculator.Length();
-
-  // Avoid making the string mutable if we're appending an empty string
-  if (count) {
-    uint32_t old_dest_length = aDest.Length();
-
-    // Grow the buffer if we need to.
-    if (!aDest.SetLength(old_dest_length + count, aFallible)) {
-      return false;
-    }
-
-    // All ready? Time to convert
-
-    ConvertUTF8toUTF16 converter(aDest.BeginWriting() + old_dest_length);
-    copy_string(aSource.BeginReading(source_start),
-                aSource.EndReading(source_end), converter);
-
-    NS_ASSERTION(converter.ErrorEncountered() ||
-                 converter.Length() == count,
-                 "CalculateUTF8Length produced the wrong length");
-
-    if (converter.ErrorEncountered()) {
-      NS_ERROR("Input wasn't UTF8 or incorrect length was calculated");
-      aDest.SetLength(old_dest_length);
-    }
-  }
-
-  return true;
-}
-
-void
-AppendUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest)
-{
-  if (aSource) {
-    AppendUTF16toUTF8(nsDependentString(aSource), aDest);
-  }
-}
-
-void
-AppendUTF8toUTF16(const char* aSource, nsAString& aDest)
-{
-  if (aSource) {
-    AppendUTF8toUTF16(nsDependentCString(aSource), aDest);
-  }
-}
-
+using mozilla::MakeSpan;
 
 /**
  * A helper function that allocates a buffer of the desired character type big enough to hold a copy of the supplied string (plus a zero terminator).
  *
  * @param aSource an string you will eventually be making a copy of
  * @return a new buffer (of the type specified by the second parameter) which you must free with |free|.
  *
  */
 template <class FromStringT, class ToCharT>
 inline
 ToCharT*
 AllocateStringCopy(const FromStringT& aSource, ToCharT*)
 {
+  // XXX can this overflow?
   return static_cast<ToCharT*>(moz_xmalloc(
-    (aSource.Length() + 1) * sizeof(ToCharT)));
+    (size_t(aSource.Length()) + 1) * sizeof(ToCharT)));
 }
 
 
 char*
 ToNewCString(const nsAString& aSource)
 {
-  char* result = AllocateStringCopy(aSource, (char*)0);
-  if (!result) {
+  char* dest = AllocateStringCopy(aSource, (char*)nullptr);
+  if (!dest) {
     return nullptr;
   }
 
-  nsAString::const_iterator fromBegin, fromEnd;
-  LossyConvertEncoding16to8 converter(result);
-  copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
-              converter).write_terminator();
-  return result;
+  auto len = aSource.Length();
+  LossyConvertUTF16toLatin1(aSource, MakeSpan(dest, len));
+  dest[len] = 0;
+  return dest;
 }
 
 char*
 ToNewUTF8String(const nsAString& aSource, uint32_t* aUTF8Count)
 {
-  nsAString::const_iterator start, end;
-  CalculateUTF8Size calculator;
-  copy_string(aSource.BeginReading(start), aSource.EndReading(end),
-              calculator);
-
-  if (aUTF8Count) {
-    *aUTF8Count = calculator.Size();
+  auto len = aSource.Length();
+  // The uses of this function seem temporary enough that it's not
+  // worthwhile to be fancy about the allocation size. Let's just use
+  // the worst case.
+  // Times 3 plus 2, because ConvertUTF16toUTF8 requires times 3 plus 1 and
+  // then we have the terminator.
+  mozilla::CheckedInt<size_t> destLen(len);
+  destLen *= 3;
+  destLen += 2;
+  if (!destLen.isValid()) {
+    return nullptr;
   }
-
-  char* result = static_cast<char*>
-                 (moz_xmalloc(calculator.Size() + 1));
-  if (!result) {
+  size_t destLenVal = destLen.value();
+  if (destLenVal > UINT32_MAX) {
+    return nullptr;
+  }
+  char* dest = static_cast<char*>(moz_xmalloc(destLenVal));
+  if (!dest) {
     return nullptr;
   }
 
-  ConvertUTF16toUTF8 converter(result);
-  copy_string(aSource.BeginReading(start), aSource.EndReading(end),
-              converter).write_terminator();
-  NS_ASSERTION(calculator.Size() == converter.Size(), "length mismatch");
+  size_t written = ConvertUTF16toUTF8(aSource, MakeSpan(dest, destLenVal));
+  dest[written] = 0;
 
-  return result;
+  if (aUTF8Count) {
+    *aUTF8Count = written;
+  }
+
+  return dest;
 }
 
 char*
 ToNewCString(const nsACString& aSource)
 {
   // no conversion needed, just allocate a buffer of the correct length and copy into it
 
-  char* result = AllocateStringCopy(aSource, (char*)0);
-  if (!result) {
+  char* dest = AllocateStringCopy(aSource, (char*)nullptr);
+  if (!dest) {
     return nullptr;
   }
 
-  nsACString::const_iterator fromBegin, fromEnd;
-  char* toBegin = result;
-  *copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
-               toBegin) = char(0);
-  return result;
+  auto len = aSource.Length();
+  memcpy(dest, aSource.BeginReading(), len * sizeof(char));
+  dest[len] = 0;
+  return dest;
 }
 
 char16_t*
 ToNewUnicode(const nsAString& aSource)
 {
   // no conversion needed, just allocate a buffer of the correct length and copy into it
 
-  char16_t* result = AllocateStringCopy(aSource, (char16_t*)0);
-  if (!result) {
+  char16_t* dest = AllocateStringCopy(aSource, (char16_t*)nullptr);
+  if (!dest) {
     return nullptr;
   }
 
-  nsAString::const_iterator fromBegin, fromEnd;
-  char16_t* toBegin = result;
-  *copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
-               toBegin) = char16_t(0);
-  return result;
+  auto len = aSource.Length();
+  memcpy(dest, aSource.BeginReading(), len * sizeof(char16_t));
+  dest[len] = 0;
+  return dest;
 }
 
 char16_t*
 ToNewUnicode(const nsACString& aSource)
 {
-  char16_t* result = AllocateStringCopy(aSource, (char16_t*)0);
-  if (!result) {
+  char16_t* dest = AllocateStringCopy(aSource, (char16_t*)nullptr);
+  if (!dest) {
     return nullptr;
   }
 
-  nsACString::const_iterator fromBegin, fromEnd;
-  LossyConvertEncoding8to16 converter(result);
-  copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
-              converter).write_terminator();
-  return result;
-}
-
-uint32_t
-CalcUTF8ToUnicodeLength(const nsACString& aSource)
-{
-  nsACString::const_iterator start, end;
-  CalculateUTF8Length calculator;
-  copy_string(aSource.BeginReading(start), aSource.EndReading(end),
-              calculator);
-  return calculator.Length();
-}
-
-char16_t*
-UTF8ToUnicodeBuffer(const nsACString& aSource, char16_t* aBuffer,
-                    uint32_t* aUTF16Count)
-{
-  nsACString::const_iterator start, end;
-  ConvertUTF8toUTF16 converter(aBuffer);
-  copy_string(aSource.BeginReading(start),
-              aSource.EndReading(end),
-              converter).write_terminator();
-  if (aUTF16Count) {
-    *aUTF16Count = converter.Length();
-  }
-  return aBuffer;
+  auto len = aSource.Length();
+  ConvertLatin1toUTF16(aSource, MakeSpan(dest, len));
+  dest[len] = 0;
+  return dest;
 }
 
 char16_t*
 UTF8ToNewUnicode(const nsACString& aSource, uint32_t* aUTF16Count)
 {
-  const uint32_t length = CalcUTF8ToUnicodeLength(aSource);
-  const size_t buffer_size = (length + 1) * sizeof(char16_t);
-  char16_t* buffer = static_cast<char16_t*>(moz_xmalloc(buffer_size));
-  if (!buffer) {
+  // Compute length plus one as required by ConvertUTF8toUTF16
+  uint32_t lengthPlusOne = aSource.Length() + 1; // Can't overflow
+
+  mozilla::CheckedInt<size_t> allocLength(lengthPlusOne);
+  // Add space for zero-termination
+  allocLength += 1;
+  // We need UTF-16 units
+  allocLength *= sizeof(char16_t);
+
+  if (!allocLength.isValid()) {
     return nullptr;
   }
 
-  uint32_t copied;
-  UTF8ToUnicodeBuffer(aSource, buffer, &copied);
-  NS_ASSERTION(length == copied, "length mismatch");
+  char16_t* dest = (char16_t*)moz_xmalloc(allocLength.value());
+  if (!dest) {
+    return nullptr;
+  }
+
+  size_t written = ConvertUTF8toUTF16(aSource, MakeSpan(dest, lengthPlusOne));
+  dest[written] = 0;
 
   if (aUTF16Count) {
-    *aUTF16Count = copied;
+    *aUTF16Count = written;
   }
-  return buffer;
+
+  return dest;
 }
 
 char16_t*
 CopyUnicodeTo(const nsAString& aSource, uint32_t aSrcOffset, char16_t* aDest,
               uint32_t aLength)
 {
-  nsAString::const_iterator fromBegin, fromEnd;
-  char16_t* toBegin = aDest;
-  copy_string(aSource.BeginReading(fromBegin).advance(int32_t(aSrcOffset)),
-              aSource.BeginReading(fromEnd).advance(int32_t(aSrcOffset + aLength)),
-              toBegin);
+  MOZ_ASSERT(aSrcOffset + aLength <= aSource.Length());
+  memcpy(aDest, aSource.BeginReading() + aSrcOffset, size_t(aLength) * sizeof(char16_t));
   return aDest;
 }
 
 void
-CopyUnicodeTo(const nsAString::const_iterator& aSrcStart,
-              const nsAString::const_iterator& aSrcEnd,
-              nsAString& aDest)
-{
-  aDest.SetLength(Distance(aSrcStart, aSrcEnd));
-
-  nsAString::char_iterator dest = aDest.BeginWriting();
-  nsAString::const_iterator fromBegin(aSrcStart);
-
-  copy_string(fromBegin, aSrcEnd, dest);
-}
-
-void
-AppendUnicodeTo(const nsAString::const_iterator& aSrcStart,
-                const nsAString::const_iterator& aSrcEnd,
-                nsAString& aDest)
-{
-  uint32_t oldLength = aDest.Length();
-  aDest.SetLength(oldLength + Distance(aSrcStart, aSrcEnd));
-
-  nsAString::char_iterator dest = aDest.BeginWriting() + oldLength;
-  nsAString::const_iterator fromBegin(aSrcStart);
-
-  copy_string(fromBegin, aSrcEnd, dest);
-}
-
-bool
-IsASCII(const nsAString& aString)
-{
-  static const char16_t NOT_ASCII = char16_t(~0x007F);
-
-
-  // Don't want to use |copy_string| for this task, since we can stop at the first non-ASCII character
-
-  nsAString::const_iterator iter, done_reading;
-  aString.BeginReading(iter);
-  aString.EndReading(done_reading);
-
-  const char16_t* c = iter.get();
-  const char16_t* end = done_reading.get();
-
-  while (c < end) {
-    if (*c++ & NOT_ASCII) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-/**
- * A character sink for in-place case conversion.
- */
-class ConvertToUpperCase
-{
-public:
-  typedef char value_type;
-
-  uint32_t
-  write(const char* aSource, uint32_t aSourceLength)
-  {
-    char* cp = const_cast<char*>(aSource);
-    const char* end = aSource + aSourceLength;
-    while (cp != end) {
-      char ch = *cp;
-      if (ch >= 'a' && ch <= 'z') {
-        *cp = ch - ('a' - 'A');
-      }
-      ++cp;
-    }
-    return aSourceLength;
-  }
-};
-
-void
 ToUpperCase(nsACString& aCString)
 {
-  ConvertToUpperCase converter;
-  char* start;
-  converter.write(aCString.BeginWriting(start), aCString.Length());
-}
-
-/**
- * A character sink for copying with case conversion.
- */
-class CopyToUpperCase
-{
-public:
-  typedef char value_type;
-
-  explicit CopyToUpperCase(nsACString::iterator& aDestIter,
-                           const nsACString::iterator& aEndIter)
-    : mIter(aDestIter)
-    , mEnd(aEndIter)
-  {
+  char* cp = aCString.BeginWriting();
+  char* end = cp + aCString.Length();
+  while (cp != end) {
+    char ch = *cp;
+    if (ch >= 'a' && ch <= 'z') {
+      *cp = ch - ('a' - 'A');
+    }
+    ++cp;
   }
-
-  uint32_t
-  write(const char* aSource, uint32_t aSourceLength)
-  {
-    uint32_t len = XPCOM_MIN(uint32_t(mEnd - mIter), aSourceLength);
-    char* cp = mIter.get();
-    const char* end = aSource + len;
-    while (aSource != end) {
-      char ch = *aSource;
-      if ((ch >= 'a') && (ch <= 'z')) {
-        *cp = ch - ('a' - 'A');
-      } else {
-        *cp = ch;
-      }
-      ++aSource;
-      ++cp;
-    }
-    mIter.advance(len);
-    return len;
-  }
-
-protected:
-  nsACString::iterator& mIter;
-  const nsACString::iterator& mEnd;
-};
+}
 
 void
 ToUpperCase(const nsACString& aSource, nsACString& aDest)
 {
-  nsACString::const_iterator fromBegin, fromEnd;
-  nsACString::iterator toBegin, toEnd;
   aDest.SetLength(aSource.Length());
-
-  CopyToUpperCase converter(aDest.BeginWriting(toBegin), aDest.EndWriting(toEnd));
-  copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
-              converter);
+  const char* src = aSource.BeginReading();
+  const char* end = src + aSource.Length();
+  char* dst = aDest.BeginWriting();
+  while (src != end) {
+    char ch = *src;
+    if (ch >= 'a' && ch <= 'z') {
+      *dst = ch - ('a' - 'A');
+    } else {
+      *dst = ch;
+    }
+    ++src;
+    ++dst;
+  }
 }
 
-/**
- * A character sink for case conversion.
- */
-class ConvertToLowerCase
-{
-public:
-  typedef char value_type;
-
-  uint32_t
-  write(const char* aSource, uint32_t aSourceLength)
-  {
-    char* cp = const_cast<char*>(aSource);
-    const char* end = aSource + aSourceLength;
-    while (cp != end) {
-      char ch = *cp;
-      if ((ch >= 'A') && (ch <= 'Z')) {
-        *cp = ch + ('a' - 'A');
-      }
-      ++cp;
-    }
-    return aSourceLength;
-  }
-};
-
 void
 ToLowerCase(nsACString& aCString)
 {
-  ConvertToLowerCase converter;
-  char* start;
-  converter.write(aCString.BeginWriting(start), aCString.Length());
-}
-
-/**
- * A character sink for copying with case conversion.
- */
-class CopyToLowerCase
-{
-public:
-  typedef char value_type;
-
-  explicit CopyToLowerCase(nsACString::iterator& aDestIter,
-                           const nsACString::iterator& aEndIter)
-    : mIter(aDestIter)
-    , mEnd(aEndIter)
-  {
+  char* cp = aCString.BeginWriting();
+  char* end = cp + aCString.Length();
+  while (cp != end) {
+    char ch = *cp;
+    if (ch >= 'A' && ch <= 'Z') {
+      *cp = ch + ('a' - 'A');
+    }
+    ++cp;
   }
-
-  uint32_t
-  write(const char* aSource, uint32_t aSourceLength)
-  {
-    uint32_t len = XPCOM_MIN(uint32_t(mEnd - mIter), aSourceLength);
-    char* cp = mIter.get();
-    const char* end = aSource + len;
-    while (aSource != end) {
-      char ch = *aSource;
-      if ((ch >= 'A') && (ch <= 'Z')) {
-        *cp = ch + ('a' - 'A');
-      } else {
-        *cp = ch;
-      }
-      ++aSource;
-      ++cp;
-    }
-    mIter.advance(len);
-    return len;
-  }
-
-protected:
-  nsACString::iterator& mIter;
-  const nsACString::iterator& mEnd;
-};
+}
 
 void
 ToLowerCase(const nsACString& aSource, nsACString& aDest)
 {
-  nsACString::const_iterator fromBegin, fromEnd;
-  nsACString::iterator toBegin, toEnd;
   aDest.SetLength(aSource.Length());
-
-  CopyToLowerCase converter(aDest.BeginWriting(toBegin), aDest.EndWriting(toEnd));
-  copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
-              converter);
+  const char* src = aSource.BeginReading();
+  const char* end = src + aSource.Length();
+  char* dst = aDest.BeginWriting();
+  while (src != end) {
+    char ch = *src;
+    if (ch >= 'A' && ch <= 'Z') {
+      *dst = ch + ('a' - 'A');
+    } else {
+      *dst = ch;
+    }
+    ++src;
+    ++dst;
+  }
 }
 
 bool
 ParseString(const nsACString& aSource, char aDelimiter,
             nsTArray<nsCString>& aArray)
 {
   nsACString::const_iterator start, end;
   aSource.BeginReading(start);
@@ -1178,117 +629,56 @@ VoidCString()
 {
   static const nsCString sNull(mozilla::detail::StringDataFlags::VOIDED);
 
   return sNull;
 }
 
 int32_t
 CompareUTF8toUTF16(const nsACString& aUTF8String,
-                   const nsAString& aUTF16String)
+                   const nsAString& aUTF16String,
+                   bool* aErr)
 {
-  static const uint32_t NOT_ASCII = uint32_t(~0x7F);
-
   const char* u8;
   const char* u8end;
   aUTF8String.BeginReading(u8);
   aUTF8String.EndReading(u8end);
 
   const char16_t* u16;
   const char16_t* u16end;
   aUTF16String.BeginReading(u16);
   aUTF16String.EndReading(u16end);
 
-  while (u8 != u8end && u16 != u16end) {
-    // Cast away the signedness of *u8 to prevent signextension when
-    // converting to uint32_t
-    uint32_t c8_32 = (uint8_t)*u8;
-
-    if (c8_32 & NOT_ASCII) {
-      bool err;
-      c8_32 = UTF8CharEnumerator::NextChar(&u8, u8end, &err);
-      if (err) {
-        return INT32_MIN;
+  for (;;) {
+    if (u8 == u8end) {
+      if (u16 == u16end) {
+        return 0;
       }
-
-      uint32_t c16_32 = UTF16CharEnumerator::NextChar(&u16, u16end);
-      // The above UTF16CharEnumerator::NextChar() calls can
-      // fail, but if it does for anything other than no data to
-      // look at (which can't happen here), it returns the
-      // Unicode replacement character 0xFFFD for the invalid
-      // data they were fed. Ignore that error and treat invalid
-      // UTF16 as 0xFFFD.
-      //
-      // This matches what our UTF16 to UTF8 conversion code
-      // does, and thus a UTF8 string that came from an invalid
-      // UTF16 string will compare equal to the invalid UTF16
-      // string it came from. Same is true for any other UTF16
-      // string differs only in the invalid part of the string.
-
-      if (c8_32 != c16_32) {
-        return c8_32 < c16_32 ? -1 : 1;
-      }
-    } else {
-      if (c8_32 != *u16) {
-        return c8_32 > *u16 ? 1 : -1;
-      }
-
-      ++u8;
-      ++u16;
+      return -1;
+    }
+    if (u16 == u16end) {
+      return 1;
     }
-  }
-
-  if (u8 != u8end) {
-    // We get to the end of the UTF16 string, but no to the end of
-    // the UTF8 string. The UTF8 string is longer than the UTF16
-    // string
-
+    // No need for ASCII optimization, since both NextChar()
+    // calls get inlined.
+    uint32_t scalar8 = UTF8CharEnumerator::NextChar(&u8, u8end, aErr);
+    uint32_t scalar16 = UTF16CharEnumerator::NextChar(&u16, u16end, aErr);
+    if (scalar16 == scalar8) {
+      continue;
+    }
+    if (scalar8 < scalar16) {
+      return -1;
+    }
     return 1;
   }
-
-  if (u16 != u16end) {
-    // We get to the end of the UTF8 string, but no to the end of
-    // the UTF16 string. The UTF16 string is longer than the UTF8
-    // string
-
-    return -1;
-  }
-
-  // The two strings match.
-
-  return 0;
 }
 
 void
 AppendUCS4ToUTF16(const uint32_t aSource, nsAString& aDest)
 {
   NS_ASSERTION(IS_VALID_CHAR(aSource), "Invalid UCS4 char");
   if (IS_IN_BMP(aSource)) {
     aDest.Append(char16_t(aSource));
   } else {
     aDest.Append(H_SURROGATE(aSource));
     aDest.Append(L_SURROGATE(aSource));
   }
 }
-
-extern "C" {
-
-void Gecko_AppendUTF16toCString(nsACString* aThis, const nsAString* aOther)
-{
-  AppendUTF16toUTF8(*aOther, *aThis);
-}
-
-void Gecko_AppendUTF8toString(nsAString* aThis, const nsACString* aOther)
-{
-  AppendUTF8toUTF16(*aOther, *aThis);
-}
-
-bool Gecko_FallibleAppendUTF16toCString(nsACString* aThis, const nsAString* aOther)
-{
-  return AppendUTF16toUTF8(*aOther, *aThis, mozilla::fallible);
-}
-
-bool Gecko_FallibleAppendUTF8toString(nsAString* aThis, const nsACString* aOther)
-{
-  return AppendUTF8toUTF16(*aOther, *aThis, mozilla::fallible);
-}
-
-}
--- a/xpcom/string/nsReadableUtils.h
+++ b/xpcom/string/nsReadableUtils.h
@@ -16,17 +16,99 @@
 #include "mozilla/Assertions.h"
 #include "nsAString.h"
 
 #include "nsTArrayForwardDeclare.h"
 
 // Can't include mozilla/Encoding.h here
 extern "C" {
   size_t encoding_utf8_valid_up_to(uint8_t const* buffer, size_t buffer_len);
-  size_t encoding_ascii_valid_up_to(uint8_t const* buffer, size_t buffer_len);
+  bool encoding_mem_is_ascii(char const* buffer, size_t buffer_len);
+  bool encoding_mem_is_basic_latin(char16_t const* buffer, size_t buffer_len);
+  bool encoding_mem_is_utf8_latin1(char const* buffer, size_t buffer_len);
+  bool encoding_mem_is_str_latin1(char const* buffer, size_t buffer_len);
+  bool encoding_mem_is_utf16_latin1(char16_t const* buffer, size_t buffer_len);
+  void encoding_mem_convert_utf16_to_latin1_lossy(const char16_t* src, size_t src_len, char* dst, size_t dst_len);
+  size_t encoding_mem_convert_utf8_to_latin1_lossy(const char* src, size_t src_len, char* dst, size_t dst_len);
+  void encoding_mem_convert_latin1_to_utf16(const char* src, size_t src_len, char16_t* dst, size_t dst_len);
+  size_t encoding_mem_convert_utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len);
+  size_t encoding_mem_convert_utf8_to_utf16(const char* src, size_t src_len, char16_t* dst, size_t dst_len);
+}
+
+// From the nsstring crate
+extern "C" {
+  bool nsstring_fallible_append_utf8_impl(nsAString* aThis, const char* aOther, size_t aOtherLen, size_t aOldLen);
+  bool nsstring_fallible_append_latin1_impl(nsAString* aThis, const char* aOther, size_t aOtherLen, size_t aOldLen);
+  bool nscstring_fallible_append_utf16_to_utf8_impl(nsACString* aThis, const char16_t*, size_t aOtherLen, size_t aOldLen);
+  bool nscstring_fallible_append_utf16_to_latin1_lossy_impl(nsACString* aThis, const char16_t*, size_t aOtherLen, size_t aOldLen);
+  bool nscstring_fallible_append_utf8_to_latin1_lossy_check(nsACString* aThis, const nsACString* aOther, size_t aOldLen);
+  bool nscstring_fallible_append_latin1_to_utf8_check(nsACString* aThis, const nsACString* aOther, size_t aOldLen);
+}
+
+/**
+ * If all the code points in the input are below U+0100, converts to Latin1, i.e. unsigned byte value is Unicode
+ * scalar value; not windows-1252. If there are code points above U+00FF, asserts in debug builds and produces
+ * garbage in release builds. The nature of the garbage depends on the CPU architecture and must not be relied upon.
+ *
+ * The length of aDest must be not be less than the length of aSource.
+ */
+inline void
+LossyConvertUTF16toLatin1(mozilla::Span<const char16_t> aSource, mozilla::Span<char> aDest)
+{
+  encoding_mem_convert_utf16_to_latin1_lossy(aSource.Elements(), aSource.Length(), aDest.Elements(), aDest.Length());
+}
+
+/**
+ * If all the code points in the input are below U+0100, converts to Latin1, i.e. unsigned byte value is Unicode
+ * scalar value; not windows-1252. If there are code points above U+00FF, asserts in debug builds and produces
+ * garbage in release builds. The nature of the garbage may depend on the CPU architecture and must not be relied upon.
+ *
+ * The length of aDest must be not be less than the length of aSource.
+ */
+inline size_t
+LossyConvertUTF8toLatin1(mozilla::Span<const char> aSource, mozilla::Span<char> aDest)
+{
+  return encoding_mem_convert_utf8_to_latin1_lossy(aSource.Elements(), aSource.Length(), aDest.Elements(), aDest.Length());
+}
+
+/**
+ * Interprets unsigned byte value as Unicode scalar value (i.e. not windows-1251!).
+ *
+ * The length of aDest must be not be less than the length of aSource.
+ */
+inline void
+ConvertLatin1toUTF16(mozilla::Span<const char> aSource, mozilla::Span<char16_t> aDest)
+{
+  encoding_mem_convert_latin1_to_utf16(aSource.Elements(), aSource.Length(), aDest.Elements(), aDest.Length());
+}
+
+/**
+ * Lone surrogates are replaced with the REPLACEMENT CHARACTER.
+ *
+ * The length of aDest must be at least the length of aSource times three _plus one_.
+ *
+ * Returns the number of code units written.
+ */
+inline size_t
+ConvertUTF16toUTF8(mozilla::Span<const char16_t> aSource, mozilla::Span<char> aDest)
+{
+  return encoding_mem_convert_utf16_to_utf8(aSource.Elements(), aSource.Length(), aDest.Elements(), aDest.Length());
+}
+
+/**
+ * Malformed byte sequences are replaced with the REPLACEMENT CHARACTER.
+ *
+ * The length of aDest must at least one greater than the length of aSource.
+ *
+ * Returns the number of code units written.
+ */
+inline size_t
+ConvertUTF8toUTF16(mozilla::Span<const char> aSource, mozilla::Span<char16_t> aDest)
+{
+  return encoding_mem_convert_utf8_to_utf16(aSource.Elements(), aSource.Length(), aDest.Elements(), aDest.Length());
 }
 
 inline size_t
 Distance(const nsReadingIterator<char16_t>& aStart,
          const nsReadingIterator<char16_t>& aEnd)
 {
   MOZ_ASSERT(aStart.get() <= aEnd.get());
   return static_cast<size_t>(aEnd.get() - aStart.get());
@@ -34,70 +116,167 @@ Distance(const nsReadingIterator<char16_
 inline size_t
 Distance(const nsReadingIterator<char>& aStart,
          const nsReadingIterator<char>& aEnd)
 {
   MOZ_ASSERT(aStart.get() <= aEnd.get());
   return static_cast<size_t>(aEnd.get() - aStart.get());
 }
 
-void LossyCopyUTF16toASCII(const nsAString& aSource, nsACString& aDest);
-void CopyASCIItoUTF16(const nsACString& aSource, nsAString& aDest);
-MOZ_MUST_USE bool CopyASCIItoUTF16(const nsACString& aSource, nsAString& aDest,
-                                   const mozilla::fallible_t&);
+// UTF-8 to UTF-16
+// Invalid UTF-8 byte sequences are replaced with the REPLACEMENT CHARACTER.
+
+inline MOZ_MUST_USE bool CopyUTF8toUTF16(mozilla::Span<const char> aSource,
+                                         nsAString& aDest,
+                                         const mozilla::fallible_t&)
+{
+  return nsstring_fallible_append_utf8_impl(&aDest, aSource.Elements(), aSource.Length(), 0);
+}
 
-void LossyCopyUTF16toASCII(const char16ptr_t aSource, nsACString& aDest);
-void CopyASCIItoUTF16(const char* aSource, nsAString& aDest);
+inline void CopyUTF8toUTF16(mozilla::Span<const char> aSource,
+                            nsAString& aDest)
+{
+  if (MOZ_UNLIKELY(!CopyUTF8toUTF16(aSource, aDest, mozilla::fallible))) {
+    aDest.AllocFailed(aSource.Length());
+  }
+}
+
+inline MOZ_MUST_USE bool AppendUTF8toUTF16(mozilla::Span<const char> aSource,
+                                           nsAString& aDest,
+                                           const mozilla::fallible_t&)
+{
+  return nsstring_fallible_append_utf8_impl(&aDest, aSource.Elements(), aSource.Length(), aDest.Length());
+}
+
+inline void AppendUTF8toUTF16(mozilla::Span<const char> aSource,
+                              nsAString& aDest)
+{
+  if (MOZ_UNLIKELY(!AppendUTF8toUTF16(aSource, aDest, mozilla::fallible))) {
+    aDest.AllocFailed(aDest.Length() + aSource.Length());
+  }
+}
 
-void CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest);
-MOZ_MUST_USE bool CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
-                                  const mozilla::fallible_t&);
-void CopyUTF8toUTF16(const nsACString& aSource, nsAString& aDest);
+// Latin1 to UTF-16
+// Interpret each incoming unsigned byte value as a Unicode scalar value (not windows-1252!).
+// The function names say "ASCII" instead of "Latin1" for legacy reasons.
+
+inline MOZ_MUST_USE bool CopyASCIItoUTF16(mozilla::Span<const char> aSource,
+                                          nsAString& aDest,
+                                          const mozilla::fallible_t&)
+{
+  return nsstring_fallible_append_latin1_impl(&aDest, aSource.Elements(), aSource.Length(), 0);
+}
 
-void CopyUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest);
-void CopyUTF8toUTF16(const char* aSource, nsAString& aDest);
+inline void CopyASCIItoUTF16(mozilla::Span<const char> aSource,
+                             nsAString& aDest)
+{
+  if (MOZ_UNLIKELY(!CopyASCIItoUTF16(aSource, aDest, mozilla::fallible))) {
+    aDest.AllocFailed(aSource.Length());
+  }
+}
+
+inline MOZ_MUST_USE bool AppendASCIItoUTF16(mozilla::Span<const char> aSource,
+                                            nsAString& aDest,
+                                            const mozilla::fallible_t&)
+{
+  return nsstring_fallible_append_latin1_impl(&aDest, aSource.Elements(), aSource.Length(), aDest.Length());
+}
+
+inline void AppendASCIItoUTF16(mozilla::Span<const char> aSource,
+                               nsAString& aDest)
+{
+  if (MOZ_UNLIKELY(!AppendASCIItoUTF16(aSource, aDest, mozilla::fallible))) {
+    aDest.AllocFailed(aDest.Length() + aSource.Length());
+  }
+}
 
-void LossyAppendUTF16toASCII(const nsAString& aSource, nsACString& aDest);
-void AppendASCIItoUTF16(const nsACString& aSource, nsAString& aDest);
-MOZ_MUST_USE bool AppendASCIItoUTF16(const nsACString& aSource,
-                                     nsAString& aDest,
-                                     const mozilla::fallible_t&);
+// UTF-16 to UTF-8
+// Unpaired surrogates are replaced with the REPLACEMENT CHARACTER.
+
+inline MOZ_MUST_USE bool CopyUTF16toUTF8(mozilla::Span<const char16_t> aSource,
+                                         nsACString& aDest,
+                                         const mozilla::fallible_t&)
+{
+  return nscstring_fallible_append_utf16_to_utf8_impl(&aDest, aSource.Elements(), aSource.Length(), 0);
+}
 
-void LossyAppendUTF16toASCII(const char16ptr_t aSource, nsACString& aDest);
-MOZ_MUST_USE bool AppendASCIItoUTF16(const char* aSource,
-                                     nsAString& aDest,
-                                     const mozilla::fallible_t&);
-void AppendASCIItoUTF16(const char* aSource, nsAString& aDest);
+inline void CopyUTF16toUTF8(mozilla::Span<const char16_t> aSource,
+                            nsACString& aDest)
+{
+  if (MOZ_UNLIKELY(!CopyUTF16toUTF8(aSource, aDest, mozilla::fallible))) {
+    aDest.AllocFailed(aSource.Length());
+  }
+}
+
+inline MOZ_MUST_USE bool AppendUTF16toUTF8(mozilla::Span<const char16_t> aSource,
+                                           nsACString& aDest,
+                                           const mozilla::fallible_t&)
+{
+  return nscstring_fallible_append_utf16_to_utf8_impl(&aDest, aSource.Elements(), aSource.Length(), aDest.Length());
+}
+
+inline void AppendUTF16toUTF8(mozilla::Span<const char16_t> aSource,
+                              nsACString& aDest)
+{
+  if (MOZ_UNLIKELY(!AppendUTF16toUTF8(aSource, aDest, mozilla::fallible))) {
+    aDest.AllocFailed(aDest.Length() + aSource.Length());
+  }
+}
 
-void AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest);
-MOZ_MUST_USE bool AppendUTF16toUTF8(const nsAString& aSource,
-                                    nsACString& aDest,
-                                    const mozilla::fallible_t&);
-void AppendUTF8toUTF16(const nsACString& aSource, nsAString& aDest);
-MOZ_MUST_USE bool AppendUTF8toUTF16(const nsACString& aSource,
-                                    nsAString& aDest,
-                                    const mozilla::fallible_t&);
+// UTF-16 to Latin1
+// If all code points in the input are below U+0100, represents each scalar value
+// as an unsigned byte. (This is not windows-1252!)
+// If there are code points above U+00FF, asserts in debug builds and memory-safely
+// produces garbage in release builds. The nature of the garbage may differ based on
+// CPU architecture and must not be relied upon.
+// The names say "ASCII" instead of "Latin1" for legacy reasons.
+
+inline MOZ_MUST_USE bool LossyCopyUTF16toASCII(mozilla::Span<const char16_t> aSource,
+                                         nsACString& aDest,
+                                         const mozilla::fallible_t&)
+{
+  return nscstring_fallible_append_utf16_to_latin1_lossy_impl(&aDest, aSource.Elements(), aSource.Length(), 0);
+}
 
-void AppendUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest);
-void AppendUTF8toUTF16(const char* aSource, nsAString& aDest);
+inline void LossyCopyUTF16toASCII(mozilla::Span<const char16_t> aSource,
+                            nsACString& aDest)
+{
+  if (MOZ_UNLIKELY(!LossyCopyUTF16toASCII(aSource, aDest, mozilla::fallible))) {
+    aDest.AllocFailed(aSource.Length());
+  }
+}
+
+inline MOZ_MUST_USE bool LossyAppendUTF16toASCII(mozilla::Span<const char16_t> aSource,
+                                           nsACString& aDest,
+                                           const mozilla::fallible_t&)
+{
+  return nscstring_fallible_append_utf16_to_latin1_lossy_impl(&aDest, aSource.Elements(), aSource.Length(), aDest.Length());
+}
+
+inline void LossyAppendUTF16toASCII(mozilla::Span<const char16_t> aSource,
+                              nsACString& aDest)
+{
+  if (MOZ_UNLIKELY(!LossyAppendUTF16toASCII(aSource, aDest, mozilla::fallible))) {
+    aDest.AllocFailed(aDest.Length() + aSource.Length());
+  }
+}
 
 /**
  * Returns a new |char| buffer containing a zero-terminated copy of |aSource|.
  *
  * Allocates and returns a new |char| buffer which you must free with |free|.
  * Performs a lossy encoding conversion by chopping 16-bit wide characters down to 8-bits wide while copying |aSource| to your new buffer.
  * This conversion is not well defined; but it reproduces legacy string behavior.
  * The new buffer is zero-terminated, but that may not help you if |aSource| contains embedded nulls.
  *
  * @param aSource a 16-bit wide string
  * @return a new |char| buffer you must free with |free|.
  */
 char* ToNewCString(const nsAString& aSource);
 
-
 /**
  * Returns a new |char| buffer containing a zero-terminated copy of |aSource|.
  *
  * Allocates and returns a new |char| buffer which you must free with |free|.
  * The new buffer is zero-terminated, but that may not help you if |aSource| contains embedded nulls.
  *
  * @param aSource an 8-bit wide string
  * @return a new |char| buffer you must free with |free|.
@@ -146,56 +325,25 @@ char16_t* ToNewUnicode(const nsAString& 
  * The new buffer is zero-terminated, but that may not help you if |aSource| contains embedded nulls.
  *
  * @param aSource an 8-bit wide string (a C-string, NOT UTF-8)
  * @return a new |char16_t| buffer you must free with |free|.
  */
 char16_t* ToNewUnicode(const nsACString& aSource);
 
 /**
- * Returns the required length for a char16_t buffer holding
- * a copy of aSource, using UTF-8 to UTF-16 conversion.
- * The length does NOT include any space for zero-termination.
- *
- * @param aSource an 8-bit wide string, UTF-8 encoded
- * @return length of UTF-16 encoded string copy, not zero-terminated
- */
-uint32_t CalcUTF8ToUnicodeLength(const nsACString& aSource);
-
-/**
- * Copies the source string into the specified buffer, converting UTF-8 to
- * UTF-16 in the process. The conversion is well defined for valid UTF-8
- * strings.
- * The copied string will be zero-terminated! Any embedded nulls will be
- * copied nonetheless. It is the caller's responsiblity to ensure the buffer
- * is large enough to hold the string copy plus one char16_t for
- * zero-termination!
- *
- * @see CalcUTF8ToUnicodeLength( const nsACString& )
- * @see UTF8ToNewUnicode( const nsACString&, uint32_t* )
- *
- * @param aSource an 8-bit wide string, UTF-8 encoded
- * @param aBuffer the buffer holding the converted string copy
- * @param aUTF16Count receiving optionally the number of 16-bit units that
- *                    were copied
- * @return aBuffer pointer, for convenience
- */
-char16_t* UTF8ToUnicodeBuffer(const nsACString& aSource,
-                              char16_t* aBuffer,
-                              uint32_t* aUTF16Count = nullptr);
-
-/**
  * Returns a new |char16_t| buffer containing a zero-terminated copy
  * of |aSource|.
  *
  * Allocates and returns a new |char| buffer which you must free with
  * |free|.  Performs an encoding conversion from UTF-8 to UTF-16
- * while copying |aSource| to your new buffer.  This conversion is well defined
- * for a valid UTF-8 string.  The new buffer is zero-terminated, but that
- * may not help you if |aSource| contains embedded nulls.
+ * while copying |aSource| to your new buffer.  Malformed byte sequences
+ * are replaced with the REPLACEMENT CHARACTER.  The new buffer is
+ * zero-terminated, but that may not help you if |aSource| contains
+ * embedded nulls.
  *
  * @param aSource an 8-bit wide string, UTF-8 encoded
  * @param aUTF16Count the number of 16-bit units that was returned
  * @return a new |char16_t| buffer you must free with |free|.
  *         (UTF-16 encoded)
  */
 char16_t* UTF8ToNewUnicode(const nsACString& aSource,
                            uint32_t* aUTF16Count = nullptr);
@@ -212,78 +360,68 @@ char16_t* UTF8ToNewUnicode(const nsACStr
  * @param aLength the number of 16-bit code units to copy
  * @return pointer to destination buffer - identical to |aDest|
  */
 char16_t* CopyUnicodeTo(const nsAString& aSource,
                         uint32_t aSrcOffset,
                         char16_t* aDest,
                         uint32_t aLength);
 
-
-/**
- * Copies 16-bit characters between iterators |aSrcStart| and
- * |aSrcEnd| to the writable string |aDest|. Similar to the
- * |nsString::Mid| method.
- *
- * After this operation |aDest| is not null terminated.
- *
- * @param aSrcStart start source iterator
- * @param aSrcEnd end source iterator
- * @param aDest destination for the copy
- */
-void CopyUnicodeTo(const nsAString::const_iterator& aSrcStart,
-                   const nsAString::const_iterator& aSrcEnd,
-                   nsAString& aDest);
-
-/**
- * Appends 16-bit characters between iterators |aSrcStart| and
- * |aSrcEnd| to the writable string |aDest|.
- *
- * After this operation |aDest| is not null terminated.
- *
- * @param aSrcStart start source iterator
- * @param aSrcEnd end source iterator
- * @param aDest destination for the copy
- */
-void AppendUnicodeTo(const nsAString::const_iterator& aSrcStart,
-                     const nsAString::const_iterator& aSrcEnd,
-                     nsAString& aDest);
-
 /**
  * Returns |true| if |aString| contains only ASCII characters, that is, characters in the range (0x00, 0x7F).
  *
  * @param aString a 16-bit wide string to scan
  */
-bool IsASCII(const nsAString& aString);
+inline bool IsASCII(mozilla::Span<const char16_t> aString)
+{
+  return encoding_mem_is_basic_latin(aString.Elements(), aString.Length());
+}
 
 /**
  * Returns |true| if |aString| contains only ASCII characters, that is, characters in the range (0x00, 0x7F).
  *
  * @param aString a 8-bit wide string to scan
  */
-inline bool IsASCII(const nsACString& aString)
+inline bool IsASCII(mozilla::Span<const char> aString)
+{
+  return encoding_mem_is_ascii(aString.Elements(), aString.Length());
+}
+
+/**
+ * Returns |true| if |aString| contains only Latin1 characters, that is, characters in the range (U+0000, U+00FF).
+ *
+ * @param aString a potentially-invalid UTF-16 string to scan
+ */
+inline bool IsUTF16Latin1(mozilla::Span<const char16_t> aString)
 {
-  size_t length = aString.Length();
-  const uint8_t* ptr = reinterpret_cast<const uint8_t*>(aString.BeginReading());
-  // For short strings, calling into Rust is a pessimization, and the SIMD
-  // code won't have a chance to kick in anyway. Additionally, handling the
-  // case of the empty string here makes null-checking ptr unnecessary.
-  // (Passing nullptr to Rust would technically be UB.)
-  if (length < 16) {
-    size_t accu = 0;
-    for (size_t i = 0; i < length; i++) {
-      accu |= ptr[i];
-    }
-    return accu < 0x80;
-  }
-  // This is not quite optimal, because it's not fail-fast when the by-register
-  // check already finds non-ASCII. Also, input to this function is almost
-  // always ASCII, so even the by-register check wouldn't need to be fail-fast
-  // and could be more like the loop above.
-  return length == encoding_ascii_valid_up_to(ptr, length);
+  return encoding_mem_is_utf16_latin1(aString.Elements(), aString.Length());
+}
+
+/**
+ * Returns |true| if |aString| contains only Latin1 characters, that is, characters in the range (U+0000, U+00FF).
+ *
+ * If you know that the argument is always absolutely guaranteed to be valid UTF-8, use the faster UnsafeIsValidUTF8Latin1() instead.
+ *
+ * @param aString potentially-invalid UTF-8 string to scan
+ */
+inline bool IsUTF8Latin1(mozilla::Span<const char> aString)
+{
+  return encoding_mem_is_utf8_latin1(aString.Elements(), aString.Length());
+}
+
+/**
+ * Returns |true| if |aString| contains only Latin1 characters, that is, characters in the range (U+0000, U+00FF).
+ *
+ * The argument MUST be valid UTF-8. If you at all unsure, use IsUTF8Latin1 instead!
+ *
+ * @param aString known-valid UTF-8 string to scan
+ */
+inline bool UnsafeIsValidUTF8Latin1(mozilla::Span<const char> aString)
+{
+  return encoding_mem_is_str_latin1(aString.Elements(), aString.Length());
 }
 
 /**
  * Returns |true| if |aString| is a valid UTF-8 string.
  *
  * Note that this doesn't check whether the string might look like a valid
  * string in another encoding, too, e.g. ISO-2022-JP.
  *
@@ -428,22 +566,24 @@ const nsCString& EmptyCString();
 
 const nsString& VoidString();
 const nsCString& VoidCString();
 
 /**
 * Compare a UTF-8 string to an UTF-16 string.
 *
 * Returns 0 if the strings are equal, -1 if aUTF8String is less
-* than aUTF16Count, and 1 in the reverse case.  In case of fatal
-* error (eg the strings are not valid UTF8 and UTF16 respectively),
-* this method will return INT32_MIN.
+* than aUTF16Count, and 1 in the reverse case. Errors are replaced
+* with U+FFFD and then the U+FFFD is compared as if it had occurred
+* in the input. If aErr is not nullptr, *aErr is set to true if
+* either string had malformed sequences.
 */
 int32_t CompareUTF8toUTF16(const nsACString& aUTF8String,
-                           const nsAString& aUTF16String);
+                           const nsAString& aUTF16String,
+                           bool* aErr = nullptr);
 
 void AppendUCS4ToUTF16(const uint32_t aSource, nsAString& aDest);
 
 template<class T>
 inline bool
 EnsureStringLength(T& aStr, uint32_t aLen)
 {
   aStr.SetLength(aLen);
deleted file mode 100644
--- a/xpcom/string/nsReadableUtilsImpl.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -*- 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 <stdint.h>
-
-namespace mozilla {
-
-inline bool IsASCII(char16_t aChar) {
-  return (aChar & 0xFF80) == 0;
-}
-
-/**
- * Provides a pointer before or equal to |aPtr| that is is suitably aligned.
- */
-inline const char16_t* aligned(const char16_t* aPtr, const uintptr_t aMask)
-{
-  return reinterpret_cast<const char16_t*>(
-      reinterpret_cast<uintptr_t>(aPtr) & ~aMask);
-}
-
-/**
- * Structures for word-sized vectorization of ASCII checking for UTF-16
- * strings.
- */
-template<size_t size> struct NonASCIIParameters;
-template<> struct NonASCIIParameters<4> {
-  static inline size_t mask() { return 0xff80ff80; }
-  static inline uintptr_t alignMask() { return 0x3; }
-  static inline size_t numUnicharsPerWord() { return 2; }
-};
-
-template<> struct NonASCIIParameters<8> {
-  static inline size_t mask() {
-    static const uint64_t maskAsUint64 = UINT64_C(0xff80ff80ff80ff80);
-    // We have to explicitly cast this 64-bit value to a size_t, or else
-    // compilers for 32-bit platforms will warn about it being too large to fit
-    // in the size_t return type. (Fortunately, this code isn't actually
-    // invoked on 32-bit platforms -- they'll use the <4> specialization above.
-    // So it is, in fact, OK that this value is too large for a 32-bit size_t.)
-    return (size_t)maskAsUint64;
-  }
-  static inline uintptr_t alignMask() { return 0x7; }
-  static inline size_t numUnicharsPerWord() { return 4; }
-};
-
-namespace SSE2 {
-
-int32_t FirstNonASCII(const char16_t* aBegin, const char16_t* aEnd);
-
-} // namespace SSE2
-} // namespace mozilla
deleted file mode 100644
--- a/xpcom/string/nsReadableUtilsSSE2.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/* -*- 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 <emmintrin.h>
-
-#include "nsReadableUtilsImpl.h"
-
-namespace mozilla {
-namespace SSE2 {
-
-static inline bool
-is_zero (__m128i x)
-{
-  return
-    _mm_movemask_epi8(_mm_cmpeq_epi8(x, _mm_setzero_si128())) == 0xffff;
-}
-
-int32_t
-FirstNonASCII(const char16_t* aBegin, const char16_t* aEnd)
-{
-  const size_t kNumUnicharsPerVector = sizeof(__m128i) / sizeof(char16_t);
-  typedef NonASCIIParameters<sizeof(size_t)> p;
-  const size_t kMask = p::mask();
-  const uintptr_t kXmmAlignMask = 0xf;
-  const uint16_t kShortMask = 0xff80;
-  const size_t kNumUnicharsPerWord = p::numUnicharsPerWord();
-
-  const char16_t* idx = aBegin;
-
-  // Align ourselves to a 16-byte boundary as required by _mm_load_si128
-  for (; idx != aEnd && ((uintptr_t(idx) & kXmmAlignMask) != 0); idx++) {
-    if (!IsASCII(*idx)) {
-      return idx - aBegin;
-    }
-  }
-
-  // Check one XMM register (16 bytes) at a time.
-  const char16_t* vectWalkEnd = aligned(aEnd, kXmmAlignMask);
-  __m128i vectmask = _mm_set1_epi16(static_cast<int16_t>(kShortMask));
-  for (; idx != vectWalkEnd; idx += kNumUnicharsPerVector) {
-    const __m128i vect = *reinterpret_cast<const __m128i*>(idx);
-    if (!is_zero(_mm_and_si128(vect, vectmask))) {
-      return idx - aBegin;
-    }
-  }
-
-  // Check one word at a time.
-  const char16_t* wordWalkEnd = aligned(aEnd, p::alignMask());
-  for(; idx != wordWalkEnd; idx += kNumUnicharsPerWord) {
-    const size_t word = *reinterpret_cast<const size_t*>(idx);
-    if (word & kMask) {
-      return idx - aBegin;
-    }
-  }
-
-  // Take care of the remainder one character at a time.
-  for (; idx != aEnd; idx++) {
-    if (!IsASCII(*idx)) {
-      return idx - aBegin;
-    }
-  }
-
-  return -1;
-}
-
-} // namespace SSE2
-} // namespace mozilla
--- a/xpcom/string/nsString.h
+++ b/xpcom/string/nsString.h
@@ -42,17 +42,17 @@ static_assert(sizeof(nsTLiteralString<ch
 /**
  * A helper class that converts a UTF-16 string to ASCII in a lossy manner
  */
 class NS_LossyConvertUTF16toASCII : public nsAutoCString
 {
 public:
   explicit NS_LossyConvertUTF16toASCII(const char16ptr_t aString)
   {
-    LossyAppendUTF16toASCII(aString, *this);
+    LossyAppendUTF16toASCII(mozilla::MakeStringSpan(aString), *this);
   }
 
   NS_LossyConvertUTF16toASCII(const char16ptr_t aString, uint32_t aLength)
   {
     LossyAppendUTF16toASCII(Substring(static_cast<const char16_t*>(aString), aLength), *this);
   }
 
   explicit NS_LossyConvertUTF16toASCII(const nsAString& aString)
@@ -66,17 +66,17 @@ private:
 };
 
 
 class NS_ConvertASCIItoUTF16 : public nsAutoString
 {
 public:
   explicit NS_ConvertASCIItoUTF16(const char* aCString)
   {
-    AppendASCIItoUTF16(aCString, *this);
+    AppendASCIItoUTF16(mozilla::MakeStringSpan(aCString), *this);
   }
 
   NS_ConvertASCIItoUTF16(const char* aCString, uint32_t aLength)
   {
     AppendASCIItoUTF16(Substring(aCString, aLength), *this);
   }
 
   explicit NS_ConvertASCIItoUTF16(const nsACString& aCString)
@@ -93,17 +93,17 @@ private:
 /**
  * A helper class that converts a UTF-16 string to UTF-8
  */
 class NS_ConvertUTF16toUTF8 : public nsAutoCString
 {
 public:
   explicit NS_ConvertUTF16toUTF8(const char16ptr_t aString)
   {
-    AppendUTF16toUTF8(aString, *this);
+    AppendUTF16toUTF8(mozilla::MakeStringSpan(aString), *this);
   }
 
   NS_ConvertUTF16toUTF8(const char16ptr_t aString, uint32_t aLength)
   {
     AppendUTF16toUTF8(Substring(static_cast<const char16_t*>(aString), aLength), *this);
   }
 
   explicit NS_ConvertUTF16toUTF8(const nsAString& aString)
@@ -117,17 +117,17 @@ private:
 };
 
 
 class NS_ConvertUTF8toUTF16 : public nsAutoString
 {
 public:
   explicit NS_ConvertUTF8toUTF16(const char* aCString)
   {
-    AppendUTF8toUTF16(aCString, *this);
+    AppendUTF8toUTF16(mozilla::MakeStringSpan(aCString), *this);
   }
 
   NS_ConvertUTF8toUTF16(const char* aCString, uint32_t aLength)
   {
     AppendUTF8toUTF16(Substring(aCString, aLength), *this);
   }
 
   explicit NS_ConvertUTF8toUTF16(const nsACString& aCString)
--- a/xpcom/string/nsSubstring.cpp
+++ b/xpcom/string/nsSubstring.cpp
@@ -451,16 +451,21 @@ char* Gecko_BeginWritingCString(nsACStri
   return aThis->BeginWriting();
 }
 
 char* Gecko_FallibleBeginWritingCString(nsACString* aThis)
 {
   return aThis->BeginWriting(mozilla::fallible);
 }
 
+char* Gecko_FallibleMaybeExpandCapacityCString(nsACString* aThis, uint32_t* aCapacity)
+{
+  return aThis->MaybeExpandCapacity(aCapacity, mozilla::fallible);
+}
+
 void Gecko_FinalizeString(nsAString* aThis)
 {
   aThis->~nsAString();
 }
 
 void Gecko_AssignString(nsAString* aThis, const nsAString* aOther)
 {
   aThis->Assign(*aOther);
@@ -506,9 +511,14 @@ char16_t* Gecko_BeginWritingString(nsASt
   return aThis->BeginWriting();
 }
 
 char16_t* Gecko_FallibleBeginWritingString(nsAString* aThis)
 {
   return aThis->BeginWriting(mozilla::fallible);
 }
 
+char16_t* Gecko_FallibleMaybeExpandCapacityString(nsAString* aThis, uint32_t* aCapacity)
+{
+  return aThis->MaybeExpandCapacity(aCapacity, mozilla::fallible);
+}
+
 } // extern "C"
--- a/xpcom/string/nsTSubstring.h
+++ b/xpcom/string/nsTSubstring.h
@@ -589,16 +589,44 @@ public:
     SetLength(aNewLength);
   }
 
 
   /**
    * buffer access
    */
 
+  /**
+   * If *aCapacity is larger than the current capacity, allocates a
+   * buffer whose length is at least *aCapacity.
+   *
+   * Sets *aCapacity and the string's length to the actual capacity.
+   *
+   * Returns a pointer to the start of the buffer or nullptr if
+   * allocation failed.
+   *
+   * Note that unlike GetMutableData, this rounds the length up to the
+   * capacity.
+   */
+  inline char_type* MaybeExpandCapacity(size_type* aCapacity, const fallible_t& aFallible)
+  {
+    // SetCapacity unshares a shared buffer even then resizing is not
+    // needed.
+    if (!SetCapacity(*aCapacity, aFallible)) {
+      return nullptr;
+    }
+    size_type capacity = Capacity();
+    // SetCapacity doesn't stretch the logical length for us.
+    this->mLength = capacity;
+    *aCapacity = capacity;
+    char_type* ptr = base_string_type::mData;
+    // SetCapacity zero-terminated at intermediate length, not capacity.
+    ptr[capacity] = 0;
+    return ptr;
+  }
 
   /**
    * Get a const pointer to the string's internal buffer.  The caller
    * MUST NOT modify the characters at the returned address.
    *
    * @returns The length of the buffer in characters.
    */
   inline size_type GetData(const char_type** aData) const
--- a/xpcom/string/nsUTF8Utils.h
+++ b/xpcom/string/nsUTF8Utils.h
@@ -6,20 +6,18 @@
 #ifndef nsUTF8Utils_h_
 #define nsUTF8Utils_h_
 
 // This file may be used in two ways: if MOZILLA_INTERNAL_API is defined, this
 // file will provide signatures for the Mozilla abstract string types. It will
 // use XPCOM assertion/debugging macros, etc.
 
 #include "nscore.h"
-#include "mozilla/arm.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/EndianUtils.h"
-#include "mozilla/SSE.h"
 #include "mozilla/TypeTraits.h"
 
 #include "nsCharTraits.h"
 
 #ifdef MOZILLA_INTERNAL_API
 #define UTF8UTILS_WARNING(msg) NS_WARNING(msg)
 #else
 #define UTF8UTILS_WARNING(msg)
@@ -43,16 +41,17 @@ public:
   static bool is3byte(char aChar)
   {
     return (aChar & 0xF0) == 0xE0;
   }
   static bool is4byte(char aChar)
   {
     return (aChar & 0xF8) == 0xF0;
   }
+
   static bool is5byte(char aChar)
   {
     return (aChar & 0xFC) == 0xF8;
   }
   static bool is6byte(char aChar)
   {
     return (aChar & 0xFE) == 0xFC;
   }
@@ -66,721 +65,186 @@ public:
       return 2;
     }
     if (is3byte(aChar)) {
       return 3;
     }
     if (is4byte(aChar)) {
       return 4;
     }
-    if (is5byte(aChar)) {
-      return 5;
-    }
-    if (is6byte(aChar)) {
-      return 6;
-    }
     MOZ_ASSERT_UNREACHABLE("should not be used for in-sequence characters");
     return 1;
   }
 };
 
 /**
- * Extract the next UCS-4 character from the buffer and return it.  The
+ * Extract the next Unicode scalar value from the buffer and return it. The
  * pointer passed in is advanced to the start of the next character in the
- * buffer.  If non-null, the parameters err and overlong are filled in to
- * indicate that the character was represented by an overlong sequence, or
- * that an error occurred.
+ * buffer. Upon error, the return value is 0xFFFD, *aBuffer is advanced
+ * over the maximal valid prefix and *aErr is set to true (if aErr is not
+ * null).
+ *
+ * Note: This method never sets *aErr to false to allow error accumulation
+ * across multiple calls.
+ *
+ * Precondition: *aBuffer < aEnd
  */
-
 class UTF8CharEnumerator
 {
 public:
-  static uint32_t NextChar(const char** aBuffer, const char* aEnd, bool* aErr)
+  static inline char32_t NextChar(const char** aBuffer, const char* aEnd, bool* aErr = nullptr)
   {
-    NS_ASSERTION(aBuffer && *aBuffer, "null buffer!");
-
-    const char* p = *aBuffer;
-    *aErr = false;
-
-    if (p >= aEnd) {
-      *aErr = true;
+    MOZ_ASSERT(aBuffer, "null buffer pointer pointer");
+    MOZ_ASSERT(aEnd, "null end pointer");
 
-      return 0;
-    }
-
-    char c = *p++;
-
-    if (UTF8traits::isASCII(c)) {
-      *aBuffer = p;
-      return c;
-    }
-
-    uint32_t ucs4;
-    uint32_t minUcs4;
-    int32_t state = 0;
+    const unsigned char* p = reinterpret_cast<const unsigned char*>(*aBuffer);
+    const unsigned char* end = reinterpret_cast<const unsigned char*>(aEnd);
 
-    if (!CalcState(c, ucs4, minUcs4, state)) {
-      NS_ERROR("Not a UTF-8 string. This code should only be used for converting from known UTF-8 strings.");
-      *aErr = true;
-
-      return 0;
-    }
-
-    while (state--) {
-      if (p == aEnd) {
-        *aErr = true;
-
-        return 0;
-      }
+    MOZ_ASSERT(p, "null buffer");
+    MOZ_ASSERT(p < end, "Bogus range");
 
-      c = *p++;
-
-      if (!AddByte(c, state, ucs4)) {
-        *aErr = true;
+    unsigned char first = *p++;
 
-        return 0;
-      }
-    }
-
-    if (ucs4 < minUcs4) {
-      // Overlong sequence
-      ucs4 = UCS2_REPLACEMENT_CHAR;
-    } else if (ucs4 >= 0xD800 &&
-               (ucs4 <= 0xDFFF || ucs4 >= UCS_END)) {
-      // Surrogates and code points outside the Unicode range.
-      ucs4 = UCS2_REPLACEMENT_CHAR;
+    if (MOZ_LIKELY(first < 0x80U)) {
+      *aBuffer = reinterpret_cast<const char*>(p);
+      return first;
     }
 
-    *aBuffer = p;
-    return ucs4;
-  }
-
-private:
-  static bool CalcState(char aChar, uint32_t& aUcs4, uint32_t& aMinUcs4,
-                        int32_t& aState)
-  {
-    if (UTF8traits::is2byte(aChar)) {
-      aUcs4 = (uint32_t(aChar) << 6) & 0x000007C0L;
-      aState = 1;
-      aMinUcs4 = 0x00000080;
-    } else if (UTF8traits::is3byte(aChar)) {
-      aUcs4 = (uint32_t(aChar) << 12) & 0x0000F000L;
-      aState = 2;
-      aMinUcs4 = 0x00000800;
-    } else if (UTF8traits::is4byte(aChar)) {
-      aUcs4 = (uint32_t(aChar) << 18) & 0x001F0000L;
-      aState = 3;
-      aMinUcs4 = 0x00010000;
-    } else if (UTF8traits::is5byte(aChar)) {
-      aUcs4 = (uint32_t(aChar) << 24) & 0x03000000L;
-      aState = 4;
-      aMinUcs4 = 0x00200000;
-    } else if (UTF8traits::is6byte(aChar)) {
-      aUcs4 = (uint32_t(aChar) << 30) & 0x40000000L;
-      aState = 5;
-      aMinUcs4 = 0x04000000;
-    } else {
-      return false;
+    // Unsigned underflow is defined behavior
+    if (MOZ_UNLIKELY((p == end) || ((first - 0xC2U) >= (0xF5U - 0xC2U)))) {
+      *aBuffer = reinterpret_cast<const char*>(p);
+      if (aErr) {
+        *aErr = true;
+      }
+      return 0xFFFDU;
     }
 
-    return true;
-  }
-
-  static bool AddByte(char aChar, int32_t aState, uint32_t& aUcs4)
-  {
-    if (UTF8traits::isInSeq(aChar)) {
-      int32_t shift = aState * 6;
-      aUcs4 |= (uint32_t(aChar) & 0x3F) << shift;
-      return true;
-    }
-
-    return false;
-  }
-};
-
+    unsigned char second = *p;
 
-/**
- * Extract the next UCS-4 character from the buffer and return it.  The
- * pointer passed in is advanced to the start of the next character in the
- * buffer.  If non-null, the err parameter is filled in if an error occurs.
- *
- * If an error occurs that causes UCS2_REPLACEMENT_CHAR to be returned, then
- * the buffer will be updated to move only a single UCS-2 character.
- *
- * Any other error returns 0 and does not move the buffer position.
- */
-
-
-class UTF16CharEnumerator
-{
-public:
-  static uint32_t NextChar(const char16_t** aBuffer, const char16_t* aEnd,
-                           bool* aErr = nullptr)
-  {
-    NS_ASSERTION(aBuffer && *aBuffer, "null buffer!");
-
-    const char16_t* p = *aBuffer;
-
-    if (p >= aEnd) {
-      NS_ERROR("No input to work with");
+    if (first < 0xE0U) {
+      // Two-byte
+      if (MOZ_LIKELY((second & 0xC0U) == 0x80U)) {
+        *aBuffer = reinterpret_cast<const char*>(++p);
+        return ((uint32_t(first) & 0x1FU) << 6) | (uint32_t(second) & 0x3FU);
+      }
+      *aBuffer = reinterpret_cast<const char*>(p);
       if (aErr) {
         *aErr = true;
       }
-
-      return 0;
+      return 0xFFFDU;
     }
 
-    char16_t c = *p++;
-
-    if (!IS_SURROGATE(c)) { // U+0000 - U+D7FF,U+E000 - U+FFFF
-      if (aErr) {
-        *aErr = false;
-      }
-      *aBuffer = p;
-      return c;
-    } else if (NS_IS_HIGH_SURROGATE(c)) { // U+D800 - U+DBFF
-      if (p == aEnd) {
-        // Found a high surrogate at the end of the buffer. Flag this
-        // as an error and return the Unicode replacement
-        // character 0xFFFD.
-
-        UTF8UTILS_WARNING("Unexpected end of buffer after high surrogate");
-
-        if (aErr) {
-          *aErr = true;
-        }
-        *aBuffer = p;
-        return 0xFFFD;
+    if (MOZ_LIKELY(first < 0xF0U)) {
+      // Three-byte
+      unsigned char lower = 0x80U;
+      unsigned char upper = 0xBFU;
+      if (first == 0xE0U) {
+        lower = 0xA0U;
+      } else if (first == 0xEDU) {
+        upper = 0x9FU;
       }
-
-      // D800- DBFF - High Surrogate
-      char16_t h = c;
-
-      c = *p++;
-
-      if (NS_IS_LOW_SURROGATE(c)) {
-        // DC00- DFFF - Low Surrogate
-        // N = (H - D800) *400 + 10000 + (L - DC00)
-        uint32_t ucs4 = SURROGATE_TO_UCS4(h, c);
-        if (aErr) {
-          *aErr = false;
+      if (MOZ_LIKELY(second >= lower && second <= upper)) {
+        if (MOZ_LIKELY(p != end)) {
+          unsigned char third = *++p;
+          if (MOZ_LIKELY((third & 0xC0U) == 0x80U)) {
+            *aBuffer = reinterpret_cast<const char*>(++p);
+            return ((uint32_t(first) & 0xFU) << 12) | ((uint32_t(second) & 0x3FU) << 6) | (uint32_t(third) & 0x3FU);
+          }
         }
-        *aBuffer = p;
-        return ucs4;
-      } else {
-        // Found a high surrogate followed by something other than
-        // a low surrogate. Flag this as an error and return the
-        // Unicode replacement character 0xFFFD.  Note that the
-        // pointer to the next character points to the second 16-bit
-        // value, not beyond it, as per Unicode 5.0.0 Chapter 3 C10,
-        // only the first code unit of an illegal sequence must be
-        // treated as an illegally terminated code unit sequence
-        // (also Chapter 3 D91, "isolated [not paired and ill-formed]
-        // UTF-16 code units in the range D800..DFFF are ill-formed").
-        UTF8UTILS_WARNING("got a High Surrogate but no low surrogate");
-
-        if (aErr) {
-          *aErr = true;
-        }
-        *aBuffer = p - 1;
-        return 0xFFFD;
       }
-    } else { // U+DC00 - U+DFFF
-      // DC00- DFFF - Low Surrogate
-
-      // Found a low surrogate w/o a preceding high surrogate. Flag
-      // this as an error and return the Unicode replacement
-      // character 0xFFFD.
-
-      UTF8UTILS_WARNING("got a low Surrogate but no high surrogate");
+      *aBuffer = reinterpret_cast<const char*>(p);
       if (aErr) {
         *aErr = true;
       }
-      *aBuffer = p;
-      return 0xFFFD;
+      return 0xFFFDU;
     }
 
-    MOZ_ASSERT_UNREACHABLE("Impossible UCS-2 character value.");
-  }
-};
-
-
-/**
- * A character sink (see |copy_string| in nsAlgorithm.h) for converting
- * UTF-8 to UTF-16
- */
-class ConvertUTF8toUTF16
-{
-public:
-  typedef char value_type;
-  typedef char16_t buffer_type;
-
-  explicit ConvertUTF8toUTF16(buffer_type* aBuffer)
-    : mStart(aBuffer), mBuffer(aBuffer), mErrorEncountered(false)
-  {
-  }
-
-  size_t Length() const
-  {
-    return mBuffer - mStart;
-  }
-
-  bool ErrorEncountered() const
-  {
-    return mErrorEncountered;
-  }
-
-  void write(const value_type* aStart, uint32_t aN)
-  {
-    if (mErrorEncountered) {
-      return;
+    // Four-byte
+    unsigned char lower = 0x80U;
+    unsigned char upper = 0xBFU;
+    if (first == 0xF0U) {
+      lower = 0x90U;
+    } else if (first == 0xF4U) {
+      upper = 0x8FU;
     }
-
-    // algorithm assumes utf8 units won't
-    // be spread across fragments
-    const value_type* p = aStart;
-    const value_type* end = aStart + aN;
-    buffer_type* out = mBuffer;
-    for (; p != end /* && *p */;) {
-      bool err;
-      uint32_t ucs4 = UTF8CharEnumerator::NextChar(&p, end, &err);
-
-      if (err) {
-        mErrorEncountered = true;
-        mBuffer = out;
-        return;
-      }
-
-      if (ucs4 >= PLANE1_BASE) {
-        *out++ = (buffer_type)H_SURROGATE(ucs4);
-        *out++ = (buffer_type)L_SURROGATE(ucs4);
-      } else {
-        *out++ = ucs4;
+    if (MOZ_LIKELY(second >= lower && second <= upper)) {
+      if (MOZ_LIKELY(p != end)) {
+        unsigned char third = *++p;
+        if (MOZ_LIKELY((third & 0xC0U) == 0x80U)) {
+          if (MOZ_LIKELY(p != end)) {
+            unsigned char fourth = *++p;
+            if (MOZ_LIKELY((fourth & 0xC0U) == 0x80U)) {
+              *aBuffer = reinterpret_cast<const char*>(++p);
+              return ((uint32_t(first) & 0x7U) << 18) | ((uint32_t(second) & 0x3FU) << 12) | ((uint32_t(third) & 0x3FU) << 6) | (uint32_t(fourth) & 0x3FU);
+            }
+          }
+        }
       }
     }
-    mBuffer = out;
+    *aBuffer = reinterpret_cast<const char*>(p);
+    if (aErr) {
+      *aErr = true;
+    }
+    return 0xFFFDU;
   }
-
-  void write_terminator()
-  {
-    *mBuffer = buffer_type(0);
-  }
-
-private:
-  buffer_type* const mStart;
-  buffer_type* mBuffer;
-  bool mErrorEncountered;
 };
 
 /**
- * A character sink (see |copy_string| in nsAlgorithm.h) for computing
- * the length of the UTF-16 string equivalent to a UTF-8 string.
+ * Extract the next Unicode scalar value from the buffer and return it. The
+ * pointer passed in is advanced to the start of the next character in the
+ * buffer. Upon error, the return value is 0xFFFD, *aBuffer is advanced over
+ * the unpaired surrogate and *aErr is set to true (if aErr is not null).
+ *
+ * Note: This method never sets *aErr to false to allow error accumulation
+ * across multiple calls.
+ *
+ * Precondition: *aBuffer < aEnd
  */
-class CalculateUTF8Length
+class UTF16CharEnumerator
 {
 public:
-  typedef char value_type;
-
-  CalculateUTF8Length()
-    : mLength(0), mErrorEncountered(false)
-  {
-  }
-
-  size_t Length() const
-  {
-    return mLength;
-  }
-
-  void write(const value_type* aStart, uint32_t aN)
+  static inline char32_t NextChar(const char16_t** aBuffer, const char16_t* aEnd, bool* aErr = nullptr)
   {
-    // ignore any further requests
-    if (mErrorEncountered) {
-      return;
-    }
-
-    // algorithm assumes utf8 units won't
-    // be spread across fragments
-    const value_type* p = aStart;
-    const value_type* end = aStart + aN;
-    for (; p < end /* && *p */; ++mLength) {
-      if (UTF8traits::isASCII(*p)) {
-        p += 1;
-      } else if (UTF8traits::is2byte(*p)) {
-        p += 2;
-      } else if (UTF8traits::is3byte(*p)) {
-        p += 3;
-      } else if (UTF8traits::is4byte(*p)) {
-        // Because a UTF-8 sequence of 4 bytes represents a codepoint
-        // greater than 0xFFFF, it will become a surrogate pair in the
-        // UTF-16 string, so add 1 more to mLength.
-        // This doesn't happen with is5byte and is6byte because they
-        // are illegal UTF-8 sequences (greater than 0x10FFFF) so get
-        // converted to a single replacement character.
-
-        // However, there is one case when a 4 byte UTF-8 sequence will
-        // only generate 2 UTF-16 bytes. If we have a properly encoded
-        // sequence, but with an invalid value (too small or too big),
-        // that will result in a replacement character being written
-        // This replacement character is encoded as just 1 single
-        // UTF-16 character, which is 2 bytes.
+    MOZ_ASSERT(aBuffer, "null buffer pointer pointer");
+    MOZ_ASSERT(aEnd, "null end pointer");
 
-        // The below code therefore only adds 1 to mLength if the UTF8
-        // data will produce a decoded character which is greater than
-        // or equal to 0x010000 and less than 0x0110000.
-
-        // A 4byte UTF8 character is encoded as
-        // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
-        // Bit 1-3 on the first byte, and bit 5-6 on the second byte,
-        // map to bit 17-21 in the final result. If these bits are
-        // between 0x01 and 0x11, that means that the final result is
-        // between 0x010000 and 0x110000. The below code reads these
-        // bits out and assigns them to c, but shifted up 4 bits to
-        // avoid having to shift twice.
-
-        // It doesn't matter what to do in the case where p + 4 > end
-        // since no UTF16 characters will be written in that case by
-        // ConvertUTF8toUTF16. Likewise it doesn't matter what we do if
-        // any of the surrogate bits are wrong since no UTF16
-        // characters will be written in that case either.
+    const char16_t* p = *aBuffer;
 
-        if (p + 4 <= end) {
-          uint32_t c = ((uint32_t)(p[0] & 0x07)) << 6 |
-                       ((uint32_t)(p[1] & 0x30));
-          if (c >= 0x010 && c < 0x110) {
-            ++mLength;
-          }
-        }
+    MOZ_ASSERT(p, "null buffer");
+    MOZ_ASSERT(p < aEnd, "Bogus range");
 
-        p += 4;
-      } else if (UTF8traits::is5byte(*p)) {
-        p += 5;
-      } else if (UTF8traits::is6byte(*p)) {
-        p += 6;
-      } else { // error
-        ++mLength; // to account for the decrement below
-        break;
-      }
-    }
-    if (p != end) {
-      NS_ERROR("Not a UTF-8 string. This code should only be used for converting from known UTF-8 strings.");
-      --mLength; // The last multi-byte char wasn't complete, discard it.
-      mErrorEncountered = true;
-    }
-  }
-
-private:
-  size_t mLength;
-  bool mErrorEncountered;
-};
+    char16_t c = *p++;
 
-/**
- * A character sink (see |copy_string| in nsAlgorithm.h) for
- * converting UTF-16 to UTF-8. Treats invalid UTF-16 data as 0xFFFD
- * (0xEFBFBD in UTF-8).
- */
-class ConvertUTF16toUTF8
-{
-public:
-  typedef char16_t value_type;
-  typedef char buffer_type;
-
-  // The error handling here is more lenient than that in
-  // |ConvertUTF8toUTF16|, but it's that way for backwards
-  // compatibility.
-
-  explicit ConvertUTF16toUTF8(buffer_type* aBuffer)
-    : mStart(aBuffer), mBuffer(aBuffer)
-  {
-  }
-
-  size_t Size() const
-  {
-    return mBuffer - mStart;
-  }
-
-  void write(const value_type* aStart, uint32_t aN)
-  {
-    buffer_type* out = mBuffer; // gcc isn't smart enough to do this!
-
-    for (const value_type* p = aStart, *end = aStart + aN; p < end; ++p) {
-      value_type c = *p;
-      if (!(c & 0xFF80)) { // U+0000 - U+007F
-        *out++ = (char)c;
-      } else if (!(c & 0xF800)) { // U+0100 - U+07FF
-        *out++ = 0xC0 | (char)(c >> 6);
-        *out++ = 0x80 | (char)(0x003F & c);
-      } else if (!IS_SURROGATE(c)) { // U+0800 - U+D7FF,U+E000 - U+FFFF
-        *out++ = 0xE0 | (char)(c >> 12);
-        *out++ = 0x80 | (char)(0x003F & (c >> 6));
-        *out++ = 0x80 | (char)(0x003F & c);
-      } else if (NS_IS_HIGH_SURROGATE(c)) { // U+D800 - U+DBFF
-        // D800- DBFF - High Surrogate
-        value_type h = c;
-
-        ++p;
-        if (p == end) {
-          // Treat broken characters as the Unicode
-          // replacement character 0xFFFD (0xEFBFBD in
-          // UTF-8)
-          *out++ = '\xEF';
-          *out++ = '\xBF';
-          *out++ = '\xBD';
-
-          UTF8UTILS_WARNING("String ending in half a surrogate pair!");
-
-          break;
+    // Let's use encoding_rs-style code golf here.
+    // Unsigned underflow is defined behavior
+    char16_t cMinusSurrogateStart = c - 0xD800U;
+    if (MOZ_LIKELY(cMinusSurrogateStart > (0xDFFFU - 0xD800U))) {
+      *aBuffer = p;
+      return c;
+    }
+    if (MOZ_LIKELY(cMinusSurrogateStart <= (0xDBFFU - 0xD800U))) {
+      // High surrogate
+      if (MOZ_LIKELY(p != aEnd)) {
+        char16_t second = *p;
+        // Unsigned underflow is defined behavior
+        if (MOZ_LIKELY((second - 0xDC00U) <= (0xDFFFU - 0xDC00U))) {
+          *aBuffer = ++p;
+          return (uint32_t(c) << 10) + uint32_t(second) - (((0xD800U << 10) - 0x10000U) + 0xDC00U);
         }
-        c = *p;
-
-        if (NS_IS_LOW_SURROGATE(c)) {
-          // DC00- DFFF - Low Surrogate
-          // N = (H - D800) *400 + 10000 + ( L - DC00 )
-          uint32_t ucs4 = SURROGATE_TO_UCS4(h, c);
-
-          // 0001 0000-001F FFFF
-          *out++ = 0xF0 | (char)(ucs4 >> 18);
-          *out++ = 0x80 | (char)(0x003F & (ucs4 >> 12));
-          *out++ = 0x80 | (char)(0x003F & (ucs4 >> 6));
-          *out++ = 0x80 | (char)(0x003F & ucs4);
-        } else {
-          // Treat broken characters as the Unicode
-          // replacement character 0xFFFD (0xEFBFBD in
-          // UTF-8)
-          *out++ = '\xEF';
-          *out++ = '\xBF';
-          *out++ = '\xBD';
-
-          // The pointer to the next character points to the second
-          // 16-bit value, not beyond it, as per Unicode 5.0.0
-          // Chapter 3 C10, only the first code unit of an illegal
-          // sequence must be treated as an illegally terminated
-          // code unit sequence (also Chapter 3 D91, "isolated [not
-          // paired and ill-formed] UTF-16 code units in the range
-          // D800..DFFF are ill-formed").
-          p--;
-
-          UTF8UTILS_WARNING("got a High Surrogate but no low surrogate");
-        }
-      } else { // U+DC00 - U+DFFF
-        // Treat broken characters as the Unicode replacement
-        // character 0xFFFD (0xEFBFBD in UTF-8)
-        *out++ = '\xEF';
-        *out++ = '\xBF';
-        *out++ = '\xBD';
-
-        // DC00- DFFF - Low Surrogate
-        UTF8UTILS_WARNING("got a low Surrogate but no high surrogate");
       }
     }
-
-    mBuffer = out;
-  }
-
-  void write_terminator()
-  {
-    *mBuffer = buffer_type(0);
-  }
-
-private:
-  buffer_type* const mStart;
-  buffer_type* mBuffer;
-};
-
-/**
- * A character sink (see |copy_string| in nsAlgorithm.h) for computing
- * the number of bytes a UTF-16 would occupy in UTF-8. Treats invalid
- * UTF-16 data as 0xFFFD (0xEFBFBD in UTF-8).
- */
-class CalculateUTF8Size
-{
-public:
-  typedef char16_t value_type;
-
-  CalculateUTF8Size()
-    : mSize(0)
-  {
-  }
-
-  size_t Size() const
-  {
-    return mSize;
+    // Unpaired surrogate
+    *aBuffer = p;
+    if (aErr) {
+      *aErr = true;
+    }
+    return 0xFFFDU;
   }
-
-  void write(const value_type* aStart, uint32_t aN)
-  {
-    // Assume UCS2 surrogate pairs won't be spread across fragments.
-    for (const value_type* p = aStart, *end = aStart + aN; p < end; ++p) {
-      value_type c = *p;
-      if (!(c & 0xFF80)) { // U+0000 - U+007F
-        mSize += 1;
-      } else if (!(c & 0xF800)) { // U+0100 - U+07FF
-        mSize += 2;
-      } else if (0xD800 != (0xF800 & c)) { // U+0800 - U+D7FF,U+E000 - U+FFFF
-        mSize += 3;
-      } else if (0xD800 == (0xFC00 & c)) { // U+D800 - U+DBFF
-        ++p;
-        if (p == end) {
-          // Treat broken characters as the Unicode
-          // replacement character 0xFFFD (0xEFBFBD in
-          // UTF-8)
-          mSize += 3;
-
-          UTF8UTILS_WARNING("String ending in half a surrogate pair!");
-
-          break;
-        }
-        c = *p;
-
-        if (0xDC00 == (0xFC00 & c)) {
-          mSize += 4;
-        } else {
-          // Treat broken characters as the Unicode
-          // replacement character 0xFFFD (0xEFBFBD in
-          // UTF-8)
-          mSize += 3;
-
-          // The next code unit is the second 16-bit value, not
-          // the one beyond it, as per Unicode 5.0.0 Chapter 3 C10,
-          // only the first code unit of an illegal sequence must
-          // be treated as an illegally terminated code unit
-          // sequence (also Chapter 3 D91, "isolated [not paired and
-          // ill-formed] UTF-16 code units in the range D800..DFFF
-          // are ill-formed").
-          p--;
-
-          UTF8UTILS_WARNING("got a high Surrogate but no low surrogate");
-        }
-      } else { // U+DC00 - U+DFFF
-        // Treat broken characters as the Unicode replacement
-        // character 0xFFFD (0xEFBFBD in UTF-8)
-        mSize += 3;
-
-        UTF8UTILS_WARNING("got a low Surrogate but no high surrogate");
-      }
-    }
-  }
-
-private:
-  size_t mSize;
 };
 
-#ifdef MOZILLA_INTERNAL_API
-/**
- * A character sink that performs a |reinterpret_cast|-style conversion
- * from char to char16_t.
- */
-class LossyConvertEncoding8to16
-{
-public:
-  typedef char value_type;
-  typedef char input_type;
-  typedef char16_t output_type;
-
-public:
-  explicit LossyConvertEncoding8to16(char16_t* aDestination) :
-    mDestination(aDestination)
-  {
-  }
-
-  void
-  write(const char* aSource, uint32_t aSourceLength)
-  {
-#ifdef MOZILLA_MAY_SUPPORT_SSE2
-    if (mozilla::supports_sse2()) {
-      write_sse2(aSource, aSourceLength);
-      return;
-    }
-#endif
-#if defined(MOZILLA_MAY_SUPPORT_NEON) && defined(MOZ_LITTLE_ENDIAN)
-    if (mozilla::supports_neon()) {
-      write_neon(aSource, aSourceLength);
-      return;
-    }
-#endif
-    const char* done_writing = aSource + aSourceLength;
-    while (aSource < done_writing) {
-      *mDestination++ = (char16_t)(unsigned char)(*aSource++);
-    }
-  }
-
-  void
-  write_sse2(const char* aSource, uint32_t aSourceLength);
-#if defined(MOZILLA_MAY_SUPPORT_NEON) && defined(MOZ_LITTLE_ENDIAN)
-  void
-  write_neon(const char* aSource, uint32_t aSourceLength);
-#endif
-
-  void
-  write_terminator()
-  {
-    *mDestination = (char16_t)(0);
-  }
-
-private:
-  char16_t* mDestination;
-};
-
-/**
- * A character sink that performs a |reinterpret_cast|-style conversion
- * from char16_t to char.
- */
-class LossyConvertEncoding16to8
-{
-public:
-  typedef char16_t value_type;
-  typedef char16_t input_type;
-  typedef char output_type;
-
-  explicit LossyConvertEncoding16to8(char* aDestination)
-    : mDestination(aDestination)
-  {
-  }
-
-  void
-  write(const char16_t* aSource, uint32_t aSourceLength)
-  {
-#ifdef MOZILLA_MAY_SUPPORT_SSE2
-    if (mozilla::supports_sse2()) {
-      write_sse2(aSource, aSourceLength);
-      return;
-    }
-#endif
-#if defined(MOZILLA_MAY_SUPPORT_NEON) && defined(MOZ_LITTLE_ENDIAN)
-    if (mozilla::supports_neon()) {
-      write_neon(aSource, aSourceLength);
-      return;
-    }
-#endif
-    const char16_t* done_writing = aSource + aSourceLength;
-    while (aSource < done_writing) {
-      *mDestination++ = (char)(*aSource++);
-    }
-  }
-
-#ifdef MOZILLA_MAY_SUPPORT_SSE2
-  void
-  write_sse2(const char16_t* aSource, uint32_t aSourceLength);
-#endif
-#if defined(MOZILLA_MAY_SUPPORT_NEON) && defined(MOZ_LITTLE_ENDIAN)
-  void
-  write_neon(const char16_t* aSource, uint32_t aSourceLength);
-#endif
-
-  void
-  write_terminator()
-  {
-    *mDestination = '\0';
-  }
-
-private:
-  char* mDestination;
-};
-#endif // MOZILLA_INTERNAL_API
-
-
 template<typename Char, typename UnsignedT>
 inline UnsignedT
 RewindToPriorUTF8Codepoint(const Char* utf8Chars, UnsignedT index)
 {
   static_assert(mozilla::IsSame<Char, char>::value ||
                 mozilla::IsSame<Char, unsigned char>::value ||
                 mozilla::IsSame<Char, signed char>::value,
                 "UTF-8 data must be in 8-bit units");
deleted file mode 100644
--- a/xpcom/string/nsUTF8UtilsNEON.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/* -*- 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 "nscore.h"
-#include "nsAlgorithm.h"
-#include "nsUTF8Utils.h"
-
-#include <arm_neon.h>
-
-void
-LossyConvertEncoding16to8::write_neon(const char16_t* aSource,
-                                      uint32_t aSourceLength)
-{
-  char* dest = mDestination;
-
-  // Align source to a 16-byte boundary and destination to 8-bytes boundary.
-  uint32_t i = 0;
-  while (((reinterpret_cast<uintptr_t>(aSource + i) & 0xf) ||
-          (reinterpret_cast<uintptr_t>(dest + i) & 0x7)) &&
-         i < aSourceLength) {
-    dest[i] = static_cast<unsigned char>(aSource[i]);
-    i++;
-  }
-
-  while ((reinterpret_cast<uintptr_t>(dest + i) & 0xf) &&
-         aSourceLength - i > 7) {
-    // source is aligned, but destination isn't aligned by 16-byte yet
-    uint16x8_t s =
-      vld1q_u16(reinterpret_cast<const uint16_t*>(
-                  __builtin_assume_aligned(aSource + i, 16)));
-    vst1_u8(reinterpret_cast<uint8_t*>(
-              __builtin_assume_aligned(dest + i, 8)),
-            vmovn_u16(s));
-    i += 8;
-  }
-
-  // Align source and destination to a 16-byte boundary.
-  while (aSourceLength - i > 15) {
-    uint16x8_t low =
-      vld1q_u16(reinterpret_cast<const uint16_t*>(
-                  __builtin_assume_aligned(aSource + i, 16)));
-    uint16x8_t high =
-      vld1q_u16(reinterpret_cast<const uint16_t*>(
-                  __builtin_assume_aligned(aSource + i + 8, 16)));
-    vst1q_u8(reinterpret_cast<uint8_t*>(
-               __builtin_assume_aligned(dest + i, 16)),
-             vcombine_u8(vmovn_u16(low), vmovn_u16(high)));
-    i += 16;
-  }
-
-  if (aSourceLength - i > 7) {
-    uint16x8_t s = vld1q_u16(reinterpret_cast<const uint16_t*>(
-                               __builtin_assume_aligned(aSource + i, 16)));
-    vst1_u8(reinterpret_cast<uint8_t*>(
-              __builtin_assume_aligned(dest + i, 8)),
-            vmovn_u16(s));
-    i += 8;
-  }
-
-  // Finish up the rest.
-  for (; i < aSourceLength; ++i) {
-    dest[i] = static_cast<unsigned char>(aSource[i]);
-  }
-
-  mDestination += i;
-}
-
-void
-LossyConvertEncoding8to16::write_neon(const char* aSource,
-                                      uint32_t aSourceLength)
-{
-  char16_t* dest = mDestination;
-
-  // Align source to a 8-byte boundary and destination to 16-bytes boundary.
-  uint32_t i = 0;
-  while (((reinterpret_cast<uintptr_t>(aSource + i) & 0x7) ||
-          (reinterpret_cast<uintptr_t>(dest + i) & 0xf)) &&
-         i < aSourceLength) {
-    dest[i] = static_cast<unsigned char>(aSource[i]);
-    i++;
-  }
-
-  if ((uintptr_t(aSource + i) & 0xf) && aSourceLength - i > 7) {
-    // destination is aligned, but source isn't aligned by 16-byte yet
-    uint8x8_t s =
-      vld1_u8(reinterpret_cast<const uint8_t*>(
-                __builtin_assume_aligned(aSource + i, 8)));
-    vst1q_u16(reinterpret_cast<uint16_t*>(
-                __builtin_assume_aligned(dest + i, 16)),
-              vmovl_u8(s));
-    i += 8;
-  }
-
-  // Align source and destination to a 16-byte boundary.
-  while (aSourceLength - i > 15) {
-    uint8x16_t s =
-      vld1q_u8(reinterpret_cast<const uint8_t*>(
-                 __builtin_assume_aligned(aSource + i, 16)));
-    uint16x8_t low = vmovl_u8(vget_low_u8(s));
-    uint16x8_t high = vmovl_u8(vget_high_u8(s));
-    vst1q_u16(reinterpret_cast<uint16_t*>(
-                __builtin_assume_aligned(dest + i, 16)),
-              low);
-    vst1q_u16(reinterpret_cast<uint16_t*>(
-                __builtin_assume_aligned(dest + i + 8, 16)),
-              high);
-    i += 16;
-  }
-
-  if (aSourceLength - i > 7) {
-    uint8x8_t s =
-      vld1_u8(reinterpret_cast<const uint8_t*>(
-                __builtin_assume_aligned(aSource + i, 8)));
-    vst1q_u16(reinterpret_cast<uint16_t*>(
-                __builtin_assume_aligned(dest + i, 16)),
-              vmovl_u8(s));
-    i += 8;
-  }
-
-  // Finish up whatever's left.
-  for (; i < aSourceLength; ++i) {
-    dest[i] = static_cast<unsigned char>(aSource[i]);
-  }
-
-  mDestination += i;
-}
deleted file mode 100644
--- a/xpcom/string/nsUTF8UtilsSSE2.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/* -*- 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 "nscore.h"
-#include "nsAlgorithm.h"
-#include <emmintrin.h>
-#include <nsUTF8Utils.h>
-
-void
-LossyConvertEncoding16to8::write_sse2(const char16_t* aSource,
-                                      uint32_t aSourceLength)
-{
-  char* dest = mDestination;
-
-  // Align source to a 16-byte boundary.
-  uint32_t i = 0;
-  uint32_t alignLen =
-    XPCOM_MIN<uint32_t>(aSourceLength,
-                        uint32_t(-NS_PTR_TO_INT32(aSource) & 0xf) / sizeof(char16_t));
-  for (; i < alignLen; ++i) {
-    dest[i] = static_cast<unsigned char>(aSource[i]);
-  }
-
-  // Walk 64 bytes (four XMM registers) at a time.
-  __m128i vectmask = _mm_set1_epi16(0x00ff);
-  for (; aSourceLength - i > 31; i += 32) {
-    __m128i source1 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i));
-    source1 = _mm_and_si128(source1, vectmask);
-
-    __m128i source2 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i + 8));
-    source2 = _mm_and_si128(source2, vectmask);
-
-    __m128i source3 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i + 16));
-    source3 = _mm_and_si128(source3, vectmask);
-
-    __m128i source4 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i + 24));
-    source4 = _mm_and_si128(source4, vectmask);
-
-
-    // Pack the source data.  SSE2 views this as a saturating uint16_t to
-    // uint8_t conversion, but since we masked off the high-order byte of every
-    // uint16_t, we're really just grabbing the low-order bytes of source1 and
-    // source2.
-    __m128i packed1 = _mm_packus_epi16(source1, source2);
-    __m128i packed2 = _mm_packus_epi16(source3, source4);
-
-    // This store needs to be unaligned since there's no guarantee that the
-    // alignment we did above for the source will align the destination.
-    _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i),      packed1);
-    _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i + 16), packed2);
-  }
-
-  // Finish up the rest.
-  for (; i < aSourceLength; ++i) {
-    dest[i] = static_cast<unsigned char>(aSource[i]);
-  }
-
-  mDestination += i;
-}
-
-void
-LossyConvertEncoding8to16::write_sse2(const char* aSource,
-                                      uint32_t aSourceLength)
-{
-  char16_t* dest = mDestination;
-
-  // Align source to a 16-byte boundary.  We choose to align source rather than
-  // dest because we'd rather have our loads than our stores be fast. You have
-  // to wait for a load to complete, but you can keep on moving after issuing a
-  // store.
-  uint32_t i = 0;
-  uint32_t alignLen = XPCOM_MIN(aSourceLength,
-                                uint32_t(-NS_PTR_TO_INT32(aSource) & 0xf));
-  for (; i < alignLen; ++i) {
-    dest[i] = static_cast<unsigned char>(aSource[i]);
-  }
-
-  // Walk 32 bytes (two XMM registers) at a time.
-  for (; aSourceLength - i > 31; i += 32) {
-    __m128i source1 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i));
-    __m128i source2 = _mm_load_si128(reinterpret_cast<const __m128i*>(aSource + i + 16));
-
-    // Interleave 0s in with the bytes of source to create lo and hi.
-    __m128i lo1 = _mm_unpacklo_epi8(source1, _mm_setzero_si128());
-    __m128i hi1 = _mm_unpackhi_epi8(source1, _mm_setzero_si128());
-    __m128i lo2 = _mm_unpacklo_epi8(source2, _mm_setzero_si128());
-    __m128i hi2 = _mm_unpackhi_epi8(source2, _mm_setzero_si128());
-
-    // store lo and hi into dest.
-    _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i),      lo1);
-    _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i + 8),  hi1);
-    _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i + 16), lo2);
-    _mm_storeu_si128(reinterpret_cast<__m128i*>(dest + i + 24), hi2);
-  }
-
-  // Finish up whatever's left.
-  for (; i < aSourceLength; ++i) {
-    dest[i] = static_cast<unsigned char>(aSource[i]);
-  }
-
-  mDestination += i;
-}
--- a/xpcom/tests/gtest/TestAtoms.cpp
+++ b/xpcom/tests/gtest/TestAtoms.cpp
@@ -89,38 +89,42 @@ TEST(Atoms, Invalid)
     {
       RefPtr<nsAtom> atom16 = NS_Atomize(Invalid16Strings[i].m16);
       EXPECT_TRUE(atom16->Equals(nsDependentString(Invalid16Strings[i].m16)));
     }
 
     EXPECT_EQ(count, NS_GetNumberOfAtoms());
   }
 
+// Don't run this test in debug builds as that intentionally asserts.
+#ifndef DEBUG
   for (unsigned int i = 0; i < ArrayLength(Invalid8Strings); ++i) {
     nsrefcnt count = NS_GetNumberOfAtoms();
 
     {
       RefPtr<nsAtom> atom8 = NS_Atomize(Invalid8Strings[i].m8);
       RefPtr<nsAtom> atom16 = NS_Atomize(Invalid8Strings[i].m16);
       EXPECT_EQ(atom16, atom8);
       EXPECT_TRUE(atom16->Equals(nsDependentString(Invalid8Strings[i].m16)));
     }
 
     EXPECT_EQ(count, NS_GetNumberOfAtoms());
   }
 
-// Don't run this test in debug builds as that intentionally asserts.
-#ifndef DEBUG
-  RefPtr<nsAtom> emptyAtom = NS_Atomize("");
-
   for (unsigned int i = 0; i < ArrayLength(Malformed8Strings); ++i) {
     nsrefcnt count = NS_GetNumberOfAtoms();
 
-    RefPtr<nsAtom> atom8 = NS_Atomize(Malformed8Strings[i]);
-    EXPECT_EQ(atom8, emptyAtom);
+    {
+      RefPtr<nsAtom> atom8 = NS_Atomize(Malformed8Strings[i]);
+      nsString str16;
+      CopyUTF8toUTF16(MakeStringSpan(Malformed8Strings[i]), str16);
+      RefPtr<nsAtom> atom16 = NS_Atomize(str16);
+      EXPECT_EQ(atom8, atom16);
+    }
+
     EXPECT_EQ(count, NS_GetNumberOfAtoms());
   }
 #endif
 }
 
 #define FIRST_ATOM_STR "first static atom. Hello!"
 #define SECOND_ATOM_STR "second static atom. @World!"
 #define THIRD_ATOM_STR "third static atom?!"
--- a/xpcom/tests/gtest/TestCRT.cpp
+++ b/xpcom/tests/gtest/TestCRT.cpp
@@ -37,18 +37,18 @@ static void Check(const char* s1, const 
   int clib = PL_strcmp(s1, s2);
   int clib_n = PL_strncmp(s1, s2, n);
 
   if (!longerThanN) {
     EXPECT_EQ(sign(clib), sign(clib_n));
   }
 
   nsAutoString t1,t2;
-  CopyASCIItoUTF16(s1, t1);
-  CopyASCIItoUTF16(s2, t2);
+  CopyASCIItoUTF16(mozilla::MakeStringSpan(s1), t1);
+  CopyASCIItoUTF16(mozilla::MakeStringSpan(s2), t2);
   const char16_t* us1 = t1.get();
   const char16_t* us2 = t2.get();
 
   int u2, u2_n;
   u2 = nsCRT::strcmp(us1, us2);
 
   EXPECT_EQ(sign(clib), sign(u2));
 
--- a/xpcom/tests/gtest/TestStrings.cpp
+++ b/xpcom/tests/gtest/TestStrings.cpp
@@ -730,22 +730,20 @@ TEST_F(Strings, replace_substr)
 
   s.AssignLiteral("foofoofoo");
   s.ReplaceSubstring("of", "fo");
   EXPECT_STREQ(s.get(), "fofoofooo");
 }
 
 TEST_F(Strings, replace_substr_2)
 {
-  const char *oldName = nullptr;
   const char *newName = "user";
   nsString acctName; acctName.AssignLiteral("forums.foo.com");
   nsAutoString newAcctName, oldVal, newVal;
-  CopyASCIItoUTF16(oldName, oldVal);
-  CopyASCIItoUTF16(newName, newVal);
+  CopyASCIItoUTF16(mozilla::MakeStringSpan(newName), newVal);
   newAcctName.Assign(acctName);
 
   // here, oldVal is empty.  we are testing that this function
   // does not hang.  see bug 235355.
   newAcctName.ReplaceSubstring(oldVal, newVal);
 
   // we expect that newAcctName will be unchanged.
   EXPECT_TRUE(newAcctName.Equals(acctName));
--- a/xpcom/tests/gtest/TestTextFormatter.cpp
+++ b/xpcom/tests/gtest/TestTextFormatter.cpp
@@ -12,17 +12,16 @@ TEST(TextFormatter, Tests)
   nsAutoString fmt(NS_LITERAL_STRING("%3$s %4$S %1$d %2$d %2$d %3$s"));
   char utf8[] = "Hello";
   char16_t ucs2[]={'W', 'o', 'r', 'l', 'd', 0x4e00, 0xAc00, 0xFF45, 0x0103, 0x00};
   int d=3;
 
   char16_t buf[256];
   nsTextFormatter::snprintf(buf, 256, fmt.get(), d, 333, utf8, ucs2);
   nsAutoString out(buf);
-  ASSERT_STREQ("Hello World", NS_LossyConvertUTF16toASCII(out).get());
 
   const char16_t *uout = out.get();
   const char16_t expected[] = {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20,
                                 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x4E00,
                                 0xAC00, 0xFF45, 0x0103, 0x20, 0x33,
                                 0x20, 0x33, 0x33, 0x33, 0x20, 0x33,
                                 0x33, 0x33, 0x20, 0x48, 0x65, 0x6C,
                                 0x6C, 0x6F};
--- a/xpcom/tests/gtest/TestUTF.cpp
+++ b/xpcom/tests/gtest/TestUTF.cpp
@@ -9,16 +9,17 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "nsString.h"
 #include "nsStringBuffer.h"
 #include "nsReadableUtils.h"
 #include "UTFStrings.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/HashFunctions.h"
+#include "nsUTF8Utils.h"
 
 #include "gtest/gtest.h"
 
 using namespace mozilla;
 
 namespace TestUTF {
 
 TEST(UTF, Valid)
@@ -101,30 +102,26 @@ TEST(UTF, Hash16)
     EXPECT_EQ(HashString(ValidStrings[i].m16),
               HashUTF8AsUTF16(str8.get(), str8.Length(), &err));
     EXPECT_FALSE(err);
   }
 
   for (unsigned int i = 0; i < ArrayLength(Invalid8Strings); ++i) {
     nsDependentCString str8(Invalid8Strings[i].m8);
     bool err;
-    EXPECT_EQ(HashString(Invalid8Strings[i].m16),
-              HashUTF8AsUTF16(str8.get(), str8.Length(), &err));
-    EXPECT_FALSE(err);
+    EXPECT_EQ(HashUTF8AsUTF16(str8.get(), str8.Length(), &err), 0u);
+    EXPECT_TRUE(err);
   }
 
-// Don't run this test in debug builds as that intentionally asserts.
-#ifndef DEBUG
   for (unsigned int i = 0; i < ArrayLength(Malformed8Strings); ++i) {
     nsDependentCString str8(Malformed8Strings[i]);
     bool err;
     EXPECT_EQ(HashUTF8AsUTF16(str8.get(), str8.Length(), &err), 0u);
     EXPECT_TRUE(err);
   }
-#endif
 }
 
 /**
  * This tests the handling of a non-ascii character at various locations in a
  * UTF-16 string that is being converted to UTF-8.
  */
 void NonASCII16_helper(const size_t aStrSize)
 {
@@ -173,19 +170,81 @@ void NonASCII16_helper(const size_t aStr
 
     // And finish with the trailing ASCII chars.
     expected.Append(asciiCString.BeginReading() + i + 1, kTestSize - i - 1);
 
     EXPECT_STREQ(dest.BeginReading(), expected.BeginReading());
   }
 }
 
-TEST(UTF, NonASCII16)
+TEST(UTF, UTF8CharEnumerator)
 {
-  // Test with various string sizes to catch any special casing.
-  NonASCII16_helper(1);
-  NonASCII16_helper(8);
-  NonASCII16_helper(16);
-  NonASCII16_helper(32);
-  NonASCII16_helper(512);
+  const char* p = "\x61\xC0\xC2\xC2\x80\xE0\x80\x80\xE0\xA0\x80\xE1\x80\x80\xED\xBF\xBF\xED\x9F\xBF\xEE\x80\x80\xEE\x80\xFF\xF0\x90\x80\x80\xF0\x80\x80\x80\xF1\x80\x80\x80\xF4\x8F\xBF\xF4\x8F\xBF\xBF\xF4\xBF\xBF\xBF";
+  const char* end = p + 49;
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0x0061U);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0x0080U);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0x0800U);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0x1000U);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xD7FFU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xE000U);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0x10000U);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0x40000U);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0x10FFFFU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(p, end);
+  p = "\xC2";
+  end = p + 1;
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(p, end);
+  p = "\xE1\x80";
+  end = p + 2;
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(p, end);
+  p = "\xF1\x80\x80";
+  end = p + 3;
+  EXPECT_EQ(UTF8CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(p, end);
+}
+
+TEST(UTF, UTF16CharEnumerator)
+{
+  const char16_t* p = u"\u0061\U0001F4A9";
+  const char16_t* end = p + 3;
+  EXPECT_EQ(UTF16CharEnumerator::NextChar(&p, end), 0x0061U);
+  EXPECT_EQ(UTF16CharEnumerator::NextChar(&p, end), 0x1F4A9U);
+  EXPECT_EQ(p, end);
+  const char16_t loneHigh = 0xD83D;
+  p = &loneHigh;
+  end = p + 1;
+  EXPECT_EQ(UTF16CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(p, end);
+  const char16_t loneLow = 0xDCA9;
+  p = &loneLow;
+  end = p + 1;
+  EXPECT_EQ(UTF16CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(p, end);
+  const char16_t loneHighStr[] = { 0xD83D, 0x0061 };
+  p = loneHighStr;
+  end = p + 2;
+  EXPECT_EQ(UTF16CharEnumerator::NextChar(&p, end), 0xFFFDU);
+  EXPECT_EQ(UTF16CharEnumerator::NextChar(&p, end), 0x0061U);
+  EXPECT_EQ(p, end);
 }
 
 } // namespace TestUTF
--- a/xpcom/tests/gtest/UTFStrings.h
+++ b/xpcom/tests/gtest/UTFStrings.h
@@ -56,40 +56,39 @@ static const UTFStringsStringPair Invali
     { { 0xDC00, 0xD800, 0xDC00, 0xD800 },
       { char(0xEF), char(0xBF), char(0xBD), char(0xF0), char(0x90), char(0x80), char(0x80), char(0xEF), char(0xBF), char(0xBD) } },
     { { 0xDC00, 0xD800, 0xD800, 0xDC00 },
       { char(0xEF), char(0xBF), char(0xBD), char(0xEF), char(0xBF), char(0xBD), char(0xF0), char(0x90), char(0x80), char(0x80) } },
   };
 
 static const UTFStringsStringPair Invalid8Strings[] =
   {
-    { { 'a', 0xFFFD, 'b' },
+    { { 'a', 0xFFFD, 0xFFFD, 'b' },
       { 'a', char(0xC0), char(0x80), 'b' } },
-    { { 0xFFFD, 0x80 },
+    { { 0xFFFD, 0xFFFD, 0x80 },
       { char(0xC1), char(0xBF), char(0xC2), char(0x80) } },
-    { { 0xFFFD },
+    { { 0xFFFD, 0xFFFD },
       { char(0xC1), char(0xBF) } },
-    { { 0xFFFD, 'x', 0x0800 },
+    { { 0xFFFD, 0xFFFD, 0xFFFD, 'x', 0x0800 },
       { char(0xE0), char(0x80), char(0x80), 'x', char(0xE0), char(0xA0), char(0x80) } },
-    { { 0xFFFD, 'x', 0xFFFD },
+    { { 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 'x', 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD },
       { char(0xF0), char(0x80), char(0x80), char(0x80), 'x', char(0xF0), char(0x80), char(0x8F), char(0x80) } },
-    { { 0xFFFD, 0xFFFD },
+    { { 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD },
       { char(0xF4), char(0x90), char(0x80), char(0x80), char(0xF7), char(0xBF), char(0xBF), char(0xBF) } },
-    { { 0xFFFD, 'x', 0xD800, 0xDC00, 0xFFFD },
+    { { 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 'x', 0xD800, 0xDC00, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD },
       { char(0xF0), char(0x8F), char(0xBF), char(0xBF), 'x', char(0xF0), char(0x90), char(0x80), char(0x80), char(0xF0), char(0x8F), char(0xBF), char(0xBF) } },
-    { { 0xFFFD, 'x', 0xFFFD },
+    { { 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 'x', 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD },
       { char(0xF8), char(0x80), char(0x80), char(0x80), char(0x80), 'x', char(0xF8), char(0x88), char(0x80), char(0x80), char(0x80) } },
-    { { 0xFFFD, 0xFFFD },
+    { { 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD },
       { char(0xFB), char(0xBF), char(0xBF), char(0xBF), char(0xBF), char(0xFC), char(0xA0), char(0x80), char(0x80), char(0x80), char(0x80) } },
-    { { 0xFFFD, 0xFFFD },
+    { { 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD },
       { char(0xFC), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0xFD), char(0xBF), char(0xBF), char(0xBF), char(0xBF), char(0xBF) } },
   };
 
 // Don't use this array in debug builds as that intentionally asserts.
-#ifndef DEBUG
 static const char Malformed8Strings[][16] =
   {
     { char(0x80) },
     { 'a', char(0xC8), 'c' },
     { 'a', char(0xC0) },
     { 'a', char(0xE8), 'c' },
     { 'a', char(0xE8), char(0x80), 'c' },
     { 'a', char(0xE8), char(0x80) },
@@ -102,11 +101,10 @@ static const char Malformed8Strings[][16
     { 'a', char(0xFA), 'c' },
     { 'a', char(0xFA), char(0x80), char(0x80), 0x7F, char(0x80), 'c' },
     { 'a', char(0xFA), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), 'c' },
     { 'a', char(0xFD) },
     { 'a', char(0xFD), char(0x80), char(0x80), char(0x80), char(0x80), 'c' },
     { 'a', char(0xFD), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80), char(0x80) },
     { 'a', char(0xFC), char(0x80), char(0x80), 0x40, char(0x80), char(0x80), 'c' },
   };
-#endif
 
 #endif
--- a/xpfe/components/directory/nsDirectoryViewer.cpp
+++ b/xpfe/components/directory/nsDirectoryViewer.cpp
@@ -158,17 +158,17 @@ nsHTTPIndex::OnFTPControlLog(bool server
     dom::AutoEntryScript aes(globalObject,
                              "nsHTTPIndex OnFTPControlLog");
     JSContext* cx = aes.cx();
 
     JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
     NS_ENSURE_TRUE(global, NS_OK);
 
     nsString unicodeMsg;
-    CopyASCIItoUTF16(msg, unicodeMsg);
+    CopyASCIItoUTF16(MakeStringSpan(msg), unicodeMsg);
     JSString* jsMsgStr = JS_NewUCStringCopyZ(cx, unicodeMsg.get());
     NS_ENSURE_TRUE(jsMsgStr, NS_ERROR_OUT_OF_MEMORY);
 
     JS::AutoValueArray<2> params(cx);
     params[0].setBoolean(server);
     params[1].setString(jsMsgStr);
 
     JS::Rooted<JS::Value> val(cx);