Bug 1261841 part 2 - Use encoding_rs instead of uconv. r=emk,mystor.
authorHenri Sivonen <hsivonen@hsivonen.fi>
Thu, 27 Apr 2017 13:27:03 +0300
changeset 414836 e155fa765af299f0e8cfb42e0a1709e5b04928b9
parent 414835 a799ece5b8ad74e7aa64e0e4f74a866958a73000
child 414837 4c8a154bf1436e74120d58e935bdaa9dc290bdc4
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk, mystor
bugs1261841
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1261841 part 2 - Use encoding_rs instead of uconv. r=emk,mystor. MozReview-Commit-ID: 15Y5GTX98bv
browser/components/migration/360seProfileMigrator.js
browser/components/preferences/fonts.xul
docshell/base/nsDocShell.cpp
dom/base/BodyUtil.cpp
dom/base/EventSource.cpp
dom/base/EventSource.h
dom/base/FormData.cpp
dom/base/WebSocket.cpp
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsDocumentEncoder.cpp
dom/base/nsIContentSerializer.h
dom/base/nsPlainTextSerializer.cpp
dom/base/nsPlainTextSerializer.h
dom/base/nsXHTMLContentSerializer.cpp
dom/base/nsXHTMLContentSerializer.h
dom/base/nsXMLContentSerializer.cpp
dom/base/nsXMLContentSerializer.h
dom/base/test/unit/test_xml_serializer.js
dom/encoding/EncodingUtils.cpp
dom/encoding/EncodingUtils.h
dom/encoding/TextDecoder.cpp
dom/encoding/TextDecoder.h
dom/encoding/TextEncoder.cpp
dom/encoding/TextEncoder.h
dom/encoding/domainsfallbacks.properties
dom/encoding/encodingsgroups.properties
dom/encoding/localesfallbacks.properties
dom/encoding/test/test_TLD.html
dom/encoding/test/unit/test_misc.js
dom/fetch/BodyExtractor.cpp
dom/fetch/Fetch.cpp
dom/fetch/FetchUtil.cpp
dom/html/HTMLFormSubmission.cpp
dom/html/HTMLFormSubmission.h
dom/html/nsHTMLDocument.cpp
dom/json/nsJSON.cpp
dom/json/nsJSON.h
dom/json/test/unit/test_encode.js
dom/plugins/base/nsPluginTags.cpp
dom/script/ScriptLoadHandler.cpp
dom/script/ScriptLoadHandler.h
dom/script/ScriptLoader.cpp
dom/script/ScriptLoader.h
dom/url/URLSearchParams.cpp
dom/url/URLSearchParams.h
dom/url/tests/test_urlSearchParams_utf8.html
dom/webidl/TextEncoder.webidl
dom/workers/ServiceWorkerEvents.cpp
dom/xhr/XMLHttpRequestMainThread.cpp
dom/xhr/XMLHttpRequestMainThread.h
extensions/spellcheck/hunspell/glue/mozHunspell.cpp
extensions/spellcheck/hunspell/glue/mozHunspell.h
extensions/spellcheck/hunspell/src/csutil.cxx
extensions/spellcheck/src/mozEnglishWordUtils.h
extensions/spellcheck/src/mozPersonalDictionary.cpp
extensions/spellcheck/src/mozPersonalDictionary.h
gfx/thebes/gfxFontUtils.cpp
intl/Encoding.h
intl/encoding_glue/Cargo.toml
intl/encoding_glue/src/lib.rs
intl/gtest/TestEncoding.cpp
intl/gtest/moz.build
intl/locale/mac/nsMacCharset.cpp
intl/locale/unix/nsUNIXCharset.cpp
intl/locale/unix/unixcharset.properties
intl/locale/windows/nsWinCharset.cpp
intl/moz.build
intl/uconv/moz.build
intl/uconv/nsConverterInputStream.cpp
intl/uconv/nsConverterInputStream.h
intl/uconv/nsConverterOutputStream.cpp
intl/uconv/nsConverterOutputStream.h
intl/uconv/nsIScriptableUConv.idl
intl/uconv/nsITextToSubURI.idl
intl/uconv/nsIUTF8ConverterService.idl
intl/uconv/nsScriptableUConv.cpp
intl/uconv/nsScriptableUConv.h
intl/uconv/nsTextToSubURI.cpp
intl/uconv/nsUConvModule.cpp
intl/uconv/nsUTF8ConverterService.cpp
intl/uconv/tests/moz.build
intl/uconv/tests/unit/hangulTestStrings.js
intl/uconv/tests/unit/test_bug1008832.js
intl/uconv/tests/unit/test_bug116882.js
intl/uconv/tests/unit/test_bug340714.js
intl/uconv/tests/unit/test_bug563283.js
intl/uconv/tests/unit/test_charset_conversion.js
intl/uconv/tests/unit/test_decode_EUCKR_Hangul.js
intl/uconv/tests/unit/test_decode_x_mac_arabic.js
intl/uconv/tests/unit/test_decode_x_mac_arabic_internal.js
intl/uconv/tests/unit/test_decode_x_mac_ce.js
intl/uconv/tests/unit/test_decode_x_mac_croatian.js
intl/uconv/tests/unit/test_decode_x_mac_devanagari.js
intl/uconv/tests/unit/test_decode_x_mac_farsi.js
intl/uconv/tests/unit/test_decode_x_mac_farsi_internal.js
intl/uconv/tests/unit/test_decode_x_mac_greek.js
intl/uconv/tests/unit/test_decode_x_mac_gujarati.js
intl/uconv/tests/unit/test_decode_x_mac_gurmukhi.js
intl/uconv/tests/unit/test_decode_x_mac_hebrew.js
intl/uconv/tests/unit/test_decode_x_mac_hebrew_internal.js
intl/uconv/tests/unit/test_decode_x_mac_icelandic.js
intl/uconv/tests/unit/test_decode_x_mac_romanian.js
intl/uconv/tests/unit/test_decode_x_mac_turkish.js
intl/uconv/tests/unit/test_encode_x_mac_arabic.js
intl/uconv/tests/unit/test_encode_x_mac_ce.js
intl/uconv/tests/unit/test_encode_x_mac_croatian.js
intl/uconv/tests/unit/test_encode_x_mac_devanagari.js
intl/uconv/tests/unit/test_encode_x_mac_farsi.js
intl/uconv/tests/unit/test_encode_x_mac_greek.js
intl/uconv/tests/unit/test_encode_x_mac_gujarati.js
intl/uconv/tests/unit/test_encode_x_mac_gurmukhi.js
intl/uconv/tests/unit/test_encode_x_mac_hebrew.js
intl/uconv/tests/unit/test_encode_x_mac_icelandic.js
intl/uconv/tests/unit/test_encode_x_mac_romanian.js
intl/uconv/tests/unit/test_encode_x_mac_turkish.js
intl/uconv/tests/unit/test_unmapped.js
intl/uconv/tests/unit/xpcshell.ini
intl/unicharutil/nsSaveAsCharset.cpp
intl/unicharutil/nsSaveAsCharset.h
js/xpconnect/src/xpc.msg
layout/reftests/bugs/116882-1-ref.html
layout/style/Loader.cpp
netwerk/base/nsStandardURL.cpp
netwerk/base/nsStandardURL.h
netwerk/base/nsUnicharStreamLoader.cpp
netwerk/base/nsUnicharStreamLoader.h
netwerk/mime/nsMIMEHeaderParamImpl.cpp
netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
netwerk/streamconv/converters/nsDirIndexParser.cpp
netwerk/streamconv/converters/nsIndexedToHTML.cpp
parser/html/nsHtml5AttributeName.cpp
parser/html/nsHtml5AttributeName.h
parser/html/nsHtml5ElementName.cpp
parser/html/nsHtml5ElementName.h
parser/html/nsHtml5HtmlAttributes.cpp
parser/html/nsHtml5HtmlAttributes.h
parser/html/nsHtml5MetaScanner.cpp
parser/html/nsHtml5MetaScanner.h
parser/html/nsHtml5MetaScannerCppSupplement.h
parser/html/nsHtml5OwningUTF16Buffer.cpp
parser/html/nsHtml5OwningUTF16Buffer.h
parser/html/nsHtml5Parser.cpp
parser/html/nsHtml5Portability.h
parser/html/nsHtml5StackNode.cpp
parser/html/nsHtml5StackNode.h
parser/html/nsHtml5StateSnapshot.cpp
parser/html/nsHtml5StateSnapshot.h
parser/html/nsHtml5StreamParser.cpp
parser/html/nsHtml5StreamParser.h
parser/html/nsHtml5UTF16Buffer.cpp
parser/html/nsHtml5UTF16Buffer.h
parser/htmlparser/nsParser.cpp
parser/htmlparser/nsScanner.cpp
parser/htmlparser/nsScanner.h
testing/web-platform/meta/dom/nodes/Document-characterSet-normalization.html.ini
testing/web-platform/meta/encoding/iso-2022-jp-decoder.html.ini
testing/web-platform/meta/encoding/single-byte-decoder.html.ini
testing/web-platform/meta/encoding/textdecoder-labels.html.ini
testing/web-platform/meta/eventsource/format-bom-2.htm.ini
testing/web-platform/meta/url/urlencoded-parser.html.ini
testing/web-platform/tests/encoding/iso-2022-jp-decoder.html
testing/web-platform/tests/encoding/single-byte-decoder.html
toolkit/components/osfile/NativeOSFileInternals.cpp
toolkit/components/passwordmgr/OSCrypto_win.js
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/gtest/rust/Cargo.toml
toolkit/library/rust/Cargo.lock
toolkit/library/rust/Cargo.toml
toolkit/library/rust/gkrust-features.mozbuild
toolkit/library/rust/shared/Cargo.toml
toolkit/library/rust/shared/lib.rs
toolkit/locales/en-US/chrome/global/charsetMenu.properties
toolkit/modules/CharsetMenu.jsm
widget/gtk/nsClipboard.cpp
xpcom/base/ErrorList.py
xpcom/io/nsIUnicharOutputStream.idl
--- a/browser/components/migration/360seProfileMigrator.js
+++ b/browser/components/migration/360seProfileMigrator.js
@@ -229,17 +229,17 @@ Object.defineProperty(Qihoo360seProfileM
       loginIni.append("login.ini");
       if (!loginIni.exists()) {
         throw new Error("360 Secure Browser's 'login.ini' does not exist.");
       }
       if (!loginIni.isReadable()) {
         throw new Error("360 Secure Browser's 'login.ini' file could not be read.");
       }
 
-      let loginIniInUtf8 = copyToTempUTF8File(loginIni, "gbk");
+      let loginIniInUtf8 = copyToTempUTF8File(loginIni, "GBK");
       let loginIniObj = parseINIStrings(loginIniInUtf8);
       try {
         loginIniInUtf8.remove(false);
       } catch (ex) {}
 
       let nowLoginEmail = loginIniObj.NowLogin && loginIniObj.NowLogin.email;
 
       /*
--- a/browser/components/preferences/fonts.xul
+++ b/browser/components/preferences/fonts.xul
@@ -20,25 +20,25 @@
             onbeforeaccept="return gFontsDialog.onBeforeAccept();"
             style="">
 
   <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
 
   <prefpane id="FontsDialogPane"
             class="largeDialogContainer"
             helpTopic="prefs-fonts-and-colors">
-  
+
     <preferences id="fontPreferences">
       <preference id="font.language.group"  name="font.language.group"  type="wstring"/>
       <preference id="browser.display.use_document_fonts"
                   name="browser.display.use_document_fonts"
                   type="int"/>
       <preference id="intl.charset.fallback.override" name="intl.charset.fallback.override" type="string"/>
     </preferences>
-    
+
     <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
     <script type="application/javascript" src="chrome://mozapps/content/preferences/fontbuilder.js"/>
     <script type="application/javascript" src="chrome://browser/content/preferences/fonts.js"/>
 
     <!-- Fonts for: [ Language ] -->
     <groupbox>
       <caption>
         <hbox align="center">
@@ -74,25 +74,25 @@
             <menuitem value="x-telu"          label="&font.langGroup.telugu;"/>
             <menuitem value="th"              label="&font.langGroup.thai;"/>
             <menuitem value="x-tibt"          label="&font.langGroup.tibetan;"/>
             <menuitem value="x-cans"          label="&font.langGroup.canadian;"/>
             <menuitem value="x-unicode"       label="&font.langGroup.other;"/>
           </menupopup>
         </menulist>
       </caption>
-      
+
       <grid>
         <columns>
           <column/>
           <column flex="1"/>
           <column/>
           <column/>
         </columns>
-        
+
         <rows>
           <row>
             <separator class="thin"/>
           </row>
 
           <row align="center">
             <hbox align="center" pack="end">
               <label accesskey="&proportional.accesskey;" control="defaultFontType">&proportional.label;</label>
@@ -102,17 +102,17 @@
                 <menuitem value="serif" label="&useDefaultFontSerif.label;"/>
                 <menuitem value="sans-serif" label="&useDefaultFontSansSerif.label;"/>
               </menupopup>
             </menulist>
             <hbox align="center" pack="end">
               <label value="&size.label;"
                      accesskey="&sizeProportional.accesskey;"
                      control="sizeVar"/>
-            </hbox>  
+            </hbox>
             <menulist id="sizeVar" delayprefsave="true">
               <menupopup>
                 <menuitem value="9" label="9"/>
                 <menuitem value="10" label="10"/>
                 <menuitem value="11" label="11"/>
                 <menuitem value="12" label="12"/>
                 <menuitem value="13" label="13"/>
                 <menuitem value="14" label="14"/>
@@ -159,17 +159,17 @@
               <label accesskey="&monospace.accesskey;" control="monospace">&monospace.label;</label>
             </hbox>
             <menulist id="monospace" flex="1" style="width: 0px;" crop="right" delayprefsave="true"
                       onsyncfrompreference="return FontBuilder.readFontSelection(this);"/>
             <hbox align="center" pack="end">
               <label value="&size.label;"
                      accesskey="&sizeMonospace.accesskey;"
                      control="sizeMono"/>
-            </hbox>  
+            </hbox>
             <menulist id="sizeMono" delayprefsave="true">
               <menupopup>
                 <menuitem value="9" label="9"/>
                 <menuitem value="10" label="10"/>
                 <menuitem value="11" label="11"/>
                 <menuitem value="12" label="12"/>
                 <menuitem value="13" label="13"/>
                 <menuitem value="14" label="14"/>
@@ -232,17 +232,17 @@
               <menuitem value="72" label="72"/>
             </menupopup>
           </menulist>
         </hbox>
       </hbox>
       <separator/>
       <separator class="groove"/>
       <hbox>
-        <checkbox id="useDocumentFonts" 
+        <checkbox id="useDocumentFonts"
                   label="&allowPagesToUseOwn.label;" accesskey="&allowPagesToUseOwn.accesskey;"
                   preference="browser.display.use_document_fonts"
                   onsyncfrompreference="return gFontsDialog.readUseDocumentFonts();"
                   onsynctopreference="return gFontsDialog.writeUseDocumentFonts();"/>
       </hbox>
     </groupbox>
 
     <!-- Text Encoding -->
@@ -255,16 +255,18 @@
                control="DefaultCharsetList"/>
         <menulist id="DefaultCharsetList" preference="intl.charset.fallback.override">
           <menupopup>
             <menuitem label="&languages.customize.Fallback.auto;"        value=""/>
             <menuitem label="&languages.customize.Fallback.arabic;"      value="windows-1256"/>
             <menuitem label="&languages.customize.Fallback.baltic;"      value="windows-1257"/>
             <menuitem label="&languages.customize.Fallback.ceiso;"       value="ISO-8859-2"/>
             <menuitem label="&languages.customize.Fallback.cewindows;"   value="windows-1250"/>
+            <!-- Using gbk instead of GBK for compat with previously-stored prefs.
+                 The value gets normalized in dom/encoding/FallbackEncoding.cpp. -->
             <menuitem label="&languages.customize.Fallback.simplified;"  value="gbk"/>
             <menuitem label="&languages.customize.Fallback.traditional;" value="Big5"/>
             <menuitem label="&languages.customize.Fallback.cyrillic;"    value="windows-1251"/>
             <menuitem label="&languages.customize.Fallback.greek;"       value="ISO-8859-7"/>
             <menuitem label="&languages.customize.Fallback.hebrew;"      value="windows-1255"/>
             <menuitem label="&languages.customize.Fallback.japanese;"    value="Shift_JIS"/>
             <menuitem label="&languages.customize.Fallback.korean;"      value="EUC-KR"/>
             <menuitem label="&languages.customize.Fallback.thai;"        value="windows-874"/>
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -11578,28 +11578,26 @@ nsDocShell::ScrollToAnchor(bool aCurHasR
 
     // Above will fail if the anchor name is not UTF-8.  Need to
     // convert from document charset to unicode.
     if (NS_FAILED(rv)) {
       // Get a document charset
       NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
       nsIDocument* doc = mContentViewer->GetDocument();
       NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
-      const nsACString& aCharset = doc->GetDocumentCharacterSet();
+      const nsACString& charset = doc->GetDocumentCharacterSet();
 
       nsCOMPtr<nsITextToSubURI> textToSubURI =
         do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Unescape and convert to unicode
-      nsXPIDLString uStr;
-
-      rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
-                                            PromiseFlatCString(aNewHash).get(),
-                                            getter_Copies(uStr));
+      nsAutoString uStr;
+
+      rv = textToSubURI->UnEscapeAndConvert(charset, aNewHash, uStr);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Ignore return value of GoToAnchor, since it will return an error
       // if there is no such anchor in the document, which is actually a
       // success condition for us (we want to update the session history
       // with the new URI no matter whether we actually scrolled
       // somewhere).
       //
--- a/dom/base/BodyUtil.cpp
+++ b/dom/base/BodyUtil.cpp
@@ -2,87 +2,39 @@
  * 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 "BodyUtil.h"
 
 #include "nsError.h"
 #include "nsString.h"
 #include "nsIGlobalObject.h"
-#include "nsIUnicodeDecoder.h"
+#include "mozilla/Encoding.h"
 
 #include "nsCharSeparatedTokenizer.h"
 #include "nsDOMString.h"
 #include "nsNetUtil.h"
 #include "nsReadableUtils.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 
 #include "mozilla/ErrorResult.h"
-#include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/FetchUtil.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FormData.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/URLSearchParams.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
-class StreamDecoder final
-{
-  nsCOMPtr<nsIUnicodeDecoder> mDecoder;
-  nsString mDecoded;
-
-public:
-  StreamDecoder()
-    : mDecoder(EncodingUtils::DecoderForEncoding("UTF-8"))
-  {
-    MOZ_ASSERT(mDecoder);
-  }
-
-  nsresult
-  AppendText(const char* aSrcBuffer, uint32_t aSrcBufferLen)
-  {
-    int32_t destBufferLen;
-    nsresult rv =
-      mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen, &destBufferLen);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    if (!mDecoded.SetCapacity(mDecoded.Length() + destBufferLen, fallible)) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    char16_t* destBuffer = mDecoded.BeginWriting() + mDecoded.Length();
-    int32_t totalChars = mDecoded.Length();
-
-    int32_t srcLen = (int32_t) aSrcBufferLen;
-    int32_t outLen = destBufferLen;
-    rv = mDecoder->Convert(aSrcBuffer, &srcLen, destBuffer, &outLen);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-    totalChars += outLen;
-    mDecoded.SetLength(totalChars);
-
-    return NS_OK;
-  }
-
-  nsString&
-  GetText()
-  {
-    return mDecoded;
-  }
-};
-
 // Reads over a CRLF and positions start after it.
 static bool
 PushOverLine(nsACString::const_iterator& aStart,
 	     const nsACString::const_iterator& aEnd)
 {
   if (*aStart == nsCRT::CR && (aEnd - aStart > 1) && *(++aStart) == nsCRT::LF) {
     ++aStart; // advance to after CRLF
     return true;
@@ -530,23 +482,21 @@ BodyUtil::ConsumeFormData(nsIGlobalObjec
   return nullptr;
 }
 
 // static
 nsresult
 BodyUtil::ConsumeText(uint32_t aInputLength, uint8_t* aInput,
                        nsString& aText)
 {
-  StreamDecoder decoder;
-  nsresult rv = decoder.AppendText(reinterpret_cast<char*>(aInput),
-                                   aInputLength);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
+  nsresult rv =
+    UTF_8_ENCODING->DecodeWithBOMRemoval(MakeSpan(aInput, aInputLength), aText);
+  if (NS_FAILED(rv)) {
     return rv;
   }
-  aText = decoder.GetText();
   return NS_OK;
 }
 
 // static
 void
 BodyUtil::ConsumeJson(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
                        const nsString& aStr, ErrorResult& aRv)
 {
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -32,32 +32,30 @@
 #include "nsIStringBundle.h"
 #include "nsIConsoleService.h"
 #include "nsIObserverService.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsJSUtils.h"
 #include "nsIThreadRetargetableRequest.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIScriptError.h"
-#include "mozilla/dom/EncodingUtils.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
 #include "xpcpublic.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/Attributes.h"
 #include "nsError.h"
+#include "mozilla/Encoding.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace workers;
 
-#define REPLACEMENT_CHAR     (char16_t)0xFFFD
-#define BOM_CHAR             (char16_t)0xFEFF
 #define SPACE_CHAR           (char16_t)0x0020
 #define CR_CHAR              (char16_t)0x000D
 #define LF_CHAR              (char16_t)0x000A
 #define COLON_CHAR           (char16_t)0x003A
 
 // Reconnection time related values in milliseconds. The default one is equal
 // to the default value of the pref dom.server-events.default-reconnection-time
 #define MIN_RECONNECTION_TIME_VALUE       500
@@ -208,23 +206,17 @@ public:
 
   RefPtr<EventSource> mEventSource;
 
   /**
    * A simple state machine used to manage the event-source's line buffer
    *
    * PARSE_STATE_OFF              -> PARSE_STATE_BEGIN_OF_STREAM
    *
-   * PARSE_STATE_BEGIN_OF_STREAM  -> PARSE_STATE_BOM_WAS_READ |
-   *                                 PARSE_STATE_CR_CHAR |
-   *                                 PARSE_STATE_BEGIN_OF_LINE |
-   *                                 PARSE_STATE_COMMENT |
-   *                                 PARSE_STATE_FIELD_NAME
-   *
-   * PARSE_STATE_BOM_WAS_READ     -> PARSE_STATE_CR_CHAR |
+   * PARSE_STATE_BEGIN_OF_STREAM     -> PARSE_STATE_CR_CHAR |
    *                                 PARSE_STATE_BEGIN_OF_LINE |
    *                                 PARSE_STATE_COMMENT |
    *                                 PARSE_STATE_FIELD_NAME
    *
    * PARSE_STATE_CR_CHAR -> PARSE_STATE_CR_CHAR |
    *                        PARSE_STATE_COMMENT |
    *                        PARSE_STATE_FIELD_NAME |
    *                        PARSE_STATE_BEGIN_OF_LINE
@@ -250,17 +242,16 @@ public:
    *
    * Whenever the parser find an empty line or the end-of-file
    * it dispatches the stacked event.
    *
    */
   enum ParserStatus {
     PARSE_STATE_OFF = 0,
     PARSE_STATE_BEGIN_OF_STREAM,
-    PARSE_STATE_BOM_WAS_READ,
     PARSE_STATE_CR_CHAR,
     PARSE_STATE_COMMENT,
     PARSE_STATE_FIELD_NAME,
     PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE,
     PARSE_STATE_FIELD_VALUE,
     PARSE_STATE_BEGIN_OF_LINE
   };
 
@@ -280,18 +271,17 @@ public:
   };
 
   // Message related data members. May be set / initialized when initializing
   // EventSourceImpl on target thread but should only be used on target thread.
   nsString mLastEventID;
   UniquePtr<Message> mCurrentMessage;
   nsDeque mMessagesToDispatch;
   ParserStatus mStatus;
-  nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
-  nsresult mLastConvertionResult;
+  mozilla::UniquePtr<mozilla::Decoder> mUnicodeDecoder;
   nsString mLastFieldName;
   nsString mLastFieldValue;
 
   // EventSourceImpl internal states.
   // The worker private where the EventSource is created. nullptr if created on
   // main thread. (accessed on worker thread only)
   WorkerPrivate* mWorkerPrivate;
   // Holder to worker to keep worker alive. (accessed on worker thread only)
@@ -346,17 +336,16 @@ NS_IMPL_ISUPPORTS(EventSourceImpl,
                   nsISupportsWeakReference,
                   nsIEventTarget,
                   nsIThreadRetargetableStreamListener)
 
 EventSourceImpl::EventSourceImpl(EventSource* aEventSource)
   : mEventSource(aEventSource)
   , mReconnectionTime(0)
   , mStatus(PARSE_STATE_OFF)
-  , mLastConvertionResult(NS_OK)
   , mMutex("EventSourceImpl::mMutex")
   , mFrozen(false)
   , mGoingToDispatchAllMessages(false)
   , mIsMainThread(NS_IsMainThread())
   , mIsShutDown(false)
   , mScriptLine(0)
   , mScriptColumn(0)
   , mInnerWindowID(0)
@@ -593,17 +582,17 @@ EventSourceImpl::Init(nsIPrincipal* aPri
       return;
     }
   }
 
   mReconnectionTime =
     Preferences::GetInt("dom.server-events.default-reconnection-time",
                         DEFAULT_RECONNECTION_TIME_VALUE);
 
-  mUnicodeDecoder = EncodingUtils::DecoderForEncoding("UTF-8");
+  mUnicodeDecoder = UTF_8_ENCODING->NewDecoderWithBOMRemoval();
 
   // the constructor should throw a SYNTAX_ERROR only if it fails resolving the
   // url parameter, so we don't care about the InitChannelAndRequestEventSource
   // result.
   InitChannelAndRequestEventSource();
 }
 
 //-----------------------------------------------------------------------------
@@ -725,37 +714,37 @@ EventSourceImpl::StreamReaderFunc(nsIInp
 
 void
 EventSourceImpl::ParseSegment(const char* aBuffer, uint32_t aLength)
 {
   AssertIsOnTargetThread();
   if (IsClosed()) {
     return;
   }
-  int32_t srcCount, outCount;
-  char16_t out[2];
-  const char* p = aBuffer;
-  const char* end = aBuffer + aLength;
-
-  do {
-    srcCount = aLength - (p - aBuffer);
-    outCount = 2;
-
-    mLastConvertionResult =
-      mUnicodeDecoder->Convert(p, &srcCount, out, &outCount);
-    MOZ_ASSERT(mLastConvertionResult != NS_ERROR_ILLEGAL_INPUT);
-
-    for (int32_t i = 0; i < outCount; ++i) {
-      nsresult rv = ParseCharacter(out[i]);
+  char16_t buffer[1024];
+  auto dst = MakeSpan(buffer);
+  auto src = AsBytes(MakeSpan(aBuffer, aLength));
+  // XXX EOF handling is https://bugzilla.mozilla.org/show_bug.cgi?id=1369018
+  for (;;) {
+    uint32_t result;
+    size_t read;
+    size_t written;
+    bool hadErrors;
+    Tie(result, read, written, hadErrors) =
+      mUnicodeDecoder->DecodeToUTF16(src, dst, false);
+    Unused << hadErrors;
+    for (auto c : dst.To(written)) {
+      nsresult rv = ParseCharacter(c);
       NS_ENSURE_SUCCESS_VOID(rv);
     }
-    p = p + srcCount;
-  } while (p < end &&
-           mLastConvertionResult != NS_PARTIAL_MORE_INPUT &&
-           mLastConvertionResult != NS_OK);
+    if (result == kInputEmpty) {
+      return;
+    }
+    src = src.From(read);
+  }
 }
 
 class DataAvailableRunnable final : public Runnable
 {
   private:
     RefPtr<EventSourceImpl> mEventSourceImpl;
     UniquePtr<char[]> mData;
     uint32_t mLength;
@@ -1158,20 +1147,19 @@ EventSourceImpl::ResetConnection()
   return NS_OK;
 }
 
 void
 EventSourceImpl::ResetDecoder()
 {
   AssertIsOnTargetThread();
   if (mUnicodeDecoder) {
-    mUnicodeDecoder->Reset();
+    UTF_8_ENCODING->NewDecoderWithBOMRemovalInto(*mUnicodeDecoder);
   }
   mStatus = PARSE_STATE_OFF;
-  mLastConvertionResult = NS_OK;
   ClearFields();
 }
 
 class CallRestartConnection final : public WorkerMainThreadRunnable
 {
 public:
   explicit CallRestartConnection(EventSourceImpl* aEventSourceImpl)
     : WorkerMainThreadRunnable(
@@ -1677,32 +1665,16 @@ EventSourceImpl::ParseCharacter(char16_t
 
   switch (mStatus) {
     case PARSE_STATE_OFF:
       NS_ERROR("Invalid state");
       return NS_ERROR_FAILURE;
       break;
 
     case PARSE_STATE_BEGIN_OF_STREAM:
-      if (aChr == BOM_CHAR) {
-        mStatus = PARSE_STATE_BOM_WAS_READ;  // ignore it
-      } else if (aChr == CR_CHAR) {
-        mStatus = PARSE_STATE_CR_CHAR;
-      } else if (aChr == LF_CHAR) {
-        mStatus = PARSE_STATE_BEGIN_OF_LINE;
-      } else if (aChr == COLON_CHAR) {
-        mStatus = PARSE_STATE_COMMENT;
-      } else {
-        mLastFieldName += aChr;
-        mStatus = PARSE_STATE_FIELD_NAME;
-      }
-
-      break;
-
-    case PARSE_STATE_BOM_WAS_READ:
       if (aChr == CR_CHAR) {
         mStatus = PARSE_STATE_CR_CHAR;
       } else if (aChr == LF_CHAR) {
         mStatus = PARSE_STATE_BEGIN_OF_LINE;
       } else if (aChr == COLON_CHAR) {
         mStatus = PARSE_STATE_COMMENT;
       } else {
         mLastFieldName += aChr;
--- a/dom/base/EventSource.h
+++ b/dom/base/EventSource.h
@@ -19,17 +19,16 @@
 #include "nsIObserver.h"
 #include "nsIStreamListener.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsITimer.h"
 #include "nsIHttpChannel.h"
 #include "nsWeakReference.h"
 #include "nsDeque.h"
-#include "nsIUnicodeDecoder.h"
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
--- a/dom/base/FormData.cpp
+++ b/dom/base/FormData.cpp
@@ -5,24 +5,25 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FormData.h"
 #include "nsIVariant.h"
 #include "nsIInputStream.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/Directory.h"
 #include "mozilla/dom/HTMLFormElement.h"
+#include "mozilla/Encoding.h"
 
 #include "MultipartBlobImpl.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 FormData::FormData(nsISupports* aOwner)
-  : HTMLFormSubmission(NS_LITERAL_CSTRING("UTF-8"), nullptr)
+  : HTMLFormSubmission(WrapNotNull(UTF_8_ENCODING), nullptr)
   , mOwner(aOwner)
 {
 }
 
 namespace {
 
 already_AddRefed<File>
 GetOrCreateFileCalledBlob(Blob& aBlob, ErrorResult& aRv)
@@ -396,17 +397,17 @@ FormData::Constructor(const GlobalObject
 
 // -------------------------------------------------------------------------
 // nsIXHRSendable
 
 NS_IMETHODIMP
 FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
                       nsACString& aContentTypeWithCharset, nsACString& aCharset)
 {
-  FSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"), nullptr);
+  FSMultipartFormData fs(WrapNotNull(UTF_8_ENCODING), nullptr);
 
   for (uint32_t i = 0; i < mFormData.Length(); ++i) {
     if (mFormData[i].wasNullBlob) {
       MOZ_ASSERT(mFormData[i].value.IsUSVString());
       fs.AddNameBlobOrNullPair(mFormData[i].name, nullptr);
     } else if (mFormData[i].value.IsUSVString()) {
       fs.AddNameValuePair(mFormData[i].name,
                           mFormData[i].value.GetAsUSVString());
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -28,17 +28,16 @@
 #include "nsIDOMWindow.h"
 #include "nsIDocument.h"
 #include "nsXPCOM.h"
 #include "nsIXPConnect.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURL.h"
-#include "nsIUnicodeEncoder.h"
 #include "nsThreadUtils.h"
 #include "nsIPromptFactory.h"
 #include "nsIWindowWatcher.h"
 #include "nsIPrompt.h"
 #include "nsIStringBundle.h"
 #include "nsIConsoleService.h"
 #include "mozilla/dom/CloseEvent.h"
 #include "mozilla/net/WebSocketEventService.h"
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -49,17 +49,16 @@
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
 #include "mozilla/dom/HTMLContentElement.h"
 #include "mozilla/dom/HTMLShadowElement.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabParent.h"
-#include "mozilla/dom/TextDecoder.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/XULCommandEvent.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
@@ -217,16 +216,17 @@
 #include "mozilla/BloomFilter.h"
 #include "TabChild.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
 #include "nsIWebNavigationInfo.h"
 #include "nsPluginHost.h"
 #include "mozilla/HangAnnotations.h"
 #include "mozilla/ServoRestyleManager.h"
+#include "mozilla/Encoding.h"
 
 #include "nsIBidiKeyboard.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 #endif
 
@@ -4492,62 +4492,45 @@ nsContentUtils::GetSubdocumentWithOuterW
 // Convert the string from the given encoding to Unicode.
 /* static */
 nsresult
 nsContentUtils::ConvertStringFromEncoding(const nsACString& aEncoding,
                                           const char* aInput,
                                           uint32_t aInputLen,
                                           nsAString& aOutput)
 {
-  CheckedInt32 len = aInputLen;
-  if (!len.isValid()) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  nsAutoCString encoding;
+  const Encoding* encoding;
   if (aEncoding.IsEmpty()) {
-    encoding.AssignLiteral("UTF-8");
+    encoding = UTF_8_ENCODING;
   } else {
-    encoding.Assign(aEncoding);
-  }
-
-  ErrorResult rv;
-  nsAutoPtr<TextDecoder> decoder(new TextDecoder());
-  decoder->InitWithEncoding(encoding, false);
-
-  decoder->Decode(aInput, len.value(), false,
-                  aOutput, rv);
-  return rv.StealNSResult();
+    encoding = Encoding::ForName(aEncoding);
+  }
+  nsresult rv = encoding->DecodeWithBOMRemoval(MakeSpan(reinterpret_cast<const uint8_t*>(aInput), aInputLen), aOutput);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  return NS_OK;
 }
 
 /* static */
 bool
 nsContentUtils::CheckForBOM(const unsigned char* aBuffer, uint32_t aLength,
                             nsACString& aCharset)
 {
-  bool found = true;
-  aCharset.Truncate();
-  if (aLength >= 3 &&
-      aBuffer[0] == 0xEF &&
-      aBuffer[1] == 0xBB &&
-      aBuffer[2] == 0xBF) {
-    aCharset = "UTF-8";
-  }
-  else if (aLength >= 2 &&
-           aBuffer[0] == 0xFE && aBuffer[1] == 0xFF) {
-    aCharset = "UTF-16BE";
-  }
-  else if (aLength >= 2 &&
-           aBuffer[0] == 0xFF && aBuffer[1] == 0xFE) {
-    aCharset = "UTF-16LE";
-  } else {
-    found = false;
-  }
-
-  return found;
+  auto span = MakeSpan(reinterpret_cast<const uint8_t*>(aBuffer), aLength);
+  const Encoding* encoding;
+  size_t bomLength;
+  Tie(encoding, bomLength) = Encoding::ForBOM(span);
+  Unused << bomLength;
+  if (!encoding) {
+    aCharset.Truncate();
+    return false;
+  }
+  encoding->Name(aCharset);
+  return true;
 }
 
 /* static */
 void
 nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver)
 {
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -615,16 +615,19 @@ public:
   static nsresult NewURIWithDocumentCharset(nsIURI** aResult,
                                             const nsAString& aSpec,
                                             nsIDocument* aDocument,
                                             nsIURI* aBaseURI);
 
   /**
    * Convert aInput (in encoding aEncoding) to UTF16 in aOutput.
    *
+   * @deprecated Use mozilla::Encoding::DecodeWithBOMRemoval() in new code.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=1369020
+   *
    * @param aEncoding the Gecko-canonical name of the encoding or the empty
    *                  string (meaning UTF-8)
    */
   static nsresult ConvertStringFromEncoding(const nsACString& aEncoding,
                                             const char* aInput,
                                             uint32_t aInputLen,
                                             nsAString& aOutput);
 
@@ -634,16 +637,19 @@ public:
     return ConvertStringFromEncoding(
         aEncoding, aInput.BeginReading(), aInput.Length(), aOutput);
   }
 
   /**
    * Determine whether a buffer begins with a BOM for UTF-8, UTF-16LE,
    * UTF-16BE
    *
+   * @deprecated Use mozilla::Encoding::ForBOM() in new code.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=1369022
+   *
    * @param aBuffer the buffer to check
    * @param aLength the length of the buffer
    * @param aCharset empty if not found
    * @return boolean indicating whether a BOM was detected.
    */
   static bool CheckForBOM(const unsigned char* aBuffer, uint32_t aLength,
                           nsACString& aCharset);
 
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -13,17 +13,17 @@
 
 #include "nscore.h"
 #include "nsIFactory.h"
 #include "nsISupports.h"
 #include "nsIDocument.h"
 #include "nsIHTMLDocument.h"
 #include "nsCOMPtr.h"
 #include "nsIContentSerializer.h"
-#include "nsIUnicodeEncoder.h"
+#include "mozilla/Encoding.h"
 #include "nsIOutputStream.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMText.h"
 #include "nsIDOMCDATASection.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMProcessingInstruction.h"
 #include "nsIDOMDocumentType.h"
 #include "nsIDOMNodeList.h"
@@ -164,22 +164,22 @@ protected:
   };
 
   nsCOMPtr<nsIDocument>          mDocument;
   nsCOMPtr<nsISelection>         mSelection;
   RefPtr<nsRange>              mRange;
   nsCOMPtr<nsINode>              mNode;
   nsCOMPtr<nsIOutputStream>      mStream;
   nsCOMPtr<nsIContentSerializer> mSerializer;
-  nsCOMPtr<nsIUnicodeEncoder>    mUnicodeEncoder;
+  UniquePtr<Encoder> mUnicodeEncoder;
   nsCOMPtr<nsINode>              mCommonParent;
   nsCOMPtr<nsIDocumentEncoderNodeFixup> mNodeFixup;
 
   nsString          mMimeType;
-  nsCString         mCharset;
+  const Encoding* mEncoding;
   uint32_t          mFlags;
   uint32_t          mWrapColumn;
   uint32_t          mStartDepth;
   uint32_t          mEndDepth;
   int32_t           mStartRootIndex;
   int32_t           mEndRootIndex;
   AutoTArray<nsINode*, 8>    mCommonAncestors;
   AutoTArray<nsIContent*, 8> mStartNodes;
@@ -191,32 +191,35 @@ protected:
   // argument of nsIContentSerializer::Init().
   bool              mNeedsPreformatScanning;
   bool              mHaltRangeHint;
   // Used when context has already been serialized for
   // table cell selections (where parent is <tr>)
   bool              mDisableContextSerialize;
   bool              mIsCopying;  // Set to true only while copying
   bool              mNodeIsContainer;
+  bool mIsPlainText;
   nsStringBuffer*   mCachedBuffer;
 };
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocumentEncoder)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDocumentEncoder)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocumentEncoder)
    NS_INTERFACE_MAP_ENTRY(nsIDocumentEncoder)
    NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION(nsDocumentEncoder,
                          mDocument, mSelection, mRange, mNode, mSerializer,
                          mCommonParent)
 
-nsDocumentEncoder::nsDocumentEncoder() : mCachedBuffer(nullptr)
+nsDocumentEncoder::nsDocumentEncoder()
+  : mEncoding(nullptr)
+  , mCachedBuffer(nullptr)
 {
   Initialize();
   mMimeType.AssignLiteral("text/plain");
 }
 
 void nsDocumentEncoder::Initialize(bool aClearCachedSerializer)
 {
   mFlags = 0;
@@ -224,16 +227,17 @@ void nsDocumentEncoder::Initialize(bool 
   mStartDepth = 0;
   mEndDepth = 0;
   mStartRootIndex = 0;
   mEndRootIndex = 0;
   mNeedsPreformatScanning = false;
   mHaltRangeHint = false;
   mDisableContextSerialize = false;
   mNodeIsContainer = false;
+  mIsPlainText = false;
   if (aClearCachedSerializer) {
     mSerializer = nullptr;
   }
 }
 
 nsDocumentEncoder::~nsDocumentEncoder()
 {
   if (mCachedBuffer) {
@@ -326,17 +330,21 @@ nsDocumentEncoder::SetNativeContainerNod
   mNodeIsContainer = true;
   mNode = aContainer;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocumentEncoder::SetCharset(const nsACString& aCharset)
 {
-  mCharset = aCharset;
+  const Encoding* encoding = Encoding::ForLabel(aCharset);
+  if (!encoding) {
+    return NS_ERROR_UCONV_NOCONV;
+  }
+  mEncoding = encoding->OutputEncoding();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocumentEncoder::GetMimeType(nsAString& aMimeType)
 {
   aMimeType = mMimeType;
   return NS_OK;
@@ -555,114 +563,77 @@ nsDocumentEncoder::SerializeToStringIter
   }
 
   return NS_OK;
 }
 
 static nsresult
 ConvertAndWrite(const nsAString& aString,
                 nsIOutputStream* aStream,
-                nsIUnicodeEncoder* aEncoder)
+                Encoder* aEncoder,
+                bool aIsPlainText)
 {
   NS_ENSURE_ARG_POINTER(aStream);
   NS_ENSURE_ARG_POINTER(aEncoder);
-  nsresult rv;
-  int32_t charLength, startCharLength;
-  const nsPromiseFlatString& flat = PromiseFlatString(aString);
-  const char16_t* unicodeBuf = flat.get();
-  int32_t unicodeLength = aString.Length();
-  int32_t startLength = unicodeLength;
 
-  rv = aEncoder->GetMaxLength(unicodeBuf, unicodeLength, &charLength);
-  startCharLength = charLength;
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!charLength) {
-    // Nothing to write.  Besides, a length 0 string has an immutable buffer, so
-    // attempts to null-terminate it will crash.
+  if (!aString.Length()) {
     return NS_OK;
   }
 
-  nsAutoCString charXferString;
-  if (!charXferString.SetLength(charLength, fallible))
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  char* charXferBuf = charXferString.BeginWriting();
-  nsresult convert_rv = NS_OK;
-
-  do {
-    unicodeLength = startLength;
-    charLength = startCharLength;
-
-    convert_rv = aEncoder->Convert(unicodeBuf, &unicodeLength, charXferBuf, &charLength);
-    NS_ENSURE_SUCCESS(convert_rv, convert_rv);
-
-    // Make sure charXferBuf is null-terminated before we call
-    // Write().
-
-    charXferBuf[charLength] = '\0';
-
-    uint32_t written;
-    rv = aStream->Write(charXferBuf, charLength, &written);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // If the converter couldn't convert a chraacer we replace the
-    // character with a characre entity.
-    if (convert_rv == NS_ERROR_UENC_NOMAPPING) {
-      // Finishes the conversion.
-      // The converter has the possibility to write some extra data and flush its final state.
-      char finish_buf[33];
-      charLength = sizeof(finish_buf) - 1;
-      rv = aEncoder->Finish(finish_buf, &charLength);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      // Make sure finish_buf is null-terminated before we call
-      // Write().
-
-      finish_buf[charLength] = '\0';
-
-      rv = aStream->Write(finish_buf, charLength, &written);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      nsAutoCString entString("&#");
-      if (NS_IS_HIGH_SURROGATE(unicodeBuf[unicodeLength - 1]) &&
-          unicodeLength < startLength && NS_IS_LOW_SURROGATE(unicodeBuf[unicodeLength]))  {
-        entString.AppendInt(SURROGATE_TO_UCS4(unicodeBuf[unicodeLength - 1],
-                                              unicodeBuf[unicodeLength]));
-        unicodeLength += 1;
+  uint8_t buffer[4096];
+  auto src = MakeSpan(aString);
+  auto bufferSpan = MakeSpan(buffer);
+  // Reserve space for terminator
+  auto dst = bufferSpan.To(bufferSpan.Length() - 1);
+  for (;;) {
+    uint32_t result;
+    size_t read;
+    size_t written;
+    bool hadErrors;
+    if (aIsPlainText) {
+      Tie(result, read, written) =
+        aEncoder->EncodeFromUTF16WithoutReplacement(src, dst, false);
+      if (result != kInputEmpty && result != kOutputFull) {
+        // There's always room for one byte in the case of
+        // an unmappable character, because otherwise
+        // we'd have gotten `kOutputFull`.
+        dst[written++] = '?';
       }
-      else
-        entString.AppendInt(unicodeBuf[unicodeLength - 1]);
-      entString.Append(';');
-
-      // Since entString is an nsAutoCString we know entString.get()
-      // returns a null-terminated string, so no need for extra
-      // null-termination before calling Write() here.
-
-      rv = aStream->Write(entString.get(), entString.Length(), &written);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      unicodeBuf += unicodeLength;
-      startLength -= unicodeLength;
+    } else {
+      Tie(result, read, written, hadErrors) =
+        aEncoder->EncodeFromUTF16(src, dst, false);
     }
-  } while (convert_rv == NS_ERROR_UENC_NOMAPPING);
-
-  return rv;
+    Unused << hadErrors;
+    src = src.From(read);
+    // Sadly, we still have test cases that implement nsIOutputStream in JS, so
+    // the buffer needs to be zero-terminated for XPConnect to do its thing.
+    // See bug 170416.
+    bufferSpan[written] = 0;
+    uint32_t streamWritten;
+    nsresult rv = aStream->Write(
+      reinterpret_cast<char*>(dst.Elements()), written, &streamWritten);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    if (result == kInputEmpty) {
+      return NS_OK;
+    }
+  }
 }
 
 nsresult
 nsDocumentEncoder::FlushText(nsAString& aString, bool aForce)
 {
   if (!mStream)
     return NS_OK;
 
   nsresult rv = NS_OK;
 
   if (aString.Length() > 1024 || aForce) {
-    rv = ConvertAndWrite(aString, mStream, mUnicodeEncoder);
+    rv = ConvertAndWrite(aString, mStream, mUnicodeEncoder.get(), mIsPlainText);
 
     aString.Truncate();
   }
 
   return rv;
 }
 
 #if 0 // This code is really fast at serializing a range, but unfortunately
@@ -1083,19 +1054,18 @@ nsDocumentEncoder::EncodeToStringWithMax
 
     mSerializer = do_CreateInstance(progId.get());
     NS_ENSURE_TRUE(mSerializer, NS_ERROR_NOT_IMPLEMENTED);
   }
 
   nsresult rv = NS_OK;
 
   bool rewriteEncodingDeclaration = !(mSelection || mRange || mNode) && !(mFlags & OutputDontRewriteEncodingDeclaration);
-  mSerializer->Init(mFlags, mWrapColumn, mCharset.get(),
-                    mIsCopying, rewriteEncodingDeclaration,
-                    &mNeedsPreformatScanning);
+  mSerializer->Init(
+    mFlags, mWrapColumn, mEncoding, mIsCopying, rewriteEncodingDeclaration, &mNeedsPreformatScanning);
 
   if (mSelection) {
     nsCOMPtr<nsIDOMRange> range;
     int32_t i, count = 0;
 
     rv = mSelection->GetRangeCount(&count);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1222,26 +1192,23 @@ nsDocumentEncoder::EncodeToStringWithMax
 NS_IMETHODIMP
 nsDocumentEncoder::EncodeToStream(nsIOutputStream* aStream)
 {
   nsresult rv = NS_OK;
 
   if (!mDocument)
     return NS_ERROR_NOT_INITIALIZED;
 
-  nsAutoCString encoding;
-  if (!EncodingUtils::FindEncodingForLabelNoReplacement(mCharset, encoding)) {
+  if (!mEncoding) {
     return NS_ERROR_UCONV_NOCONV;
   }
-  mUnicodeEncoder = EncodingUtils::EncoderForEncoding(encoding);
 
-  if (mMimeType.LowerCaseEqualsLiteral("text/plain")) {
-    rv = mUnicodeEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nullptr, '?');
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
+  mUnicodeEncoder = mEncoding->NewEncoder();
+
+  mIsPlainText = (mMimeType.LowerCaseEqualsLiteral("text/plain"));
 
   mStream = aStream;
 
   nsAutoString buf;
 
   rv = EncodeToString(buf);
 
   // Force a flush of the last chunk of data.
--- a/dom/base/nsIContentSerializer.h
+++ b/dom/base/nsIContentSerializer.h
@@ -9,32 +9,35 @@
 
 #include "nsISupports.h"
 
 class nsIContent;
 class nsIDocument;
 class nsAString;
 
 namespace mozilla {
+class Encoding;
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_ICONTENTSERIALIZER_IID \
 { 0xb1ee32f2, 0xb8c4, 0x49b9, \
   { 0x93, 0xdf, 0xb6, 0xfa, 0xb5, 0xd5, 0x46, 0x88 } }
 
 class nsIContentSerializer : public nsISupports {
  public: 
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENTSERIALIZER_IID)
 
-  NS_IMETHOD Init(uint32_t flags, uint32_t aWrapColumn,
-                  const char* aCharSet, bool aIsCopying,
+  NS_IMETHOD Init(uint32_t flags,
+                  uint32_t aWrapColumn,
+                  const mozilla::Encoding* aEncoding,
+                  bool aIsCopying,
                   bool aIsWholeDocument,
                   bool* aNeedsPerformatScanning) = 0;
 
   NS_IMETHOD AppendText(nsIContent* aText, int32_t aStartOffset,
                         int32_t aEndOffset, nsAString& aStr) = 0;
 
   NS_IMETHOD AppendCDATASection(nsIContent* aCDATASection,
                                 int32_t aStartOffset, int32_t aEndOffset,
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -20,16 +20,20 @@
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/BinarySearch.h"
 #include "nsComputedDOMStyle.h"
 
+namespace mozilla {
+class Encoding;
+}
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define PREF_STRUCTS "converter.html2txt.structs"
 #define PREF_HEADER_STRATEGY "converter.html2txt.header_strategy"
 #define PREF_ALWAYS_INCLUDE_RUBY "converter.html2txt.always_include_ruby"
 
 static const  int32_t kTabSize=4;
@@ -131,19 +135,21 @@ nsPlainTextSerializer::nsPlainTextSerial
 
 nsPlainTextSerializer::~nsPlainTextSerializer()
 {
   delete[] mTagStack;
   delete[] mOLStack;
   NS_WARNING_ASSERTION(mHeadLevel == 0, "Wrong head level!");
 }
 
-NS_IMETHODIMP 
-nsPlainTextSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn,
-                            const char* aCharSet, bool aIsCopying,
+NS_IMETHODIMP
+nsPlainTextSerializer::Init(uint32_t aFlags,
+                            uint32_t aWrapColumn,
+                            const Encoding* aEncoding,
+                            bool aIsCopying,
                             bool aIsWholeDocument,
                             bool* aNeedsPreformatScanning)
 {
 #ifdef DEBUG
   // Check if the major control flags are set correctly.
   if (aFlags & nsIDocumentEncoder::OutputFormatFlowed) {
     NS_ASSERTION(aFlags & nsIDocumentEncoder::OutputFormatted,
                  "If you want format=flowed, you must combine it with "
--- a/dom/base/nsPlainTextSerializer.h
+++ b/dom/base/nsPlainTextSerializer.h
@@ -37,18 +37,20 @@ class nsPlainTextSerializer final : publ
 {
 public:
   nsPlainTextSerializer();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsPlainTextSerializer)
 
   // nsIContentSerializer
-  NS_IMETHOD Init(uint32_t flags, uint32_t aWrapColumn,
-                  const char* aCharSet, bool aIsCopying,
+  NS_IMETHOD Init(uint32_t flags,
+                  uint32_t aWrapColumn,
+                  const mozilla::Encoding* aEncoding,
+                  bool aIsCopying,
                   bool aIsWholeDocument,
                   bool* aNeedsPreformatScanning) override;
 
   NS_IMETHOD AppendText(nsIContent* aText, int32_t aStartOffset,
                         int32_t aEndOffset, nsAString& aStr) override;
   NS_IMETHOD AppendCDATASection(nsIContent* aCDATASection,
                                 int32_t aStartOffset, int32_t aEndOffset,
                                 nsAString& aStr) override;
--- a/dom/base/nsXHTMLContentSerializer.cpp
+++ b/dom/base/nsXHTMLContentSerializer.cpp
@@ -53,33 +53,34 @@ nsXHTMLContentSerializer::nsXHTMLContent
 }
 
 nsXHTMLContentSerializer::~nsXHTMLContentSerializer()
 {
   NS_ASSERTION(mOLStateStack.IsEmpty(), "Expected OL State stack to be empty");
 }
 
 NS_IMETHODIMP
-nsXHTMLContentSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn,
-                               const char* aCharSet, bool aIsCopying,
+nsXHTMLContentSerializer::Init(uint32_t aFlags,
+                               uint32_t aWrapColumn,
+                               const mozilla::Encoding* aEncoding,
+                               bool aIsCopying,
                                bool aRewriteEncodingDeclaration,
                                bool* aNeedsPreformatScanning)
 {
   // The previous version of the HTML serializer did implicit wrapping
   // when there is no flags, so we keep wrapping in order to keep
   // compatibility with the existing calling code
   // XXXLJ perhaps should we remove this default settings later ?
   if (aFlags & nsIDocumentEncoder::OutputFormatted ) {
       aFlags = aFlags | nsIDocumentEncoder::OutputWrap;
   }
 
   nsresult rv;
-  rv = nsXMLContentSerializer::Init(aFlags, aWrapColumn, aCharSet,
-                                    aIsCopying, aRewriteEncodingDeclaration,
-                                    aNeedsPreformatScanning);
+  rv = nsXMLContentSerializer::Init(
+    aFlags, aWrapColumn, aEncoding, aIsCopying, aRewriteEncodingDeclaration, aNeedsPreformatScanning);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mRewriteEncodingDeclaration = aRewriteEncodingDeclaration;
   mIsCopying = aIsCopying;
   mIsFirstChildOfOL = false;
   mInBody = 0;
   mDisableEntityEncoding = 0;
   mBodyOnly = (mFlags & nsIDocumentEncoder::OutputBodyOnly) ? true
--- a/dom/base/nsXHTMLContentSerializer.h
+++ b/dom/base/nsXHTMLContentSerializer.h
@@ -17,23 +17,29 @@
 #include "nsXMLContentSerializer.h"
 #include "nsIEntityConverter.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 class nsIContent;
 class nsIAtom;
 
+namespace mozilla {
+class Encoding;
+}
+
 class nsXHTMLContentSerializer : public nsXMLContentSerializer {
  public:
   nsXHTMLContentSerializer();
   virtual ~nsXHTMLContentSerializer();
 
-  NS_IMETHOD Init(uint32_t flags, uint32_t aWrapColumn,
-                  const char* aCharSet, bool aIsCopying,
+  NS_IMETHOD Init(uint32_t flags,
+                  uint32_t aWrapColumn,
+                  const mozilla::Encoding* aEncoding,
+                  bool aIsCopying,
                   bool aRewriteEncodingDeclaration,
                   bool* aNeedsPreformatScanning) override;
 
   NS_IMETHOD AppendText(nsIContent* aText,
                         int32_t aStartOffset,
                         int32_t aEndOffset,
                         nsAString& aStr) override;
 
--- a/dom/base/nsXMLContentSerializer.cpp
+++ b/dom/base/nsXMLContentSerializer.cpp
@@ -26,17 +26,19 @@
 #include "mozilla/Sprintf.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "nsContentUtils.h"
 #include "nsAttrName.h"
 #include "nsILineBreaker.h"
 #include "mozilla/dom/Element.h"
 #include "nsParserConstants.h"
+#include "mozilla/Encoding.h"
 
+using namespace mozilla;
 using namespace mozilla::dom;
 
 #define kXMLNS "xmlns"
 
 // to be readable, we assume that an indented line contains
 // at least this number of characters (arbitrary value here).
 // This is a limit for the indentation.
 #define MIN_INDENTED_LINE_LENGTH 15
@@ -69,34 +71,38 @@ nsXMLContentSerializer::nsXMLContentSeri
 
 nsXMLContentSerializer::~nsXMLContentSerializer()
 {
 }
 
 NS_IMPL_ISUPPORTS(nsXMLContentSerializer, nsIContentSerializer)
 
 NS_IMETHODIMP
-nsXMLContentSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn,
-                             const char* aCharSet, bool aIsCopying,
+nsXMLContentSerializer::Init(uint32_t aFlags,
+                             uint32_t aWrapColumn,
+                             const Encoding* aEncoding,
+                             bool aIsCopying,
                              bool aRewriteEncodingDeclaration,
                              bool* aNeedsPreformatScanning)
 {
   *aNeedsPreformatScanning = false;
   mPrefixIndex = 0;
   mColPos = 0;
   mIndentOverflow = 0;
   mIsIndentationAddedOnCurrentLine = false;
   mInAttribute = false;
   mAddNewlineForRootNode = false;
   mAddSpace = false;
   mMayIgnoreLineBreakSequence = false;
   mBodyOnly = false;
   mInBody = 0;
 
-  mCharset = aCharSet;
+  if (aEncoding) {
+    aEncoding->Name(mCharset);
+  }
   mFlags = aFlags;
 
   // Set the line break character:
   if ((mFlags & nsIDocumentEncoder::OutputCRLineBreak)
       && (mFlags & nsIDocumentEncoder::OutputLFLineBreak)) { // Windows
     mLineBreak.AssignLiteral("\r\n");
   }
   else if (mFlags & nsIDocumentEncoder::OutputCRLineBreak) { // Mac
--- a/dom/base/nsXMLContentSerializer.h
+++ b/dom/base/nsXMLContentSerializer.h
@@ -21,24 +21,30 @@
 #include "nsString.h"
 
 #define kIndentStr NS_LITERAL_STRING("  ")
 #define kEndTag NS_LITERAL_STRING("</")
 
 class nsIAtom;
 class nsINode;
 
+namespace mozilla {
+class Encoding;
+}
+
 class nsXMLContentSerializer : public nsIContentSerializer {
  public:
   nsXMLContentSerializer();
 
   NS_DECL_ISUPPORTS
 
-  NS_IMETHOD Init(uint32_t flags, uint32_t aWrapColumn,
-                  const char* aCharSet, bool aIsCopying,
+  NS_IMETHOD Init(uint32_t flags,
+                  uint32_t aWrapColumn,
+                  const mozilla::Encoding* aEncoding,
+                  bool aIsCopying,
                   bool aRewriteEncodingDeclaration,
                   bool* aNeedsPreformatScanning) override;
 
   NS_IMETHOD AppendText(nsIContent* aText, int32_t aStartOffset,
                         int32_t aEndOffset, nsAString& aStr) override;
 
   NS_IMETHOD AppendCDATASection(nsIContent* aCDATASection,
                                 int32_t aStartOffset, int32_t aEndOffset,
--- a/dom/base/test/unit/test_xml_serializer.js
+++ b/dom/base/test/unit/test_xml_serializer.js
@@ -68,36 +68,36 @@ function test2() {
 function test3() {
   // Test basic appending of kids.  Again, we're making assumptions
   // about how our serializer will serialize simple DOMs.
   var doc = ParseXML('<root xmlns="ns1"/>');
   var root = doc.documentElement;
   var child = doc.createElementNS("ns2", "child");
   root.appendChild(child);
   do_check_serialize(doc);
-  do_check_eq(SerializeXML(doc), 
+  do_check_eq(SerializeXML(doc),
               '<root xmlns="ns1"><child xmlns="ns2"/></root>');
-  
+
   doc = ParseXML('<root xmlns="ns1"/>');
   root = doc.documentElement;
   child = doc.createElementNS("ns2", "prefix:child");
   root.appendChild(child);
   do_check_serialize(doc);
-  do_check_eq(SerializeXML(doc), 
+  do_check_eq(SerializeXML(doc),
               '<root xmlns="ns1"><prefix:child xmlns:prefix="ns2"/></root>');
-  
+
   doc = ParseXML('<prefix:root xmlns:prefix="ns1"/>');
   root = doc.documentElement;
   child = doc.createElementNS("ns2", "prefix:child");
   root.appendChild(child);
   do_check_serialize(doc);
-  do_check_eq(SerializeXML(doc), 
+  do_check_eq(SerializeXML(doc),
               '<prefix:root xmlns:prefix="ns1"><a0:child xmlns:a0="ns2"/>'+
               '</prefix:root>');
-  
+
 }
 
 function test4() {
   // setAttributeNS tests
 
   var doc = ParseXML('<root xmlns="ns1"/>');
   var root = doc.documentElement;
   root.setAttributeNS("ns1", "prefix:local", "val");
@@ -211,17 +211,17 @@ function test6() {
   root = doc.documentElement;
   child1 = root.firstChild;
   child2 = doc.createElementNS("ns1", "prefix:child2");
   child1.appendChild(child2);
   do_check_serialize(doc);
   do_check_eq(SerializeXML(doc),
               '<prefix:root xmlns:prefix="ns1"><prefix:child1 xmlns:prefix="ns2">'+
               '<a0:child2 xmlns:a0="ns1"/></prefix:child1></prefix:root>');
-  
+
 
   doc = ParseXML('<root xmlns="ns1"/>');
   root = doc.documentElement;
   child1 = doc.createElementNS("ns2", "child1");
   child2 = doc.createElementNS("ns1", "child2");
   child1.appendChild(child2);
   root.appendChild(child1);
   do_check_serialize(doc);
@@ -274,101 +274,102 @@ function test7() {
   do_check_serialize(doc);
   do_check_eq(SerializeXML(doc),
               '<root xmlns="http://www.w3.org/1999/xhtml"><child1 xmlns="">' +
               '<a0:child2 xmlns:a0="http://www.w3.org/1999/xhtml" xmlns=""></a0:child2></child1></root>');
 }
 
 function test8() {
   // Test behavior of serializing with a given charset.
-  var str1 = '<?xml version="1.0" encoding="ISO-8859-1"?>'+LB+'<root/>';
-  var str2 = '<?xml version="1.0" encoding="UTF8"?>'+LB+'<root/>';
+  var str1 = '<?xml version="1.0" encoding="windows-1252"?>'+LB+'<root/>';
+  var str2 = '<?xml version="1.0" encoding="UTF-8"?>'+LB+'<root/>';
   var doc1 = ParseXML(str1);
   var doc2 = ParseXML(str2);
 
   var p = Pipe();
-  DOMSerializer().serializeToStream(doc1, p.outputStream, "ISO-8859-1");
+  DOMSerializer().serializeToStream(doc1, p.outputStream, "windows-1252");
   p.outputStream.close();
   do_check_eq(ScriptableInput(p).read(-1), str1);
 
   p = Pipe();
-  DOMSerializer().serializeToStream(doc2, p.outputStream, "ISO-8859-1");
+  DOMSerializer().serializeToStream(doc2, p.outputStream, "windows-1252");
   p.outputStream.close();
   do_check_eq(ScriptableInput(p).read(-1), str1);
 
   p = Pipe();
-  DOMSerializer().serializeToStream(doc1, p.outputStream, "UTF8");
+  DOMSerializer().serializeToStream(doc1, p.outputStream, "UTF-8");
   p.outputStream.close();
   do_check_eq(ScriptableInput(p).read(-1), str2);
 
   p = Pipe();
-  DOMSerializer().serializeToStream(doc2, p.outputStream, "UTF8");
+  DOMSerializer().serializeToStream(doc2, p.outputStream, "UTF-8");
   p.outputStream.close();
   do_check_eq(ScriptableInput(p).read(-1), str2);
 }
 
 function test9() {
   // Test behavior of serializing between given charsets, using
-  // ISO-8859-1-representable text.
+  // windows-1252-representable text.
   var contents = '<root>' +
                    '\u00BD + \u00BE == \u00BD\u00B2 + \u00BC + \u00BE' +
                  '</root>';
-  var str1 = '<?xml version="1.0" encoding="ISO-8859-1"?>'+ LB + contents;
-  var str2 = '<?xml version="1.0" encoding="UTF8"?>'+ LB + contents;
+  var str1 = '<?xml version="1.0" encoding="windows-1252"?>'+ LB + contents;
+  var str2 = '<?xml version="1.0" encoding="UTF-8"?>'+ LB + contents;
   var str3 = '<?xml version="1.0" encoding="UTF-16"?>'+ LB + contents;
   var doc1 = ParseXML(str1);
   var doc2 = ParseXML(str2);
   var doc3 = ParseXML(str3);
 
-  checkSerialization(doc1, "ISO-8859-1", str1);
-  checkSerialization(doc2, "ISO-8859-1", str1);
-  checkSerialization(doc3, "ISO-8859-1", str1);
+  checkSerialization(doc1, "windows-1252", str1);
+  checkSerialization(doc2, "windows-1252", str1);
+  checkSerialization(doc3, "windows-1252", str1);
 
-  checkSerialization(doc1, "UTF8", str2);
-  checkSerialization(doc2, "UTF8", str2);
-  checkSerialization(doc3, "UTF8", str2);
+  checkSerialization(doc1, "UTF-8", str2);
+  checkSerialization(doc2, "UTF-8", str2);
+  checkSerialization(doc3, "UTF-8", str2);
 
-  checkSerialization(doc1, "UTF-16", str3);
-  checkSerialization(doc2, "UTF-16", str3);
-  checkSerialization(doc3, "UTF-16", str3);
+  checkSerialization(doc1, "UTF-16", str2);
+  checkSerialization(doc2, "UTF-16", str2);
+  checkSerialization(doc3, "UTF-16", str2);
 }
 
 function test10() {
   // Test behavior of serializing between given charsets, using
   // Unicode characters (XXX but only BMP ones because I don't know
   // how to create one with non-BMP characters, either with JS strings
   // or using DOM APIs).
   var contents = '<root>' +
                    'AZaz09 \u007F ' +               // U+000000 to U+00007F
                    '\u0080 \u0398 \u03BB \u0725 ' + // U+000080 to U+0007FF
                    '\u0964 \u0F5F \u20AC \uFFFB' +  // U+000800 to U+00FFFF
                  '</root>';
-  var str1 = '<?xml version="1.0" encoding="UTF8"?>'+ LB + contents;
+  var str1 = '<?xml version="1.0" encoding="UTF-8"?>'+ LB + contents;
   var str2 = '<?xml version="1.0" encoding="UTF-16"?>'+ LB + contents;
   var doc1 = ParseXML(str1);
   var doc2 = ParseXML(str2);
 
   checkSerialization(doc1, "UTF8", str1);
   checkSerialization(doc2, "UTF8", str1);
 
-  checkSerialization(doc1, "UTF-16", str2);
-  checkSerialization(doc2, "UTF-16", str2);
+  checkSerialization(doc1, "UTF-16", str1);
+  checkSerialization(doc2, "UTF-16", str1);
 }
 
 function checkSerialization(doc, toCharset, expectedString) {
   var p = Pipe();
   DOMSerializer().serializeToStream(doc, p.outputStream, toCharset);
   p.outputStream.close();
 
+  var inCharset = (toCharset == "UTF-16") ? "UTF-8" : toCharset;
   var cin = C["@mozilla.org/intl/converter-input-stream;1"]
              .createInstance(I.nsIConverterInputStream);
-  cin.init(p.inputStream, toCharset, 1024, 0x0);
+  cin.init(p.inputStream, inCharset, 1024, 0x0);
 
   // compare the first expectedString.length characters for equality
   var outString = {};
   var count = cin.readString(expectedString.length, outString);
-  do_check_true(count == expectedString.length);
-  do_check_true(outString.value == expectedString);
+  do_check_eq(count, expectedString.length);
+  do_check_eq(outString.value, expectedString);
 
   // if there's anything more in the stream, it's a bug
   do_check_eq(0, cin.readString(1, outString));
   do_check_eq(outString.value, "");
 }
--- a/dom/encoding/EncodingUtils.cpp
+++ b/dom/encoding/EncodingUtils.cpp
@@ -3,60 +3,51 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/EncodingUtils.h"
 
 #include "mozilla/ArrayUtils.h" // ArrayLength
 #include "nsUConvPropertySearch.h"
-#include "nsIUnicodeDecoder.h"
-#include "nsIUnicodeEncoder.h"
-#include "nsComponentManagerUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 static constexpr nsUConvProp labelsEncodings[] = {
 #include "labelsencodings.properties.h"
 };
 
 static constexpr nsUConvProp encodingsGroups[] = {
 #include "encodingsgroups.properties.h"
 };
 
 bool
 EncodingUtils::FindEncodingForLabel(const nsACString& aLabel,
                                     nsACString& aOutEncoding)
 {
-  // Save aLabel first because it may refer the same string as aOutEncoding.
-  nsCString label(aLabel);
-
-  EncodingUtils::TrimSpaceCharacters(label);
-  if (label.IsEmpty()) {
+  auto encoding = Encoding::ForLabel(aLabel);
+  if (!encoding) {
     aOutEncoding.Truncate();
     return false;
   }
-
-  ToLowerCase(label);
-  return NS_SUCCEEDED(nsUConvPropertySearch::SearchPropertyValue(
-      labelsEncodings, ArrayLength(labelsEncodings), label, aOutEncoding));
+  encoding->Name(aOutEncoding);
+  return true;
 }
 
 bool
 EncodingUtils::FindEncodingForLabelNoReplacement(const nsACString& aLabel,
                                                  nsACString& aOutEncoding)
 {
-  if(!FindEncodingForLabel(aLabel, aOutEncoding)) {
-    return false;
-  }
-  if (aOutEncoding.EqualsLiteral("replacement")) {
+  auto encoding = Encoding::ForLabelNoReplacement(aLabel);
+  if (!encoding) {
     aOutEncoding.Truncate();
     return false;
   }
+  encoding->Name(aOutEncoding);
   return true;
 }
 
 bool
 EncodingUtils::IsAsciiCompatible(const nsACString& aPreferredName)
 {
   // HZ and UTF-7 are no longer in mozilla-central, but keeping them here
   // just in case for the benefit of comm-central.
@@ -64,36 +55,26 @@ EncodingUtils::IsAsciiCompatible(const n
            aPreferredName.LowerCaseEqualsLiteral("utf-16be") ||
            aPreferredName.LowerCaseEqualsLiteral("utf-16le") ||
            aPreferredName.LowerCaseEqualsLiteral("replacement") ||
            aPreferredName.LowerCaseEqualsLiteral("hz-gb-2312") ||
            aPreferredName.LowerCaseEqualsLiteral("utf-7") ||
            aPreferredName.LowerCaseEqualsLiteral("x-imap4-modified-utf7"));
 }
 
-already_AddRefed<nsIUnicodeDecoder>
+UniquePtr<Decoder>
 EncodingUtils::DecoderForEncoding(const nsACString& aEncoding)
 {
-  nsAutoCString contractId(NS_UNICODEDECODER_CONTRACTID_BASE);
-  contractId.Append(aEncoding);
-
-  nsCOMPtr<nsIUnicodeDecoder> decoder = do_CreateInstance(contractId.get());
-  MOZ_ASSERT(decoder, "Tried to create decoder for unknown encoding.");
-  return decoder.forget();
+  return Encoding::ForName(aEncoding)->NewDecoderWithBOMRemoval();
 }
 
-already_AddRefed<nsIUnicodeEncoder>
+UniquePtr<Encoder>
 EncodingUtils::EncoderForEncoding(const nsACString& aEncoding)
 {
-  nsAutoCString contractId(NS_UNICODEENCODER_CONTRACTID_BASE);
-  contractId.Append(aEncoding);
-
-  nsCOMPtr<nsIUnicodeEncoder> encoder = do_CreateInstance(contractId.get());
-  MOZ_ASSERT(encoder, "Tried to create encoder for unknown encoding.");
-  return encoder.forget();
+  return Encoding::ForName(aEncoding)->NewEncoder();
 }
 
 void
 EncodingUtils::LangGroupForEncoding(const nsACString& aEncoding,
                                     nsACString& aOutGroup)
 {
   if (NS_FAILED(nsUConvPropertySearch::SearchPropertyValue(
       encodingsGroups, ArrayLength(encodingsGroups), aEncoding, aOutGroup))) {
--- a/dom/encoding/EncodingUtils.h
+++ b/dom/encoding/EncodingUtils.h
@@ -4,35 +4,35 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_encodingutils_h_
 #define mozilla_dom_encodingutils_h_
 
 #include "nsDataHashtable.h"
 #include "nsString.h"
-
-class nsIUnicodeDecoder;
-class nsIUnicodeEncoder;
+#include "mozilla/Encoding.h"
 
 namespace mozilla {
 namespace dom {
 
 class EncodingUtils
 {
 public:
-
   /**
    * Implements get an encoding algorithm from Encoding spec.
    * http://encoding.spec.whatwg.org/#concept-encoding-get
    * Given a label, this function returns the corresponding encoding or a
    * false.
    * The returned name may not be lowercased due to compatibility with
    * our internal implementations.
    *
+   * @deprecated Use mozilla::Encoding::ForLabel() in new code.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=1369025
+   *
    * @param      aLabel, incoming label describing charset to be decoded.
    * @param      aOutEncoding, returning corresponding encoding for label.
    * @return     false if no encoding was found for label.
    *             true if valid encoding found.
    */
   static bool FindEncodingForLabel(const nsACString& aLabel,
                                    nsACString& aOutEncoding);
 
@@ -41,16 +41,19 @@ public:
   {
     return FindEncodingForLabel(NS_ConvertUTF16toUTF8(aLabel), aOutEncoding);
   }
 
   /**
    * Like FindEncodingForLabel() except labels that map to "replacement"
    * are treated as unknown.
    *
+   * @deprecated Use mozilla::Encoding::ForLabelNoReplacement() in new code.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=1369025
+   *
    * @param      aLabel, incoming label describing charset to be decoded.
    * @param      aOutEncoding, returning corresponding encoding for label.
    * @return     false if no encoding was found for label.
    *             true if valid encoding found.
    */
   static bool FindEncodingForLabelNoReplacement(const nsACString& aLabel,
                                                 nsACString& aOutEncoding);
 
@@ -84,56 +87,70 @@ public:
    * @param aPreferredName a preferred encoding label
    * @return whether the encoding is ASCII-compatible
    */
   static bool IsAsciiCompatible(const nsACString& aPreferredName);
 
   /**
    * Instantiates a decoder for an encoding. The input must be a
    * Gecko-canonical encoding name.
+   *
+   * @deprecated Use mozilla::Encoding::NewDecoderWithBOMRemoval()
+   *             (or more appropriate variant) in new code.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=1369032
+   *
    * @param aEncoding a Gecko-canonical encoding name
    * @return a decoder
    */
-  static already_AddRefed<nsIUnicodeDecoder>
-  DecoderForEncoding(const char* aEncoding)
+  static UniquePtr<Decoder> DecoderForEncoding(const char* aEncoding)
   {
     nsDependentCString encoding(aEncoding);
     return DecoderForEncoding(encoding);
   }
 
   /**
    * Instantiates a decoder for an encoding. The input must be a
+   *
+   * @deprecated Use mozilla::Encoding::NewDecoderWithBOMRemoval()
+   *             (or more appropriate variant) in new code.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=1369032
+   *
    * Gecko-canonical encoding name
    * @param aEncoding a Gecko-canonical encoding name
    * @return a decoder
    */
-  static already_AddRefed<nsIUnicodeDecoder>
-  DecoderForEncoding(const nsACString& aEncoding);
+  static UniquePtr<Decoder> DecoderForEncoding(const nsACString& aEncoding);
 
   /**
    * Instantiates an encoder for an encoding. The input must be a
+   *
+   * @deprecated Use mozilla::Encoding::NewEncoder() in new code.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=1369032
+   *
    * Gecko-canonical encoding name.
    * @param aEncoding a Gecko-canonical encoding name
    * @return an encoder
    */
-  static already_AddRefed<nsIUnicodeEncoder>
-  EncoderForEncoding(const char* aEncoding)
+  static UniquePtr<Encoder> EncoderForEncoding(const char* aEncoding)
   {
     nsDependentCString encoding(aEncoding);
     return EncoderForEncoding(encoding);
   }
 
   /**
    * Instantiates an encoder for an encoding. The input must be a
    * Gecko-canonical encoding name.
+   *
+   * @deprecated Use mozilla::Encoding::NewEncoder() in new code.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=1369032
+   *
    * @param aEncoding a Gecko-canonical encoding name
    * @return an encoder
    */
-  static already_AddRefed<nsIUnicodeEncoder>
-  EncoderForEncoding(const nsACString& aEncoding);
+  static UniquePtr<Encoder> EncoderForEncoding(const nsACString& aEncoding);
 
   /**
    * Finds a Gecko language group string (e.g. x-western) for a Gecko-canonical
    * encoding name.
    *
    * @param      aEncoding, incoming label describing charset to be decoded.
    * @param      aOutGroup, returning corresponding language group.
    */
--- a/dom/encoding/TextDecoder.cpp
+++ b/dom/encoding/TextDecoder.cpp
@@ -39,106 +39,94 @@ TextDecoder::InitWithEncoding(const nsAC
   mEncoding = aEncoding;
   // If the constructor is called with an options argument,
   // and the fatal property of the dictionary is set,
   // set the internal fatal flag of the decoder object.
   mFatal = aFatal;
 
   // Create a decoder object for mEncoding.
   mDecoder = EncodingUtils::DecoderForEncoding(mEncoding);
-
-  if (mFatal) {
-    mDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal);
-  }
 }
 
 void
-TextDecoder::Decode(const char* aInput, const int32_t aLength,
-                    const bool aStream, nsAString& aOutDecodedString,
+TextDecoder::Decode(Span<const uint8_t> aInput,
+                    const bool aStream,
+                    nsAString& aOutDecodedString,
                     ErrorResult& aRv)
 {
   aOutDecodedString.Truncate();
 
-  // Run or resume the decoder algorithm of the decoder object's encoder.
-  int32_t outLen;
-  nsresult rv = mDecoder->GetMaxLength(aInput, aLength, &outLen);
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
+  CheckedInt<size_t> needed = mDecoder->MaxUTF16BufferLength(aInput.Length());
+  if (!needed.isValid() ||
+      needed.value() > MaxValue<nsAString::size_type>::value) {
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
-  // Need a fallible allocator because the caller may be a content
-  // and the content can specify the length of the string.
-  auto buf = MakeUniqueFallible<char16_t[]>(outLen + 1);
-  if (!buf) {
+
+  if (!aOutDecodedString.SetLength(needed.value(), fallible)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
-  int32_t length = aLength;
-  rv = mDecoder->Convert(aInput, &length, buf.get(), &outLen);
-  MOZ_ASSERT(mFatal || rv != NS_ERROR_ILLEGAL_INPUT);
-  buf[outLen] = 0;
+  uint32_t result;
+  size_t read;
+  size_t written;
+  bool hadErrors;
+  if (mFatal) {
+    Tie(result, read, written) = mDecoder->DecodeToUTF16WithoutReplacement(
+      aInput, aOutDecodedString, !aStream);
+    if (result != kInputEmpty) {
+      aRv.ThrowTypeError<MSG_DOM_DECODING_FAILED>();
+      return;
+    }
+  } else {
+    Tie(result, read, written, hadErrors) =
+      mDecoder->DecodeToUTF16(aInput, aOutDecodedString, !aStream);
+  }
+  MOZ_ASSERT(result == kInputEmpty);
+  MOZ_ASSERT(read == aInput.Length());
+  MOZ_ASSERT(written <= aOutDecodedString.Length());
+  Unused << hadErrors;
 
-  if (!aOutDecodedString.Append(buf.get(), outLen, fallible)) {
+  if (!aOutDecodedString.SetLength(written, fallible)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   // If the internal streaming flag of the decoder object is not set,
   // then reset the encoding algorithm state to the default values
   if (!aStream) {
-    mDecoder->Reset();
-    if (rv == NS_OK_UDEC_MOREINPUT) {
-      if (mFatal) {
-        aRv.ThrowTypeError<MSG_DOM_DECODING_FAILED>();
-      } else {
-        // Need to emit a decode error manually
-        // to simulate the EOF handling of the Encoding spec.
-        aOutDecodedString.Append(kReplacementChar);
-      }
-    }
-  }
-
-  if (NS_FAILED(rv)) {
-    aRv.ThrowTypeError<MSG_DOM_DECODING_FAILED>();
+    mDecoder->Encoding()->NewDecoderWithBOMRemovalInto(*mDecoder);
   }
 }
 
 void
 TextDecoder::Decode(const Optional<ArrayBufferViewOrArrayBuffer>& aBuffer,
                     const TextDecodeOptions& aOptions,
                     nsAString& aOutDecodedString,
                     ErrorResult& aRv)
 {
   if (!aBuffer.WasPassed()) {
-    Decode(nullptr, 0, aOptions.mStream, aOutDecodedString, aRv);
+    Decode(nullptr, aOptions.mStream, aOutDecodedString, aRv);
     return;
   }
   const ArrayBufferViewOrArrayBuffer& buf = aBuffer.Value();
   uint8_t* data;
   uint32_t length;
   if (buf.IsArrayBufferView()) {
     buf.GetAsArrayBufferView().ComputeLengthAndData();
     data = buf.GetAsArrayBufferView().Data();
     length = buf.GetAsArrayBufferView().Length();
   } else {
     MOZ_ASSERT(buf.IsArrayBuffer());
     buf.GetAsArrayBuffer().ComputeLengthAndData();
     data = buf.GetAsArrayBuffer().Data();
     length = buf.GetAsArrayBuffer().Length();
   }
-  // The other Decode signature takes a signed int, because that's
-  // what nsIUnicodeDecoder::Convert takes as the length.  Throw if
-  // our length is too big.
-  if (length > INT32_MAX) {
-    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return;
-  }
-  Decode(reinterpret_cast<char*>(data), length, aOptions.mStream,
-         aOutDecodedString, aRv);
+  Decode(MakeSpan(data, length), aOptions.mStream, aOutDecodedString, aRv);
 }
 
 void
 TextDecoder::GetEncoding(nsAString& aEncoding)
 {
   CopyASCIItoUTF16(mEncoding, aEncoding);
   nsContentUtils::ASCIIToLower(aEncoding);
 }
--- a/dom/encoding/TextDecoder.h
+++ b/dom/encoding/TextDecoder.h
@@ -6,17 +6,17 @@
 
 #ifndef mozilla_dom_textdecoder_h_
 #define mozilla_dom_textdecoder_h_
 
 #include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "mozilla/dom/TextDecoderBinding.h"
 #include "mozilla/dom/TypedArray.h"
 #include "nsAutoPtr.h"
-#include "nsIUnicodeDecoder.h"
+#include "mozilla/Encoding.h"
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
 class ArrayBufferViewOrArrayBuffer;
@@ -94,31 +94,32 @@ public:
    * character(s) for unidentified character(s).
    *
    * @param      aView, incoming byte stream of characters to be decoded to
    *                    to UTF-16 code points.
    * @param      aOptions, indicates if streaming or not.
    * @param      aOutDecodedString, decoded string of UTF-16 code points.
    * @param      aRv, error result.
    */
-  void Decode(const char* aInput, const int32_t aLength,
-              const bool aStream, nsAString& aOutDecodedString,
+  void Decode(mozilla::Span<const uint8_t> aInput,
+              const bool aStream,
+              nsAString& aOutDecodedString,
               ErrorResult& aRv);
 
   void Decode(const Optional<ArrayBufferViewOrArrayBuffer>& aBuffer,
               const TextDecodeOptions& aOptions,
               nsAString& aOutDecodedString,
               ErrorResult& aRv);
 
   bool Fatal() const {
     return mFatal;
   }
 
 private:
   nsCString mEncoding;
-  nsCOMPtr<nsIUnicodeDecoder> mDecoder;
+  mozilla::UniquePtr<mozilla::Decoder> mDecoder;
   bool mFatal;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_textdecoder_h_
--- a/dom/encoding/TextEncoder.cpp
+++ b/dom/encoding/TextEncoder.cpp
@@ -1,79 +1,49 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/TextEncoder.h"
-#include "mozilla/dom/EncodingUtils.h"
-#include "mozilla/UniquePtrExtensions.h"
-#include "nsContentUtils.h"
+#include "mozilla/Encoding.h"
 
 namespace mozilla {
 namespace dom {
 
 void
 TextEncoder::Init()
 {
-  // Create an encoder object for utf-8.
-  mEncoder = EncodingUtils::EncoderForEncoding(NS_LITERAL_CSTRING("UTF-8"));
 }
 
 void
 TextEncoder::Encode(JSContext* aCx,
                     JS::Handle<JSObject*> aObj,
                     const nsAString& aString,
                     JS::MutableHandle<JSObject*> aRetval,
                     ErrorResult& aRv)
 {
-  // Run the steps of the encoding algorithm.
-  int32_t srcLen = aString.Length();
-  int32_t maxLen;
-  const char16_t* data = aString.BeginReading();
-  nsresult rv = mEncoder->GetMaxLength(data, srcLen, &maxLen);
+  nsAutoCString utf8;
+  nsresult rv;
+  const Encoding* ignored;
+  Tie(rv, ignored) = UTF_8_ENCODING->Encode(aString, utf8);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return;
   }
-  // Need a fallible allocator because the caller may be a content
-  // and the content can specify the length of the string.
-  auto buf = MakeUniqueFallible<char[]>(maxLen + 1);
-  if (!buf) {
+
+  JSAutoCompartment ac(aCx, aObj);
+  JSObject* outView = Uint8Array::Create(
+    aCx, utf8.Length(), reinterpret_cast<const uint8_t*>(utf8.BeginReading()));
+  if (!outView) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
-  int32_t dstLen = maxLen;
-  rv = mEncoder->Convert(data, &srcLen, buf.get(), &dstLen);
-
-  // Now reset the encoding algorithm state to the default values for encoding.
-  int32_t finishLen = maxLen - dstLen;
-  rv = mEncoder->Finish(&buf[dstLen], &finishLen);
-  if (NS_SUCCEEDED(rv)) {
-    dstLen += finishLen;
-  }
-
-  JSObject* outView = nullptr;
-  if (NS_SUCCEEDED(rv)) {
-    buf[dstLen] = '\0';
-    JSAutoCompartment ac(aCx, aObj);
-    outView = Uint8Array::Create(aCx, dstLen,
-                                 reinterpret_cast<uint8_t*>(buf.get()));
-    if (!outView) {
-      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return;
-    }
-  }
-
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
-    return;
-  }
   aRetval.set(outView);
 }
 
 void
 TextEncoder::GetEncoding(nsAString& aEncoding)
 {
   aEncoding.AssignLiteral("utf-8");
 }
--- a/dom/encoding/TextEncoder.h
+++ b/dom/encoding/TextEncoder.h
@@ -5,17 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_textencoder_h_
 #define mozilla_dom_textencoder_h_
 
 #include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "mozilla/dom/TextEncoderBinding.h"
 #include "mozilla/dom/TypedArray.h"
-#include "nsIUnicodeEncoder.h"
+#include "mozilla/Encoding.h"
 
 namespace mozilla {
 class ErrorResult;
 
 namespace dom {
 
 class TextEncoder final : public NonRefcountedDOMObject
 {
@@ -65,16 +65,14 @@ public:
    * @return JSObject* The Uint8Array wrapped in a JS object.  Returned via
    *                   the aRetval out param.
    */
   void Encode(JSContext* aCx,
               JS::Handle<JSObject*> aObj,
               const nsAString& aString,
               JS::MutableHandle<JSObject*> aRetval,
               ErrorResult& aRv);
-private:
-  nsCOMPtr<nsIUnicodeEncoder> mEncoder;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_textencoder_h_
--- a/dom/encoding/domainsfallbacks.properties
+++ b/dom/encoding/domainsfallbacks.properties
@@ -3,20 +3,20 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # This file contains educated guesses about which top-level domains are
 # likely to host legacy content that assumes a non-windows-1252 encoding.
 # Punycode TLDs are included on the theory that legacy content might appear
 # behind those relatively new TLDs if DNS just points to a legacy server.
 #
 # Encodings for which a confident-enough educated guess is missing are
-# listed in nonparticipatingdomains.properties. Domains that are listed 
+# listed in nonparticipatingdomains.properties. Domains that are listed
 # neither there nor here get windows-1252 as the associated fallback.
 #
-# The list below includes Arabic-script TLDs not on IANA list but on the 
+# The list below includes Arabic-script TLDs not on IANA list but on the
 # ICANN list:
 # http://www.icann.org/en/resources/idn/fast-track/string-evaluation-completion
 # Otherwise, the list includes non-windows-1252-affilited country TLDs from
 # https://data.iana.org/TLD/tlds-alpha-by-domain.txt
 #
 # The guesses are assigned as follows:
 # * If the country has a dominant country-affiliated language and that language
 #   is part of the languages to fallbacks mapping, use the encoding for that
@@ -31,21 +31,21 @@ xn--mgbaam7a8h=windows-1256
 af=windows-1256
 
 bg=windows-1251
 
 bh=windows-1256
 
 by=windows-1251
 
-cn=gbk
-xn--fiqs8s=gbk
-# Assume that Traditional Chinese TLD is meant to work if URL input happens to 
+cn=GBK
+xn--fiqs8s=GBK
+# Assume that Traditional Chinese TLD is meant to work if URL input happens to
 # be in the traditional mode. Expect content to be simplified anyway.
-xn--fiqz9s=gbk
+xn--fiqz9s=GBK
 
 cz=windows-1250
 
 dz=windows-1256
 xn--lgbbat1ad8j=windows-1256
 
 ee=windows-1257
 
@@ -123,18 +123,18 @@ ru=windows-1251
 xn--p1ai=windows-1251
 
 sa=windows-1256
 xn--mgberp4a5d4ar=windows-1256
 
 sd=windows-1256
 xn--mgbpl2fh=windows-1256
 
-sg=gbk
-xn--yfro4i67o=gbk
+sg=GBK
+xn--yfro4i67o=GBK
 
 si=ISO-8859-2
 
 sk=windows-1250
 
 su=windows-1251
 
 sy=windows-1256
--- a/dom/encoding/encodingsgroups.properties
+++ b/dom/encoding/encodingsgroups.properties
@@ -3,17 +3,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # x-unicode is assumed for encodings not listed here
 
 Big5=zh-TW
 EUC-JP=ja
 EUC-KR=ko
 gb18030=zh-CN
-gbk=zh-CN
+GBK=zh-CN
 IBM866=x-cyrillic
 ISO-2022-JP=ja
 ISO-8859-3=x-western
 ISO-8859-4=x-western
 ISO-8859-5=x-cyrillic
 ISO-8859-6=ar
 ISO-8859-7=el
 ISO-8859-8=he
--- a/dom/encoding/localesfallbacks.properties
+++ b/dom/encoding/localesfallbacks.properties
@@ -12,19 +12,19 @@
 # Rules:
 #
 # * Avoid editing this file!
 #
 # * If you do edit this file, be sure to file a spec bug against WHATWG HTML
 #   to keep this file in sync with
 #   http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#determining-the-character-encoding
 #
-# * As an exception to the previous rule, gbk is used instead of GB18030 
+# * As an exception to the previous rule, GBK is used instead of GB18030
 #   until/unless work on http://encoding.spec.whatwg.org/ shows that the former
-#   can be treated as an alias of the latter and our decoder implementation 
+#   can be treated as an alias of the latter and our decoder implementation
 #   has been audited to match the spec.
 #
 # * Use only the language code without a hyphen or anything that would come
 #   after the hyphen.
 #
 # * Don't put windows-1252-affiliated languages here.
 #
 # * Don't put Traditional Chinese here.
@@ -64,9 +64,9 @@ sr=windows-1251
 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=23089
 tg=windows-1251
 th=windows-874
 tr=windows-1254
 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=23089
 tt=windows-1251
 uk=windows-1251
 vi=windows-1258
-zh=gbk
+zh=GBK
--- a/dom/encoding/test/test_TLD.html
+++ b/dom/encoding/test/test_TLD.html
@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript">
 
   /** Test for Bug 910211 **/
 
   SimpleTest.waitForExplicitFinish();
 
   var tlds = [
     {'tld': 'tw', 'encoding': 'Big5'},
-    {'tld': 'cn', 'encoding': 'gbk'},
+    {'tld': 'cn', 'encoding': 'GBK'},
     {'tld': 'co.jp', 'encoding': 'Shift_JIS'},
     {'tld': 'fi', 'encoding': 'windows-1252'},
   ];
 
   var iframe = null;
 
   var current = null;
 
--- a/dom/encoding/test/unit/test_misc.js
+++ b/dom/encoding/test/unit/test_misc.js
@@ -175,17 +175,17 @@ test(
   function () {
     var encodings = ["utf-8", "ibm866", "iso-8859-2", "iso-8859-3", "iso-8859-4", "iso-8859-5", "iso-8859-6", "iso-8859-7", "iso-8859-8", "iso-8859-8-i", "iso-8859-10", "iso-8859-13", "iso-8859-14", "iso-8859-15", "iso-8859-16", "koi8-r", "koi8-u", "macintosh", "windows-874", "windows-1250", "windows-1251", "windows-1252", "windows-1253", "windows-1254", "windows-1255", "windows-1256", "windows-1257", "windows-1258", "x-mac-cyrillic", "gbk", "gb18030", "big5", "euc-jp", "iso-2022-jp", "shift_jis", "euc-kr", "x-user-defined"];
 
     encodings.forEach(function (encoding) {
       var string = '', bytes = [];
       for (var i = 0; i < 128; ++i) {
 
         // Encodings that have escape codes in 0x00-0x7F
-        if (encoding === "iso-2022-jp" && i === 0x1B)
+        if (encoding === "iso-2022-jp" && (i === 0x1B || i === 0xE || i === 0xF))
           continue;
 
         string += String.fromCharCode(i);
         bytes.push(i);
       }
       var ascii_encoded = new TextEncoder('utf-8').encode(string);
       assert_equals(new TextDecoder(encoding).decode(ascii_encoded), string, encoding);
       //assert_array_equals(new TextEncoder(encoding).encode(string), bytes, encoding);
--- a/dom/fetch/BodyExtractor.cpp
+++ b/dom/fetch/BodyExtractor.cpp
@@ -13,17 +13,16 @@
 #include "mozilla/dom/XMLHttpRequest.h"
 #include "nsContentUtils.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMSerializer.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsIStorageStream.h"
 #include "nsStringStream.h"
-#include "nsIUnicodeEncoder.h"
 
 namespace mozilla {
 namespace dom {
 
 static nsresult
 GetBufferDataAsStream(const uint8_t* aData, uint32_t aDataLength,
                       nsIInputStream** aResult, uint64_t* aContentLength,
                       nsACString& aContentType, nsACString& aCharset)
@@ -130,51 +129,25 @@ BodyExtractor<nsIDocument>::GetAsStream(
 }
 
 template<> nsresult
 BodyExtractor<const nsAString>::GetAsStream(nsIInputStream** aResult,
                                             uint64_t* aContentLength,
                                             nsACString& aContentTypeWithCharset,
                                             nsACString& aCharset) const
 {
-  nsCOMPtr<nsIUnicodeEncoder> encoder =
-    EncodingUtils::EncoderForEncoding("UTF-8");
-  if (!encoder) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
+  nsCString encoded;
+  CopyUTF16toUTF8(*mBody, encoded);
 
-  int32_t destBufferLen;
-  nsresult rv = encoder->GetMaxLength(mBody->BeginReading(), mBody->Length(),
-                                      &destBufferLen);
+  nsresult rv = NS_NewCStringInputStream(aResult, encoded);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  nsCString encoded;
-  if (!encoded.SetCapacity(destBufferLen, fallible)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  char* destBuffer = encoded.BeginWriting();
-  int32_t srcLen = (int32_t) mBody->Length();
-  int32_t outLen = destBufferLen;
-  rv = encoder->Convert(mBody->BeginReading(), &srcLen, destBuffer, &outLen);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  MOZ_ASSERT(outLen <= destBufferLen);
-  encoded.SetLength(outLen);
-
-  rv = NS_NewCStringInputStream(aResult, encoded);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  *aContentLength = outLen;
+  *aContentLength = encoded.Length();
   aContentTypeWithCharset.AssignLiteral("text/plain;charset=UTF-8");
   aCharset.AssignLiteral("UTF-8");
   return NS_OK;
 }
 
 template<> nsresult
 BodyExtractor<nsIInputStream>::GetAsStream(nsIInputStream** aResult,
                                            uint64_t* aContentLength,
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Fetch.h"
 
 #include "nsIDocument.h"
 #include "nsIGlobalObject.h"
 #include "nsIStreamLoader.h"
 #include "nsIThreadRetargetableRequest.h"
-#include "nsIUnicodeDecoder.h"
 
 #include "nsCharSeparatedTokenizer.h"
 #include "nsDOMString.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "nsReadableUtils.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
--- a/dom/fetch/FetchUtil.cpp
+++ b/dom/fetch/FetchUtil.cpp
@@ -1,16 +1,14 @@
 #include "FetchUtil.h"
 
 #include "nsError.h"
-#include "nsIUnicodeDecoder.h"
 #include "nsString.h"
 #include "nsIDocument.h"
 
-#include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/InternalRequest.h"
 
 namespace mozilla {
 namespace dom {
 
 // static
 nsresult
 FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod)
--- a/dom/html/HTMLFormSubmission.cpp
+++ b/dom/html/HTMLFormSubmission.cpp
@@ -85,28 +85,28 @@ RetrieveDirectoryName(Directory* aDirect
 }
 
 // --------------------------------------------------------------------------
 
 class FSURLEncoded : public EncodingFormSubmission
 {
 public:
   /**
-   * @param aCharset the charset of the form as a string
+   * @param aEncoding the character encoding of the form
    * @param aMethod the method of the submit (either NS_FORM_METHOD_GET or
    *        NS_FORM_METHOD_POST).
    */
-  FSURLEncoded(const nsACString& aCharset,
+  FSURLEncoded(NotNull<const Encoding*> aEncoding,
                int32_t aMethod,
                nsIDocument* aDocument,
                nsIContent* aOriginatingElement)
-    : EncodingFormSubmission(aCharset, aOriginatingElement),
-      mMethod(aMethod),
-      mDocument(aDocument),
-      mWarnedFileControl(false)
+    : EncodingFormSubmission(aEncoding, aOriginatingElement)
+    , mMethod(aMethod)
+    , mDocument(aDocument)
+    , mWarnedFileControl(false)
   {
   }
 
   virtual nsresult
   AddNameValuePair(const nsAString& aName, const nsAString& aValue) override;
 
   virtual nsresult
   AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
@@ -331,26 +331,18 @@ FSURLEncoded::GetEncodedSubmission(nsIUR
       // that is passed to it.  Right now this operation does a copy.
       rv = NS_NewCStringInputStream(getter_AddRefs(dataStream), mQueryString);
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIMIMEInputStream> mimeStream(
         do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
       NS_ENSURE_SUCCESS(rv, rv);
 
-#ifdef SPECIFY_CHARSET_IN_CONTENT_TYPE
-      mimeStream->AddHeader("Content-Type",
-                            PromiseFlatString(
-                              "application/x-www-form-urlencoded; charset="
-                              + mCharset
-                            ).get());
-#else
       mimeStream->AddHeader("Content-Type",
                             "application/x-www-form-urlencoded");
-#endif
       mimeStream->SetAddContentLength(true);
       mimeStream->SetData(dataStream);
 
       *aPostDataStream = mimeStream;
       NS_ADDREF(*aPostDataStream);
     }
 
   } else {
@@ -423,19 +415,19 @@ FSURLEncoded::URLEncode(const nsAString&
 
   return NS_OK;
 }
 
 } // anonymous namespace
 
 // --------------------------------------------------------------------------
 
-FSMultipartFormData::FSMultipartFormData(const nsACString& aCharset,
+FSMultipartFormData::FSMultipartFormData(NotNull<const Encoding*> aEncoding,
                                          nsIContent* aOriginatingElement)
-    : EncodingFormSubmission(aCharset, aOriginatingElement)
+  : EncodingFormSubmission(aEncoding, aOriginatingElement)
 {
   mPostDataStream =
     do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
   mTotalLength = 0;
 
   mBoundary.AssignLiteral("---------------------------");
   mBoundary.AppendInt(rand());
   mBoundary.AppendInt(rand());
@@ -690,18 +682,19 @@ FSMultipartFormData::AddPostDataStream()
 
 // --------------------------------------------------------------------------
 
 namespace {
 
 class FSTextPlain : public EncodingFormSubmission
 {
 public:
-  FSTextPlain(const nsACString& aCharset, nsIContent* aOriginatingElement)
-    : EncodingFormSubmission(aCharset, aOriginatingElement)
+  FSTextPlain(NotNull<const Encoding*> aEncoding,
+              nsIContent* aOriginatingElement)
+    : EncodingFormSubmission(aEncoding, aOriginatingElement)
   {
   }
 
   virtual nsresult
   AddNameValuePair(const nsAString& aName, const nsAString& aValue) override;
 
   virtual nsresult
   AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
@@ -808,43 +801,48 @@ FSTextPlain::GetEncodedSubmission(nsIURI
 
   return rv;
 }
 
 } // anonymous namespace
 
 // --------------------------------------------------------------------------
 
-EncodingFormSubmission::EncodingFormSubmission(const nsACString& aCharset,
-                                               nsIContent* aOriginatingElement)
-  : HTMLFormSubmission(aCharset, aOriginatingElement)
-  , mEncoder(aCharset)
+EncodingFormSubmission::EncodingFormSubmission(
+  NotNull<const Encoding*> aEncoding,
+  nsIContent* aOriginatingElement)
+  : HTMLFormSubmission(aEncoding, aOriginatingElement)
 {
-  if (!(aCharset.EqualsLiteral("UTF-8") || aCharset.EqualsLiteral("gb18030"))) {
-    NS_ConvertUTF8toUTF16 charsetUtf16(aCharset);
-    const char16_t* charsetPtr = charsetUtf16.get();
+  if (!aEncoding->CanEncodeEverything()) {
+    nsAutoCString name;
+    aEncoding->Name(name);
+    NS_ConvertUTF8toUTF16 nameUtf16(name);
+    const char16_t* namePtr = nameUtf16.get();
     SendJSWarning(aOriginatingElement ? aOriginatingElement->GetOwnerDocument()
                                       : nullptr,
                   "CannotEncodeAllUnicode",
-                  &charsetPtr,
+                  &namePtr,
                   1);
   }
 }
 
 EncodingFormSubmission::~EncodingFormSubmission()
 {
 }
 
 // i18n helper routines
 nsresult
 EncodingFormSubmission::EncodeVal(const nsAString& aStr, nsCString& aOut,
                                   bool aHeaderEncode)
 {
-  if (!mEncoder.Encode(aStr, aOut)) {
-    return NS_ERROR_OUT_OF_MEMORY;
+  nsresult rv;
+  const Encoding* ignored;
+  Tie(rv, ignored) = mEncoding->Encode(aStr, aOut);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
   if (aHeaderEncode) {
     aOut.Adopt(nsLinebreakConverter::
                ConvertLineBreaks(aOut.get(),
                                  nsLinebreakConverter::eLinebreakAny,
                                  nsLinebreakConverter::eLinebreakSpace));
     aOut.ReplaceSubstring(NS_LITERAL_CSTRING("\""),
@@ -854,50 +852,50 @@ EncodingFormSubmission::EncodeVal(const 
 
   return NS_OK;
 }
 
 // --------------------------------------------------------------------------
 
 namespace {
 
-void
-GetSubmitCharset(nsGenericHTMLElement* aForm,
-                 nsACString& oCharset)
+NotNull<const Encoding*>
+GetSubmitEncoding(nsGenericHTMLElement* aForm)
 {
-  oCharset.AssignLiteral("UTF-8"); // default to utf-8
-
   nsAutoString acceptCharsetValue;
   aForm->GetAttr(kNameSpaceID_None, nsGkAtoms::acceptcharset,
                  acceptCharsetValue);
 
   int32_t charsetLen = acceptCharsetValue.Length();
   if (charsetLen > 0) {
     int32_t offset=0;
     int32_t spPos=0;
     // get charset from charsets one by one
     do {
       spPos = acceptCharsetValue.FindChar(char16_t(' '), offset);
       int32_t cnt = ((-1==spPos)?(charsetLen-offset):(spPos-offset));
       if (cnt > 0) {
         nsAutoString uCharset;
         acceptCharsetValue.Mid(uCharset, offset, cnt);
 
-        if (EncodingUtils::FindEncodingForLabelNoReplacement(uCharset, oCharset))
-          return;
+        auto encoding = Encoding::ForLabelNoReplacement(uCharset);
+        if (encoding) {
+          return WrapNotNull(encoding);
+        }
       }
       offset = spPos + 1;
     } while (spPos != -1);
   }
   // if there are no accept-charset or all the charset are not supported
   // Get the charset from document
   nsIDocument* doc = aForm->GetComposedDoc();
   if (doc) {
-    oCharset = doc->GetDocumentCharacterSet();
+    return Encoding::ForName(doc->GetDocumentCharacterSet());
   }
+  return WrapNotNull(UTF_8_ENCODING);
 }
 
 void
 GetEnumAttr(nsGenericHTMLElement* aContent,
             nsIAtom* atom, int32_t* aValue)
 {
   const nsAttrValue* value = aContent->GetParsedAttr(atom);
   if (value && value->Type() == nsAttrValue::eEnum) {
@@ -929,36 +927,26 @@ HTMLFormSubmission::GetFromForm(nsGeneri
   int32_t method = NS_FORM_METHOD_GET;
   if (aOriginatingElement &&
       aOriginatingElement->HasAttr(kNameSpaceID_None, nsGkAtoms::formmethod)) {
     GetEnumAttr(aOriginatingElement, nsGkAtoms::formmethod, &method);
   } else {
     GetEnumAttr(aForm, nsGkAtoms::method, &method);
   }
 
-  // Get charset
-  nsAutoCString charset;
-  GetSubmitCharset(aForm, charset);
-
-  // We now have a canonical charset name, so we only have to check it
-  // against canonical names.
-
-  // use UTF-8 for UTF-16* (per WHATWG and existing practice of
-  // MS IE/Opera).
-  if (StringBeginsWith(charset, NS_LITERAL_CSTRING("UTF-16"))) {
-    charset.AssignLiteral("UTF-8");
-  }
+  // Get encoding
+  auto encoding = GetSubmitEncoding(aForm)->OutputEncoding();
 
   // Choose encoder
   if (method == NS_FORM_METHOD_POST &&
       enctype == NS_FORM_ENCTYPE_MULTIPART) {
-    *aFormSubmission = new FSMultipartFormData(charset, aOriginatingElement);
+    *aFormSubmission = new FSMultipartFormData(encoding, aOriginatingElement);
   } else if (method == NS_FORM_METHOD_POST &&
              enctype == NS_FORM_ENCTYPE_TEXTPLAIN) {
-    *aFormSubmission = new FSTextPlain(charset, aOriginatingElement);
+    *aFormSubmission = new FSTextPlain(encoding, aOriginatingElement);
   } else {
     nsIDocument* doc = aForm->OwnerDoc();
     if (enctype == NS_FORM_ENCTYPE_MULTIPART ||
         enctype == NS_FORM_ENCTYPE_TEXTPLAIN) {
       nsAutoString enctypeStr;
       if (aOriginatingElement &&
           aOriginatingElement->HasAttr(kNameSpaceID_None,
                                        nsGkAtoms::formenctype)) {
@@ -966,17 +954,17 @@ HTMLFormSubmission::GetFromForm(nsGeneri
                                      enctypeStr);
       } else {
         aForm->GetAttr(kNameSpaceID_None, nsGkAtoms::enctype, enctypeStr);
       }
       const char16_t* enctypeStrPtr = enctypeStr.get();
       SendJSWarning(doc, "ForgotPostWarning",
                     &enctypeStrPtr, 1);
     }
-    *aFormSubmission = new FSURLEncoded(charset, method, doc,
-                                        aOriginatingElement);
+    *aFormSubmission =
+      new FSURLEncoded(encoding, method, doc, aOriginatingElement);
   }
 
   return NS_OK;
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/html/HTMLFormSubmission.h
+++ b/dom/html/HTMLFormSubmission.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_HTMLFormSubmission_h
 #define mozilla_dom_HTMLFormSubmission_h
 
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 #include "nsIContent.h"
-#include "nsNCRFallbackEncoderWrapper.h"
+#include "mozilla/Encoding.h"
 #include "nsString.h"
 
 class nsIURI;
 class nsIInputStream;
 class nsGenericHTMLElement;
 class nsIMultiplexInputStream;
 
 namespace mozilla {
@@ -107,84 +107,77 @@ public:
    * @param aPostDataStream a data stream for POST data [OUT]
    */
   virtual nsresult
   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) = 0;
 
   /**
    * Get the charset that will be used for submission.
    */
-  void GetCharset(nsACString& aCharset)
-  {
-    aCharset = mCharset;
-  }
+  void GetCharset(nsACString& aCharset) { mEncoding->Name(aCharset); }
 
   nsIContent* GetOriginatingElement() const
   {
     return mOriginatingElement.get();
   }
 
 protected:
   /**
    * Can only be constructed by subclasses.
    *
-   * @param aCharset the charset of the form as a string
+   * @param aEncoding the character encoding of the form
    * @param aOriginatingElement the originating element (can be null)
    */
-  HTMLFormSubmission(const nsACString& aCharset,
+  HTMLFormSubmission(mozilla::NotNull<const mozilla::Encoding*> aEncoding,
                      nsIContent* aOriginatingElement)
-    : mCharset(aCharset)
+    : mEncoding(aEncoding)
     , mOriginatingElement(aOriginatingElement)
   {
     MOZ_COUNT_CTOR(HTMLFormSubmission);
   }
 
-  // The name of the encoder charset
-  nsCString mCharset;
+  // The character encoding of this form submission
+  mozilla::NotNull<const mozilla::Encoding*> mEncoding;
 
   // Originating element.
   nsCOMPtr<nsIContent> mOriginatingElement;
 };
 
 class EncodingFormSubmission : public HTMLFormSubmission
 {
 public:
-  EncodingFormSubmission(const nsACString& aCharset,
+  EncodingFormSubmission(mozilla::NotNull<const mozilla::Encoding*> aEncoding,
                          nsIContent* aOriginatingElement);
 
   virtual ~EncodingFormSubmission();
 
   /**
    * Encode a Unicode string to bytes using the encoder (or just copy the input
    * if there is no encoder).
    * @param aStr the string to encode
    * @param aResult the encoded string [OUT]
    * @param aHeaderEncode If true, turns all linebreaks into spaces and escapes
    *                      all quotes
    * @throws an error if UnicodeToNewBytes fails
    */
   nsresult EncodeVal(const nsAString& aStr, nsCString& aResult,
                      bool aHeaderEncode);
-
-private:
-  // The encoder that will encode Unicode names and values
-  nsNCRFallbackEncoderWrapper mEncoder;
 };
 
 /**
  * Handle multipart/form-data encoding, which does files as well as normal
  * inputs.  This always does POST.
  */
 class FSMultipartFormData : public EncodingFormSubmission
 {
 public:
   /**
-   * @param aCharset the charset of the form as a string
+   * @param aEncoding the character encoding of the form
    */
-  FSMultipartFormData(const nsACString& aCharset,
+  FSMultipartFormData(mozilla::NotNull<const mozilla::Encoding*> aEncoding,
                       nsIContent* aOriginatingElement);
   ~FSMultipartFormData();
  
   virtual nsresult
   AddNameValuePair(const nsAString& aName, const nsAString& aValue) override;
 
   virtual nsresult
   AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -713,17 +713,17 @@ nsHTMLDocument::StartDocumentLoad(const 
       TryCacheCharset(cachingChan, charsetSource, charset);
     }
 
     TryTLD(charsetSource, charset);
     TryFallback(charsetSource, charset);
 
     if (wyciwygChannel) {
       // We know for sure that the parser needs to be using UTF16.
-      parserCharset = "UTF-16";
+      parserCharset = "UTF-16LE";
       parserCharsetSource = charsetSource < kCharsetFromChannel ?
         kCharsetFromChannel : charsetSource;
 
       nsAutoCString cachedCharset;
       int32_t cachedSource;
       rv = wyciwygChannel->GetCharsetAndSource(&cachedSource, cachedCharset);
       if (NS_SUCCEEDED(rv)) {
         if (cachedSource > charsetSource) {
--- a/dom/json/nsJSON.cpp
+++ b/dom/json/nsJSON.cpp
@@ -7,31 +7,28 @@
 #include "jsapi.h"
 #include "js/CharacterEncoding.h"
 #include "nsJSON.h"
 #include "nsIXPConnect.h"
 #include "nsIXPCScriptable.h"
 #include "nsStreamUtils.h"
 #include "nsIInputStream.h"
 #include "nsStringStream.h"
-#include "mozilla/dom/EncodingUtils.h"
-#include "nsIUnicodeEncoder.h"
-#include "nsIUnicodeDecoder.h"
 #include "nsNetUtil.h"
 #include "nsIURI.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsIScriptError.h"
 #include "nsCRTGlue.h"
 #include "nsIScriptSecurityManager.h"
 #include "NullPrincipal.h"
 #include "mozilla/Maybe.h"
 #include <algorithm>
 
-using mozilla::dom::EncodingUtils;
+using namespace mozilla;
 
 #define JSON_STREAM_BUFSIZE 4096
 
 NS_INTERFACE_MAP_BEGIN(nsJSON)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSON)
   NS_INTERFACE_MAP_ENTRY(nsIJSON)
 NS_INTERFACE_MAP_END
 
@@ -87,25 +84,21 @@ nsJSON::Encode(JS::Handle<JS::Value> aVa
       aJSON.Append(writer.mOutputString);
     }
   }
 
   return rv;
 }
 
 static const char UTF8BOM[] = "\xEF\xBB\xBF";
-static const char UTF16LEBOM[] = "\xFF\xFE";
-static const char UTF16BEBOM[] = "\xFE\xFF";
 
 static nsresult CheckCharset(const char* aCharset)
 {
   // Check that the charset is permissible
-  if (!(strcmp(aCharset, "UTF-8") == 0 ||
-        strcmp(aCharset, "UTF-16LE") == 0 ||
-        strcmp(aCharset, "UTF-16BE") == 0)) {
+  if (!(strcmp(aCharset, "UTF-8") == 0)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJSON::EncodeToStream(nsIOutputStream *aStream,
@@ -130,28 +123,21 @@ nsJSON::EncodeToStream(nsIOutputStream *
     rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedStream),
                                     aStream, 4096);
     NS_ENSURE_SUCCESS(rv, rv);
   //  aStream = bufferedStream;
   //}
 
   uint32_t ignored;
   if (aWriteBOM) {
-    if (strcmp(aCharset, "UTF-8") == 0)
-      rv = aStream->Write(UTF8BOM, 3, &ignored);
-    else if (strcmp(aCharset, "UTF-16LE") == 0)
-      rv = aStream->Write(UTF16LEBOM, 2, &ignored);
-    else if (strcmp(aCharset, "UTF-16BE") == 0)
-      rv = aStream->Write(UTF16BEBOM, 2, &ignored);
+    rv = aStream->Write(UTF8BOM, 3, &ignored);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsJSONWriter writer(bufferedStream);
-  rv = writer.SetCharset(aCharset);
-  NS_ENSURE_SUCCESS(rv, rv);
 
   if (aArgc == 0) {
     return NS_OK;
   }
 
   rv = EncodeInternal(cx, val, &writer);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -253,48 +239,35 @@ nsJSON::EncodeInternal(JSContext* cx, co
 nsJSONWriter::nsJSONWriter() : mStream(nullptr),
                                mBuffer(nullptr),
                                mBufferCount(0),
                                mDidWrite(false),
                                mEncoder(nullptr)
 {
 }
 
-nsJSONWriter::nsJSONWriter(nsIOutputStream *aStream) : mStream(aStream),
-                                                       mBuffer(nullptr),
-                                                       mBufferCount(0),
-                                                       mDidWrite(false),
-                                                       mEncoder(nullptr)
+nsJSONWriter::nsJSONWriter(nsIOutputStream* aStream)
+  : mStream(aStream)
+  , mBuffer(nullptr)
+  , mBufferCount(0)
+  , mDidWrite(false)
+  , mEncoder(UTF_8_ENCODING->NewEncoder())
 {
 }
 
 nsJSONWriter::~nsJSONWriter()
 {
   delete [] mBuffer;
 }
 
 nsresult
-nsJSONWriter::SetCharset(const char* aCharset)
-{
-  nsresult rv = NS_OK;
-  if (mStream) {
-    mEncoder = EncodingUtils::EncoderForEncoding(aCharset);
-    rv = mEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Signal,
-                                          nullptr, '\0');
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return rv;
-}
-
-nsresult
 nsJSONWriter::Write(const char16_t *aBuffer, uint32_t aLength)
 {
   if (mStream) {
-    return WriteToStream(mStream, mEncoder, aBuffer, aLength);
+    return WriteToStream(mStream, mEncoder.get(), aBuffer, aLength);
   }
 
   if (!mDidWrite) {
     mBuffer = new char16_t[JSON_STREAM_BUFSIZE];
     mDidWrite = true;
   }
 
   if (JSON_STREAM_BUFSIZE <= aLength + mBufferCount) {
@@ -320,43 +293,45 @@ bool nsJSONWriter::DidWrite()
 
 void
 nsJSONWriter::FlushBuffer()
 {
   mOutputString.Append(mBuffer, mBufferCount);
 }
 
 nsresult
-nsJSONWriter::WriteToStream(nsIOutputStream *aStream,
-                            nsIUnicodeEncoder *encoder,
-                            const char16_t *aBuffer,
+nsJSONWriter::WriteToStream(nsIOutputStream* aStream,
+                            Encoder* encoder,
+                            const char16_t* aBuffer,
                             uint32_t aLength)
 {
-  nsresult rv;
-  int32_t srcLength = aLength;
-  uint32_t bytesWritten;
-
-  // The bytes written to the stream might differ from the char16_t size
-  int32_t aDestLength;
-  rv = encoder->GetMaxLength(aBuffer, srcLength, &aDestLength);
-  NS_ENSURE_SUCCESS(rv, rv);
+  uint8_t buffer[1024];
+  auto dst = MakeSpan(buffer);
+  auto src = MakeSpan(aBuffer, aLength);
 
-  // create the buffer we need
-  char* destBuf = (char *) moz_xmalloc(aDestLength);
-  if (!destBuf)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  rv = encoder->Convert(aBuffer, &srcLength, destBuf, &aDestLength);
-  if (NS_SUCCEEDED(rv))
-    rv = aStream->Write(destBuf, aDestLength, &bytesWritten);
-
-  free(destBuf);
-  mDidWrite = true;
-
-  return rv;
+  for (;;) {
+    uint32_t result;
+    size_t read;
+    size_t written;
+    bool hadErrors;
+    Tie(result, read, written, hadErrors) =
+      encoder->EncodeFromUTF16(src, dst, false);
+    Unused << hadErrors;
+    src = src.From(read);
+    uint32_t ignored;
+    nsresult rv =
+      aStream->Write(reinterpret_cast<const char*>(buffer), written, &ignored);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    if (result == kInputEmpty) {
+      mDidWrite = true;
+      return NS_OK;
+    }
+  }
 }
 
 NS_IMETHODIMP
 nsJSON::Decode(const nsAString& json, JSContext* cx,
                JS::MutableHandle<JS::Value> aRetval)
 {
   nsresult rv = WarnDeprecatedMethod(DecodeWarning);
   if (NS_FAILED(rv))
@@ -502,35 +477,25 @@ NS_INTERFACE_MAP_BEGIN(nsJSONListener)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsJSONListener)
 NS_IMPL_RELEASE(nsJSONListener)
 
 NS_IMETHODIMP
 nsJSONListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
-  mSniffBuffer.Truncate();
   mDecoder = nullptr;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJSONListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
                               nsresult aStatusCode)
 {
-  nsresult rv;
-
-  // This can happen with short UTF-8 messages (<4 bytes)
-  if (!mSniffBuffer.IsEmpty()) {
-    // Just consume mSniffBuffer
-    rv = ProcessBytes(nullptr, 0);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
   JS::Rooted<JS::Value> reviver(mCx, JS::NullValue()), value(mCx);
 
   JS::ConstTwoByteChars chars(reinterpret_cast<const char16_t*>(mBufferedChars.Elements()),
                               mBufferedChars.Length());
   bool ok = JS_ParseJSONWithReviver(mCx, chars.begin().get(),
                                       uint32_t(mBufferedChars.Length()),
                                       reviver, &value);
 
@@ -541,27 +506,18 @@ nsJSONListener::OnStopRequest(nsIRequest
 
 NS_IMETHODIMP
 nsJSONListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext,
                                 nsIInputStream *aStream,
                                 uint64_t aOffset, uint32_t aLength)
 {
   nsresult rv = NS_OK;
 
-  if (mNeedsConverter && mSniffBuffer.Length() < 4) {
-    uint32_t readCount = (aLength < 4) ? aLength : 4;
-    rv = NS_ConsumeStream(aStream, readCount, mSniffBuffer);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (mSniffBuffer.Length() < 4)
-      return NS_OK;
-  }
-
   char buffer[JSON_STREAM_BUFSIZE];
-  unsigned long bytesRemaining = aLength - mSniffBuffer.Length();
+  unsigned long bytesRemaining = aLength;
   while (bytesRemaining) {
     unsigned int bytesRead;
     rv = aStream->Read(buffer,
                        std::min((unsigned long)sizeof(buffer), bytesRemaining),
                        &bytesRead);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = ProcessBytes(buffer, bytesRead);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -569,84 +525,68 @@ nsJSONListener::OnDataAvailable(nsIReque
   }
 
   return rv;
 }
 
 nsresult
 nsJSONListener::ProcessBytes(const char* aBuffer, uint32_t aByteLength)
 {
-  nsresult rv;
-  // Check for BOM, or sniff charset
-  nsAutoCString charset;
   if (mNeedsConverter && !mDecoder) {
-    if (!nsContentUtils::CheckForBOM((const unsigned char*) mSniffBuffer.get(),
-                                      mSniffBuffer.Length(), charset)) {
-      // OK, found no BOM, sniff the first character to see what this is
-      // See section 3 of RFC4627 for details on why this works.
-      const char *buffer = mSniffBuffer.get();
-      if (mSniffBuffer.Length() >= 4) {
-        if (buffer[0] == 0x00 && buffer[1] != 0x00 &&
-            buffer[2] == 0x00 && buffer[3] != 0x00) {
-          charset = "UTF-16BE";
-        } else if (buffer[0] != 0x00 && buffer[1] == 0x00 &&
-                   buffer[2] != 0x00 && buffer[3] == 0x00) {
-          charset = "UTF-16LE";
-        } else if (buffer[0] != 0x00 && buffer[1] != 0x00 &&
-                   buffer[2] != 0x00 && buffer[3] != 0x00) {
-          charset = "UTF-8";
-        }
-      } else {
-        // Not enough bytes to sniff, assume UTF-8
-        charset = "UTF-8";
-      }
-    }
-
-    // We should have a unicode charset by now
-    rv = CheckCharset(charset.get());
-    NS_ENSURE_SUCCESS(rv, rv);
-    mDecoder = EncodingUtils::DecoderForEncoding(charset);
-
-    // consume the sniffed bytes
-    rv = ConsumeConverted(mSniffBuffer.get(), mSniffBuffer.Length());
-    NS_ENSURE_SUCCESS(rv, rv);
-    mSniffBuffer.Truncate();
+    // BOM sniffing is built into the decoder.
+    mDecoder = UTF_8_ENCODING->NewDecoder();
   }
 
   if (!aBuffer)
     return NS_OK;
 
+  nsresult rv;
   if (mNeedsConverter) {
     rv = ConsumeConverted(aBuffer, aByteLength);
   } else {
     uint32_t unichars = aByteLength / sizeof(char16_t);
     rv = Consume((char16_t *) aBuffer, unichars);
   }
 
   return rv;
 }
 
 nsresult
 nsJSONListener::ConsumeConverted(const char* aBuffer, uint32_t aByteLength)
 {
-  nsresult rv;
-  int32_t unicharLength = 0;
-  int32_t srcLen = aByteLength;
+  CheckedInt<size_t> needed = mDecoder->MaxUTF16BufferLength(aByteLength);
+  if (!needed.isValid()) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
-  rv = mDecoder->GetMaxLength(aBuffer, srcLen, &unicharLength);
-  NS_ENSURE_SUCCESS(rv, rv);
+  CheckedInt<size_t> total(needed);
+  total += mBufferedChars.Length();
+  if (!total.isValid()) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
-  char16_t* endelems = mBufferedChars.AppendElements(unicharLength);
-  int32_t preLength = unicharLength;
-  rv = mDecoder->Convert(aBuffer, &srcLen, endelems, &unicharLength);
-  if (NS_FAILED(rv))
-    return rv;
-  MOZ_ASSERT(preLength >= unicharLength, "GetMaxLength lied");
-  if (preLength > unicharLength)
-    mBufferedChars.TruncateLength(mBufferedChars.Length() - (preLength - unicharLength));
+  char16_t* endelems = mBufferedChars.AppendElements(needed.value(), fallible);
+  if (!endelems) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  auto src = AsBytes(MakeSpan(aBuffer, aByteLength));
+  auto dst = MakeSpan(endelems, needed.value());
+  uint32_t result;
+  size_t read;
+  size_t written;
+  bool hadErrors;
+  // Ignoring EOF like the old code
+  Tie(result, read, written, hadErrors) =
+    mDecoder->DecodeToUTF16(src, dst, false);
+  MOZ_ASSERT(result == kInputEmpty);
+  MOZ_ASSERT(read == src.Length());
+  MOZ_ASSERT(written <= needed.value());
+  Unused << hadErrors;
+  mBufferedChars.TruncateLength(total.value() - (needed.value() - written));
   return NS_OK;
 }
 
 nsresult
 nsJSONListener::Consume(const char16_t* aBuffer, uint32_t aByteLength)
 {
   if (!mBufferedChars.AppendElements(aBuffer, aByteLength))
     return NS_ERROR_FAILURE;
--- a/dom/json/nsJSON.h
+++ b/dom/json/nsJSON.h
@@ -6,45 +6,45 @@
 
 #ifndef nsJSON_h__
 #define nsJSON_h__
 
 #include "nsIJSON.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIOutputStream.h"
-#include "nsIUnicodeEncoder.h"
-#include "nsIUnicodeDecoder.h"
+#include "mozilla/Encoding.h"
 #include "nsIRequestObserver.h"
 #include "nsIStreamListener.h"
 #include "nsTArray.h"
 
 class nsIURI;
 
 class MOZ_STACK_CLASS nsJSONWriter
 {
 public:
   nsJSONWriter();
   explicit nsJSONWriter(nsIOutputStream* aStream);
   virtual ~nsJSONWriter();
-  nsresult SetCharset(const char *aCharset);
   nsCOMPtr<nsIOutputStream> mStream;
   nsresult Write(const char16_t *aBuffer, uint32_t aLength);
   nsString mOutputString;
   bool DidWrite();
   void FlushBuffer();
 
 protected:
   char16_t *mBuffer;
   uint32_t mBufferCount;
   bool mDidWrite;
-  nsresult WriteToStream(nsIOutputStream *aStream, nsIUnicodeEncoder *encoder,
-                         const char16_t *aBuffer, uint32_t aLength);
+  nsresult WriteToStream(nsIOutputStream* aStream,
+                         mozilla::Encoder* encoder,
+                         const char16_t* aBuffer,
+                         uint32_t aLength);
 
-  nsCOMPtr<nsIUnicodeEncoder> mEncoder;
+  mozilla::UniquePtr<mozilla::Encoder> mEncoder;
 };
 
 class nsJSON : public nsIJSON
 {
 public:
   nsJSON();
 
   NS_DECL_ISUPPORTS
@@ -78,17 +78,16 @@ public:
   NS_DECL_NSISTREAMLISTENER
 
 protected:
   virtual ~nsJSONListener();
 
   bool mNeedsConverter;
   JSContext *mCx;
   JS::Value *mRootVal;
-  nsCOMPtr<nsIUnicodeDecoder> mDecoder;
-  nsCString mSniffBuffer;
+  mozilla::UniquePtr<mozilla::Decoder> mDecoder;
   nsTArray<char16_t> mBufferedChars;
   nsresult ProcessBytes(const char* aBuffer, uint32_t aByteLength);
   nsresult ConsumeConverted(const char* aBuffer, uint32_t aByteLength);
   nsresult Consume(const char16_t *data, uint32_t len);
 };
 
 #endif
--- a/dom/json/test/unit/test_encode.js
+++ b/dom/json/test/unit/test_encode.js
@@ -38,17 +38,17 @@ function testStringEncode()
   // the otherwise-exactly-identical JSON.stringify.
 }
 
 function testToJSON() {
   var obj1 = {a:1};
   var obj2 = {foo:"bar"};
   do_check_eq(nativeJSON.encode({toJSON: () => obj1}), '{"a":1}');
   do_check_eq(nativeJSON.encode({toJSON: () => obj2}), '{"foo":"bar"}');
-  
+
   do_check_eq(nativeJSON.encode({toJSON: () => null}), null);
   do_check_eq(nativeJSON.encode({toJSON: () => ""}), null);
   do_check_eq(nativeJSON.encode({toJSON: () => undefined }), null);
   do_check_eq(nativeJSON.encode({toJSON: () => 5}), null);
   do_check_eq(nativeJSON.encode({toJSON: () => function(){}}), null);
   do_check_eq(nativeJSON.encode({toJSON: () => dump}), null);
 }
 
@@ -106,38 +106,31 @@ function testOutputStreams() {
     ['[1,2,3]', [1,2,3]],
     ['[1,null,3]',[1,,3]],
   ];
   for (var i = 0; i < pairs.length; i++)
   {
     var pair = pairs[i];
     if (pair[1] && (typeof pair[1] == "object")) {
       var utf8File = writeToFile(pair[1], "UTF-8", false);
-      var utf16LEFile = writeToFile(pair[1], "UTF-16LE", false);
-      var utf16BEFile = writeToFile(pair[1], "UTF-16BE", false);
 
       // all ascii with no BOMs, so this will work
-      do_check_eq(utf16LEFile.fileSize / 2, utf8File.fileSize);
-      do_check_eq(utf16LEFile.fileSize, utf16BEFile.fileSize);
+      do_check_eq(utf8File.fileSize, pair[0].length);
     }
   }
 
   // check BOMs
   // the clone() calls are there to work around -- bug 410005
   var f = writeToFile({},"UTF-8", true).clone();
   do_check_eq(f.fileSize, 5);
-  var f = writeToFile({},"UTF-16LE", true).clone();
-  do_check_eq(f.fileSize, 6);
-  var f = writeToFile({},"UTF-16BE", true).clone();
-  do_check_eq(f.fileSize, 6);
-  
+
   outputDir.remove(true);
 }
 
 function run_test()
 {
   testStringEncode();
   testToJSON();
   testThrowingToJSON();
-  
+
   testOutputStreams();
-  
+
 }
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -6,30 +6,28 @@
 #include "nsPluginTags.h"
 
 #include "prlink.h"
 #include "plstr.h"
 #include "nsIPluginInstanceOwner.h"
 #include "nsPluginsDir.h"
 #include "nsPluginHost.h"
 #include "nsIBlocklistService.h"
-#include "nsIUnicodeDecoder.h"
 #include "nsIPlatformCharset.h"
 #include "nsPluginLogging.h"
 #include "nsNPAPIPlugin.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 #include "nsNetUtil.h"
 #include <cctype>
-#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/Encoding.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/FakePluginTagInitBinding.h"
 
-using mozilla::dom::EncodingUtils;
 using mozilla::dom::FakePluginTagInit;
 using namespace mozilla;
 
 // These legacy flags are used in the plugin registry. The states are now
 // stored in prefs, but we still need to be able to import them.
 #define NS_PLUGIN_FLAG_ENABLED      0x0001    // is this plugin enabled?
 // no longer used                   0x0002    // reuse only if regenerating pluginreg.dat
 #define NS_PLUGIN_FLAG_FROMCACHE    0x0004    // this plugintag info was loaded from cache
@@ -440,70 +438,34 @@ nsPluginTag::InitSandboxLevel()
   if (mIsFlashPlugin && mSandboxLevel < 2) {
     mSandboxLevel = 2;
   }
 #endif
 #endif
 }
 
 #if !defined(XP_WIN) && !defined(XP_MACOSX)
-static nsresult ConvertToUTF8(nsIUnicodeDecoder *aUnicodeDecoder,
-                              nsAFlatCString& aString)
+static void
+ConvertToUTF8(nsAFlatCString& aString)
 {
-  int32_t numberOfBytes = aString.Length();
-  int32_t outUnicodeLen;
-  nsAutoString buffer;
-  nsresult rv = aUnicodeDecoder->GetMaxLength(aString.get(), numberOfBytes,
-                                              &outUnicodeLen);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!buffer.SetLength(outUnicodeLen, fallible))
-    return NS_ERROR_OUT_OF_MEMORY;
-  rv = aUnicodeDecoder->Convert(aString.get(), &numberOfBytes,
-                                buffer.BeginWriting(), &outUnicodeLen);
-  NS_ENSURE_SUCCESS(rv, rv);
-  buffer.SetLength(outUnicodeLen);
-  CopyUTF16toUTF8(buffer, aString);
-
-  return NS_OK;
+  Unused << UTF_8_ENCODING->DecodeWithoutBOMHandling(aString, aString);
 }
 #endif
 
 nsresult nsPluginTag::EnsureMembersAreUTF8()
 {
 #if defined(XP_WIN) || defined(XP_MACOSX)
   return NS_OK;
 #else
-  nsresult rv;
-
-  nsCOMPtr<nsIPlatformCharset> pcs =
-  do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIUnicodeDecoder> decoder;
-
-  nsAutoCString charset;
-  rv = pcs->GetCharset(kPlatformCharsetSel_FileName, charset);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!charset.LowerCaseEqualsLiteral("utf-8")) {
-    decoder = EncodingUtils::DecoderForEncoding(charset);
-    ConvertToUTF8(decoder, mFileName);
-    ConvertToUTF8(decoder, mFullPath);
-  }
-
-  // The description of the plug-in and the various MIME type descriptions
-  // should be encoded in the standard plain text file encoding for this system.
-  // XXX should we add kPlatformCharsetSel_PluginResource?
-  rv = pcs->GetCharset(kPlatformCharsetSel_PlainTextInFile, charset);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!charset.LowerCaseEqualsLiteral("utf-8")) {
-    decoder = EncodingUtils::DecoderForEncoding(charset);
-    ConvertToUTF8(decoder, mName);
-    ConvertToUTF8(decoder, mDescription);
-    for (uint32_t i = 0; i < mMimeDescriptions.Length(); ++i) {
-      ConvertToUTF8(decoder, mMimeDescriptions[i]);
-    }
+  ConvertToUTF8(mFileName);
+  ConvertToUTF8(mFullPath);
+  ConvertToUTF8(mName);
+  ConvertToUTF8(mDescription);
+  for (uint32_t i = 0; i < mMimeDescriptions.Length(); ++i) {
+    ConvertToUTF8(mMimeDescriptions[i]);
   }
   return NS_OK;
 #endif
 }
 
 void nsPluginTag::FixupVersion()
 {
 #if defined(XP_LINUX)
--- a/dom/script/ScriptLoadHandler.cpp
+++ b/dom/script/ScriptLoadHandler.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ScriptLoadHandler.h"
 #include "ScriptLoader.h"
 #include "ScriptTrace.h"
 
 #include "nsContentUtils.h"
 
-#include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/Telemetry.h"
 
 namespace mozilla {
 namespace dom {
 
 #undef LOG
 #define LOG(args) \
   MOZ_LOG(ScriptLoader::gScriptLoaderLog, mozilla::LogLevel::Debug, args)
@@ -95,41 +94,44 @@ ScriptLoadHandler::OnIncrementalData(nsI
   return rv;
 }
 
 nsresult
 ScriptLoadHandler::DecodeRawData(const uint8_t* aData,
                                  uint32_t aDataLength,
                                  bool aEndOfStream)
 {
-  int32_t srcLen = aDataLength;
-  const char* src = reinterpret_cast<const char*>(aData);
-  int32_t dstLen;
-  nsresult rv =
-    mDecoder->GetMaxLength(src, srcLen, &dstLen);
-
-  NS_ENSURE_SUCCESS(rv, rv);
+  CheckedInt<size_t> needed = mDecoder->MaxUTF16BufferLength(aDataLength);
+  if (!needed.isValid()) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   uint32_t haveRead = mRequest->mScriptText.length();
 
   CheckedInt<uint32_t> capacity = haveRead;
-  capacity += dstLen;
+  capacity += needed.value();
 
   if (!capacity.isValid() || !mRequest->mScriptText.reserve(capacity.value())) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  rv = mDecoder->Convert(src,
-                         &srcLen,
-                         mRequest->mScriptText.begin() + haveRead,
-                         &dstLen);
+  uint32_t result;
+  size_t read;
+  size_t written;
+  bool hadErrors;
+  Tie(result, read, written, hadErrors) = mDecoder->DecodeToUTF16(
+    MakeSpan(aData, aDataLength),
+    MakeSpan(mRequest->mScriptText.begin() + haveRead, needed.value()),
+    aEndOfStream);
+  MOZ_ASSERT(result == kInputEmpty);
+  MOZ_ASSERT(read == aDataLength);
+  MOZ_ASSERT(written <= needed.value());
+  Unused << hadErrors;
 
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  haveRead += dstLen;
+  haveRead += written;
   MOZ_ASSERT(haveRead <= capacity.value(), "mDecoder produced more data than expected");
   MOZ_ALWAYS_TRUE(mRequest->mScriptText.resizeUninitialized(haveRead));
 
   return NS_OK;
 }
 
 bool
 ScriptLoadHandler::EnsureDecoder(nsIIncrementalStreamLoader* aLoader,
@@ -159,46 +161,53 @@ ScriptLoadHandler::EnsureDecoder(nsIIncr
                                  const uint8_t* aData,
                                  uint32_t aDataLength,
                                  bool aEndOfStream,
                                  nsCString& oCharset)
 {
   // JavaScript modules are always UTF-8.
   if (mRequest->IsModuleRequest()) {
     oCharset = "UTF-8";
-    mDecoder = EncodingUtils::DecoderForEncoding(oCharset);
+    mDecoder = UTF_8_ENCODING->NewDecoderWithBOMRemoval();
     return true;
   }
 
   // Determine if BOM check should be done.  This occurs either
   // if end-of-stream has been reached, or at least 3 bytes have
   // been read from input.
   if (!aEndOfStream && (aDataLength < 3)) {
     return false;
   }
 
   // Do BOM detection.
-  if (nsContentUtils::CheckForBOM(aData, aDataLength, oCharset)) {
-    mDecoder = EncodingUtils::DecoderForEncoding(oCharset);
+  const Encoding* encoding;
+  size_t bomLength;
+  Tie(encoding, bomLength) = Encoding::ForBOM(MakeSpan(aData, aDataLength));
+  if (encoding) {
+    mDecoder = encoding->NewDecoderWithBOMRemoval();
+    encoding->Name(oCharset);
     return true;
   }
 
   // BOM detection failed, check content stream for charset.
   nsCOMPtr<nsIRequest> req;
   nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
   NS_ASSERTION(req, "StreamLoader's request went away prematurely");
   NS_ENSURE_SUCCESS(rv, false);
 
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(req);
 
-  if (channel &&
-      NS_SUCCEEDED(channel->GetContentCharset(oCharset)) &&
-      EncodingUtils::FindEncodingForLabel(oCharset, oCharset)) {
-    mDecoder = EncodingUtils::DecoderForEncoding(oCharset);
-    return true;
+  if (channel) {
+    nsAutoCString label;
+    if (NS_SUCCEEDED(channel->GetContentCharset(label)) &&
+        (encoding = Encoding::ForLabel(label))) {
+      mDecoder = encoding->NewDecoderWithoutBOMHandling();
+      encoding->Name(oCharset);
+      return true;
+    }
   }
 
   // Check the hint charset from the script element or preload
   // request.
   nsAutoString hintCharset;
   if (!mRequest->IsPreload()) {
     mRequest->mElement->GetScriptCharset(hintCharset);
   } else {
@@ -206,35 +215,36 @@ ScriptLoadHandler::EnsureDecoder(nsIIncr
       mScriptLoader->mPreloads.IndexOf(mRequest, 0,
             ScriptLoader::PreloadRequestComparator());
 
     NS_ASSERTION(i != mScriptLoader->mPreloads.NoIndex,
                  "Incorrect preload bookkeeping");
     hintCharset = mScriptLoader->mPreloads[i].mCharset;
   }
 
-  if (EncodingUtils::FindEncodingForLabel(hintCharset, oCharset)) {
-    mDecoder = EncodingUtils::DecoderForEncoding(oCharset);
+  if ((encoding = Encoding::ForLabel(hintCharset))) {
+    mDecoder = encoding->NewDecoderWithoutBOMHandling();
+    encoding->Name(oCharset);
     return true;
   }
 
   // Get the charset from the charset of the document.
   if (mScriptLoader->mDocument) {
-    oCharset = mScriptLoader->mDocument->GetDocumentCharacterSet();
-    mDecoder = EncodingUtils::DecoderForEncoding(oCharset);
+    encoding =
+      Encoding::ForName(mScriptLoader->mDocument->GetDocumentCharacterSet());
+    mDecoder = encoding->NewDecoderWithoutBOMHandling();
+    encoding->Name(oCharset);
     return true;
   }
 
   // Curiously, there are various callers that don't pass aDocument. The
   // fallback in the old code was ISO-8859-1, which behaved like
-  // windows-1252. Saying windows-1252 for clarity and for compliance
-  // with the Encoding Standard.
+  // windows-1252.
   oCharset = "windows-1252";
-  mDecoder = EncodingUtils::DecoderForEncoding(oCharset);
-
+  mDecoder = WINDOWS_1252_ENCODING->NewDecoderWithoutBOMHandling();
   return true;
 }
 
 nsresult
 ScriptLoadHandler::MaybeDecodeSRI()
 {
   if (!mSRIDataVerifier || mSRIDataVerifier->IsComplete() || NS_FAILED(mSRIStatus)) {
     return NS_OK;
--- a/dom/script/ScriptLoadHandler.h
+++ b/dom/script/ScriptLoadHandler.h
@@ -69,15 +69,15 @@ private:
 
   // SRI data verifier.
   nsAutoPtr<SRICheckDataVerifier> mSRIDataVerifier;
 
   // Status of SRI data operations.
   nsresult mSRIStatus;
 
   // Unicode decoder for charset.
-  nsCOMPtr<nsIUnicodeDecoder> mDecoder;
+  mozilla::UniquePtr<mozilla::Decoder> mDecoder;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ScriptLoadHandler_h
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -2437,88 +2437,87 @@ ScriptLoader::ConvertToUTF16(nsIChannel*
                              char16_t*& aBufOut, size_t& aLengthOut)
 {
   if (!aLength) {
     aBufOut = nullptr;
     aLengthOut = 0;
     return NS_OK;
   }
 
+  auto data = MakeSpan(aData, aLength);
+
   // The encoding info precedence is as follows from high to low:
   // The BOM
   // HTTP Content-Type (if name recognized)
   // charset attribute (if name recognized)
   // The encoding of the document
 
-  nsAutoCString charset;
-
-  nsCOMPtr<nsIUnicodeDecoder> unicodeDecoder;
-
-  if (nsContentUtils::CheckForBOM(aData, aLength, charset)) {
-    // charset is now one of "UTF-16BE", "UTF-16BE" or "UTF-8".  Those decoder
-    // will take care of swallowing the BOM.
-    unicodeDecoder = EncodingUtils::DecoderForEncoding(charset);
+  UniquePtr<Decoder> unicodeDecoder;
+
+  const Encoding* encoding;
+  size_t bomLength;
+  Tie(encoding, bomLength) = Encoding::ForBOM(data);
+  if (encoding) {
+    unicodeDecoder = encoding->NewDecoderWithBOMRemoval();
   }
 
-  if (!unicodeDecoder &&
-      aChannel &&
-      NS_SUCCEEDED(aChannel->GetContentCharset(charset)) &&
-      EncodingUtils::FindEncodingForLabel(charset, charset)) {
-    unicodeDecoder = EncodingUtils::DecoderForEncoding(charset);
+  if (!unicodeDecoder && aChannel) {
+    nsAutoCString label;
+    if (NS_SUCCEEDED(aChannel->GetContentCharset(label)) &&
+        (encoding = Encoding::ForLabel(label))) {
+      unicodeDecoder = encoding->NewDecoderWithoutBOMHandling();
+    }
   }
 
-  if (!unicodeDecoder &&
-      EncodingUtils::FindEncodingForLabel(aHintCharset, charset)) {
-    unicodeDecoder = EncodingUtils::DecoderForEncoding(charset);
+  if (!unicodeDecoder && (encoding = Encoding::ForLabel(aHintCharset))) {
+    unicodeDecoder = encoding->NewDecoderWithoutBOMHandling();
   }
 
   if (!unicodeDecoder && aDocument) {
-    charset = aDocument->GetDocumentCharacterSet();
-    unicodeDecoder = EncodingUtils::DecoderForEncoding(charset);
+    unicodeDecoder = Encoding::ForName(aDocument->GetDocumentCharacterSet())
+                       ->NewDecoderWithoutBOMHandling();
   }
 
   if (!unicodeDecoder) {
     // Curiously, there are various callers that don't pass aDocument. The
     // fallback in the old code was ISO-8859-1, which behaved like
-    // windows-1252. Saying windows-1252 for clarity and for compliance
-    // with the Encoding Standard.
-    charset = "windows-1252";
-    unicodeDecoder = EncodingUtils::DecoderForEncoding(charset);
+    // windows-1252.
+    unicodeDecoder = WINDOWS_1252_ENCODING->NewDecoderWithoutBOMHandling();
   }
 
-  int32_t unicodeLength = 0;
-
-  nsresult rv =
-    unicodeDecoder->GetMaxLength(reinterpret_cast<const char*>(aData),
-                                 aLength, &unicodeLength);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  aBufOut = static_cast<char16_t*>(js_malloc(unicodeLength * sizeof(char16_t)));
+  CheckedInt<size_t> unicodeLength =
+    unicodeDecoder->MaxUTF16BufferLength(aLength);
+  if (!unicodeLength.isValid()) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  aBufOut =
+    static_cast<char16_t*>(js_malloc(unicodeLength.value() * sizeof(char16_t)));
   if (!aBufOut) {
     aLengthOut = 0;
     return NS_ERROR_OUT_OF_MEMORY;
   }
-  aLengthOut = unicodeLength;
-
-  rv = unicodeDecoder->Convert(reinterpret_cast<const char*>(aData),
-                               (int32_t *) &aLength, aBufOut,
-                               &unicodeLength);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-  aLengthOut = unicodeLength;
-  if (NS_FAILED(rv)) {
-    js_free(aBufOut);
-    aBufOut = nullptr;
-    aLengthOut = 0;
-  }
-  if (charset.Length() == 0) {
-    charset = "?";
-  }
+
+  uint32_t result;
+  size_t read;
+  size_t written;
+  bool hadErrors;
+  Tie(result, read, written, hadErrors) = unicodeDecoder->DecodeToUTF16(
+    data, MakeSpan(aBufOut, unicodeLength.value()), true);
+  MOZ_ASSERT(result == kInputEmpty);
+  MOZ_ASSERT(read == aLength);
+  MOZ_ASSERT(written <= unicodeLength.value());
+  Unused << hadErrors;
+  aLengthOut = written;
+
+  nsAutoCString charset;
+  unicodeDecoder->Encoding()->Name(charset);
   mozilla::Telemetry::Accumulate(mozilla::Telemetry::DOM_SCRIPT_SRC_ENCODING,
     charset);
-  return rv;
+  return NS_OK;
 }
 
 nsresult
 ScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
                                ScriptLoadRequest* aRequest,
                                nsresult aChannelStatus,
                                nsresult aSRIStatus,
                                mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier)
--- a/dom/script/ScriptLoader.h
+++ b/dom/script/ScriptLoader.h
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ScriptLoader_h
 #define mozilla_dom_ScriptLoader_h
 
 #include "nsCOMPtr.h"
 #include "nsRefPtrHashtable.h"
-#include "nsIUnicodeDecoder.h"
+#include "mozilla/Encoding.h"
 #include "nsIScriptElement.h"
 #include "nsCOMArray.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsICacheInfoChannel.h"
 #include "nsIDocument.h"
 #include "nsIIncrementalStreamLoader.h"
--- a/dom/url/URLSearchParams.cpp
+++ b/dom/url/URLSearchParams.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "URLSearchParams.h"
 #include "mozilla/dom/URLSearchParamsBinding.h"
-#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/Encoding.h"
 #include "nsDOMString.h"
 #include "nsIInputStream.h"
 #include "nsStringStream.h"
 
 namespace mozilla {
 namespace dom {
 
 bool
@@ -100,48 +100,18 @@ URLParams::Delete(const nsAString& aName
   }
 
   return found;
 }
 
 void
 URLParams::ConvertString(const nsACString& aInput, nsAString& aOutput)
 {
-  aOutput.Truncate();
-
-  if (!mDecoder) {
-    mDecoder = EncodingUtils::DecoderForEncoding("UTF-8");
-    if (!mDecoder) {
-      MOZ_ASSERT(mDecoder, "Failed to create a decoder.");
-      return;
-    }
-  }
-
-  int32_t inputLength = aInput.Length();
-  int32_t outputLength = 0;
-
-  nsresult rv = mDecoder->GetMaxLength(aInput.BeginReading(), inputLength,
-                                       &outputLength);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  if (!aOutput.SetLength(outputLength, fallible)) {
-    return;
-  }
-
-  int32_t newOutputLength = outputLength;
-  rv = mDecoder->Convert(aInput.BeginReading(), &inputLength,
-                         aOutput.BeginWriting(), &newOutputLength);
-  if (NS_FAILED(rv)) {
-    aOutput.Truncate();
-    return;
-  }
-  if (newOutputLength < outputLength) {
-    aOutput.Truncate(newOutputLength);
+  if (NS_FAILED(UTF_8_ENCODING->DecodeWithoutBOMHandling(aInput, aOutput))) {
+    MOZ_CRASH("Out of memory when converting URL params.");
   }
 }
 
 void
 URLParams::DecodeString(const nsACString& aInput, nsAString& aOutput)
 {
   nsACString::const_iterator start, end;
   aInput.BeginReading(start);
--- a/dom/url/URLSearchParams.h
+++ b/dom/url/URLSearchParams.h
@@ -8,17 +8,16 @@
 #define mozilla_dom_URLSearchParams_h
 
 #include "js/StructuredClone.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "nsISupports.h"
-#include "nsIUnicodeDecoder.h"
 #include "nsIXMLHttpRequest.h"
 
 namespace mozilla {
 namespace dom {
 
 class URLSearchParams;
 class USVStringSequenceSequenceOrUSVStringUSVStringRecordOrUSVString;
 
@@ -117,17 +116,16 @@ private:
 
   struct Param
   {
     nsString mKey;
     nsString mValue;
   };
 
   nsTArray<Param> mParams;
-  nsCOMPtr<nsIUnicodeDecoder> mDecoder;
 };
 
 class URLSearchParams final : public nsIXHRSendable,
                               public nsWrapperCache
 {
   ~URLSearchParams();
 
 public:
--- a/dom/url/tests/test_urlSearchParams_utf8.html
+++ b/dom/url/tests/test_urlSearchParams_utf8.html
@@ -21,20 +21,19 @@ https://bugzilla.mozilla.org/show_bug.cg
 </pre>
 <a href="http://www.example.net?a=b&c=d" id="anchor">foobar</a>
 <area href="http://www.example.net?a=b&c=d" id="area">foobar</area>
 <script type="application/javascript">
 
   /** Test for Bug 1032511 **/
   var a = new URLSearchParams("%e2");
   ok(a, "a exists");
-  is(a.toString(), '=', "The value should be here.");
+  is(a.toString(), '%EF%BF%BD=', "The value should be here.");
 
   a = new URLSearchParams("a%e2");
-  // This is a known decoder bug that fails to emit a REPLACEMENT CHARACTER.
-  is(a.toString(), 'a=', "The value should be here.");
+  is(a.toString(), 'a%EF%BF%BD=', "The value should be here.");
 
   a = new URLSearchParams("a%e2b");
   is(a.toString(), 'a%EF%BF%BDb=', "The value should be here.");
 
 </script>
 </body>
 </html>
--- a/dom/webidl/TextEncoder.webidl
+++ b/dom/webidl/TextEncoder.webidl
@@ -10,11 +10,19 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 [Constructor,
  Exposed=(Window,Worker,System)]
 interface TextEncoder {
   [Constant]
   readonly attribute DOMString encoding;
+  /*
+   * This is spec-wise USVString but marking it as
+   * DOMString to avoid duplicate work. Since the
+   * UTF-16 to UTF-8 converter performs processing
+   * that's equivalent to first converting a
+   * DOMString to a USVString, let's avoid having
+   * the binding code doing it, too.
+   */
   [NewObject]
-  Uint8Array encode(optional USVString input = "");
+  Uint8Array encode(optional DOMString input = "");
 };
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -8,18 +8,17 @@
 
 #include "nsAutoPtr.h"
 #include "nsIConsoleReportCollector.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIOutputStream.h"
 #include "nsIScriptError.h"
 #include "nsITimedChannel.h"
-#include "nsIUnicodeDecoder.h"
-#include "nsIUnicodeEncoder.h"
+#include "mozilla/Encoding.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStreamUtils.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsSerializationHelper.h"
@@ -975,42 +974,31 @@ NS_IMPL_RELEASE_INHERITED(ExtendableEven
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ExtendableEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
 
 namespace {
 nsresult
 ExtractBytesFromUSVString(const nsAString& aStr, nsTArray<uint8_t>& aBytes)
 {
   MOZ_ASSERT(aBytes.IsEmpty());
-  nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
-  if (NS_WARN_IF(!encoder)) {
+  auto encoder = UTF_8_ENCODING->NewEncoder();
+  CheckedInt<size_t> needed =
+    encoder->MaxBufferLengthFromUTF16WithoutReplacement(aStr.Length());
+  if (NS_WARN_IF(!needed.isValid() ||
+                 !aBytes.SetLength(needed.value(), fallible))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
-
-  int32_t srcLen = aStr.Length();
-  int32_t destBufferLen;
-  nsresult rv = encoder->GetMaxLength(aStr.BeginReading(), srcLen, &destBufferLen);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (NS_WARN_IF(!aBytes.SetLength(destBufferLen, fallible))) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  char* destBuffer = reinterpret_cast<char*>(aBytes.Elements());
-  int32_t outLen = destBufferLen;
-  rv = encoder->Convert(aStr.BeginReading(), &srcLen, destBuffer, &outLen);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aBytes.Clear();
-    return rv;
-  }
-
-  aBytes.TruncateLength(outLen);
-
+  uint32_t result;
+  size_t read;
+  size_t written;
+  Tie(result, read, written) =
+    encoder->EncodeFromUTF16WithoutReplacement(aStr, aBytes, true);
+  MOZ_ASSERT(result == kInputEmpty);
+  MOZ_ASSERT(read == aStr.Length());
+  aBytes.TruncateLength(written);
   return NS_OK;
 }
 
 nsresult
 ExtractBytesFromData(const OwningArrayBufferViewOrArrayBufferOrUSVString& aDataInit, nsTArray<uint8_t>& aBytes)
 {
   if (aDataInit.IsArrayBufferView()) {
     const ArrayBufferView& view = aDataInit.GetAsArrayBufferView();
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -72,17 +72,16 @@
 #include "nsIContentSecurityPolicy.h"
 #include "nsAsyncRedirectVerifyHelper.h"
 #include "nsStringBuffer.h"
 #include "nsIFileChannel.h"
 #include "mozilla/Telemetry.h"
 #include "jsfriendapi.h"
 #include "GeckoProfiler.h"
 #include "mozilla/dom/EncodingUtils.h"
-#include "nsIUnicodeDecoder.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/Attributes.h"
 #include "MultipartBlobImpl.h"
 #include "nsIPermissionManager.h"
 #include "nsMimeTypes.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIClassOfService.h"
 #include "nsCharSeparatedTokenizer.h"
@@ -524,45 +523,48 @@ XMLHttpRequestMainThread::DetectCharset(
 }
 
 nsresult
 XMLHttpRequestMainThread::AppendToResponseText(const char * aSrcBuffer,
                                                uint32_t aSrcBufferLen)
 {
   NS_ENSURE_STATE(mDecoder);
 
-  int32_t destBufferLen;
-  nsresult rv = mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen,
-                                       &destBufferLen);
-  NS_ENSURE_SUCCESS(rv, rv);
+  CheckedInt<size_t> destBufferLen =
+    mDecoder->MaxUTF16BufferLength(aSrcBufferLen);
+  if (!destBufferLen.isValid()) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   CheckedInt32 size = mResponseText.Length();
-  size += destBufferLen;
+  size += destBufferLen.value();
   if (!size.isValid()) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   XMLHttpRequestStringWriterHelper helper(mResponseText);
 
-  if (!helper.AddCapacity(destBufferLen)) {
+  if (!helper.AddCapacity(destBufferLen.value())) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  // This code here is basically a copy of a similar thing in
-  // nsScanner::Append(const char* aBuffer, uint32_t aLen).
-  int32_t srclen = (int32_t)aSrcBufferLen;
-  int32_t destlen = (int32_t)destBufferLen;
-  rv = mDecoder->Convert(aSrcBuffer,
-                         &srclen,
-                         helper.EndOfExistingData(),
-                         &destlen);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-  MOZ_ASSERT(destlen <= destBufferLen);
-
-  helper.AddLength(destlen);
+  // XXX there's no handling for incomplete byte sequences on EOF!
+  uint32_t result;
+  size_t read;
+  size_t written;
+  bool hadErrors;
+  Tie(result, read, written, hadErrors) = mDecoder->DecodeToUTF16(
+    AsBytes(MakeSpan(aSrcBuffer, aSrcBufferLen)),
+    MakeSpan(helper.EndOfExistingData(), destBufferLen.value()),
+    false);
+  MOZ_ASSERT(result == kInputEmpty);
+  MOZ_ASSERT(read == aSrcBufferLen);
+  MOZ_ASSERT(written <= destBufferLen.value());
+  Unused << hadErrors;
+  helper.AddLength(written);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::GetResponseText(nsAString& aResponseText)
 {
   ErrorResult rv;
   DOMString str;
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -39,26 +39,26 @@
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FormData.h"
 #include "mozilla/dom/URLSearchParams.h"
 #include "mozilla/dom/XMLHttpRequest.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestEventTarget.h"
 #include "mozilla/dom/XMLHttpRequestString.h"
+#include "mozilla/Encoding.h"
 
 #ifdef Status
 /* Xlib headers insist on this for some reason... Nuke it because
    it'll override our member name */
 #undef Status
 #endif
 
 class nsIJARChannel;
 class nsILoadGroup;
-class nsIUnicodeDecoder;
 class nsIJSID;
 
 namespace mozilla {
 namespace dom {
 
 class BlobSet;
 class DOMString;
 class XMLHttpRequestUpload;
@@ -687,17 +687,17 @@ protected:
   uint32_t mResponseBodyDecodedPos;
 
   // Decoder used for decoding into mResponseText
   // Only used for DEFAULT, TEXT and JSON responseTypes.
   // In cases where we've only received half a surrogate, the decoder itself
   // carries the state to remember this. Next time we receive more data we
   // simply feed the new data into the decoder which will handle the second
   // part of the surrogate.
-  nsCOMPtr<nsIUnicodeDecoder> mDecoder;
+  mozilla::UniquePtr<mozilla::Decoder> mDecoder;
 
   nsCString mResponseCharset;
 
   void MatchCharsetAndDecoderToResponseDocument();
 
   XMLHttpRequestResponseType mResponseType;
 
   // It is either a cached blob-response from the last call to GetResponse,
--- a/extensions/spellcheck/hunspell/glue/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/glue/mozHunspell.cpp
@@ -69,38 +69,34 @@
 #include "mozISpellI18NManager.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "mozInlineSpellChecker.h"
 #include "mozilla/Services.h"
 #include <stdlib.h>
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
-#include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/ContentParent.h"
 
 using mozilla::dom::ContentParent;
-using mozilla::dom::EncodingUtils;
+using namespace mozilla;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozHunspell)
 
 NS_INTERFACE_MAP_BEGIN(mozHunspell)
   NS_INTERFACE_MAP_ENTRY(mozISpellCheckingEngine)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozISpellCheckingEngine)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozHunspell)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTION(mozHunspell,
-                         mPersonalDictionary,
-                         mEncoder,
-                         mDecoder)
+NS_IMPL_CYCLE_COLLECTION(mozHunspell, mPersonalDictionary)
 
 template<> mozilla::Atomic<size_t> mozilla::CountingAllocatorBase<HunspellAllocator>::sAmount(0);
 
 mozHunspell::mozHunspell()
   : mHunspell(nullptr)
 {
 #ifdef DEBUG
   // There must be only one instance of this class: it reports memory based on
@@ -194,26 +190,23 @@ NS_IMETHODIMP mozHunspell::SetDictionary
   mDictionary = aDictionary;
   mAffixFileName = affFileName;
 
   mHunspell = new Hunspell(affFileName.get(),
                          dictFileName.get());
   if (!mHunspell)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  nsAutoCString label(mHunspell->get_dict_encoding().c_str());
-  nsAutoCString encoding;
-  if (!EncodingUtils::FindEncodingForLabelNoReplacement(label, encoding)) {
+  auto encoding =
+    Encoding::ForLabelNoReplacement(mHunspell->get_dict_encoding());
+  if (!encoding) {
     return NS_ERROR_UCONV_NOCONV;
   }
-  mEncoder = EncodingUtils::EncoderForEncoding(encoding);
-  mDecoder = EncodingUtils::DecoderForEncoding(encoding);
-
-  if (mEncoder)
-    mEncoder->SetOutputErrorBehavior(mEncoder->kOnError_Signal, nullptr, '?');
+  mEncoder = encoding->NewEncoder();
+  mDecoder = encoding->NewDecoderWithoutBOMHandling();
 
   int32_t pos = mDictionary.FindChar('-');
   if (pos == -1)
     pos = mDictionary.FindChar('_');
 
   if (pos == -1)
     mLanguage.Assign(mDictionary);
   else
@@ -480,30 +473,41 @@ mozHunspell::LoadDictionariesFromDir(nsI
 }
 
 nsresult
 mozHunspell::ConvertCharset(const char16_t* aStr, std::string* aDst)
 {
   NS_ENSURE_ARG_POINTER(aDst);
   NS_ENSURE_TRUE(mEncoder, NS_ERROR_NULL_POINTER);
 
-  int32_t outLength;
-  int32_t inLength = NS_strlen(aStr);
-  nsresult rv = mEncoder->GetMaxLength(aStr, inLength, &outLength);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  aDst->resize(outLength);
-
-  char* dst = &aDst->operator[](0);
-  rv = mEncoder->Convert(aStr, &inLength, dst, &outLength);
-  if (NS_SUCCEEDED(rv)) {
-    aDst->resize(outLength);
+  auto src = MakeStringSpan(aStr);
+  CheckedInt<size_t> needed =
+    mEncoder->MaxBufferLengthFromUTF16WithoutReplacement(src.Length());
+  if (!needed.isValid()) {
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  return rv;
+  aDst->resize(needed.value());
+
+  char* dstPtr = &aDst->operator[](0);
+  auto dst = MakeSpan(reinterpret_cast<uint8_t*>(dstPtr), needed.value());
+
+  uint32_t result;
+  size_t read;
+  size_t written;
+  Tie(result, read, written) =
+    mEncoder->EncodeFromUTF16WithoutReplacement(src, dst, true);
+  Unused << read;
+  MOZ_ASSERT(result != kOutputFull);
+  if (result != kInputEmpty) {
+    return NS_ERROR_UENC_NOMAPPING;
+  }
+  aDst->resize(written);
+  mEncoder->Encoding()->NewEncoderInto(*mEncoder);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 mozHunspell::CollectReports(nsIHandleReportCallback* aHandleReport,
                             nsISupports* aData, bool aAnonymize)
 {
   MOZ_COLLECT_REPORT(
     "explicit/spell-check", KIND_HEAP, UNITS_BYTES,
@@ -547,32 +551,52 @@ NS_IMETHODIMP mozHunspell::Suggest(const
   std::vector<std::string> suggestions = mHunspell->suggest(charsetWord);
   *aSuggestionCount = static_cast<uint32_t>(suggestions.size());
 
   if (*aSuggestionCount) {
     *aSuggestions  = (char16_t **)moz_xmalloc(*aSuggestionCount * sizeof(char16_t *));
     if (*aSuggestions) {
       uint32_t index = 0;
       for (index = 0; index < *aSuggestionCount && NS_SUCCEEDED(rv); ++index) {
+        // If the IDL used an array of AString, we could use
+        // Encoding::DecodeWithoutBOMHandling() here.
         // Convert the suggestion to utf16
-        int32_t inLength = suggestions[index].size();
-        int32_t outLength;
-        rv = mDecoder->GetMaxLength(suggestions[index].c_str(), inLength, &outLength);
-        if (NS_SUCCEEDED(rv))
-        {
-          (*aSuggestions)[index] = (char16_t *) moz_xmalloc(sizeof(char16_t) * (outLength+1));
-          if ((*aSuggestions)[index])
-          {
-            rv = mDecoder->Convert(suggestions[index].c_str(), &inLength, (*aSuggestions)[index], &outLength);
-            if (NS_SUCCEEDED(rv))
-              (*aSuggestions)[index][outLength] = 0;
-          }
-          else
-            rv = NS_ERROR_OUT_OF_MEMORY;
+        Span<const char> charSrc(suggestions[index]);
+        auto src = AsBytes(charSrc);
+        CheckedInt<size_t> needed =
+          mDecoder->MaxUTF16BufferLength(src.Length());
+        if (!needed.isValid()) {
+          rv = NS_ERROR_OUT_OF_MEMORY;
+          break;
+        }
+        size_t dstLen = needed.value();
+        needed += 1;
+        needed *= sizeof(char16_t);
+        if (!needed.isValid()) {
+          rv = NS_ERROR_OUT_OF_MEMORY;
+          break;
         }
+        (*aSuggestions)[index] = (char16_t*)moz_xmalloc(needed.value());
+        if (!((*aSuggestions)[index])) {
+          rv = NS_ERROR_OUT_OF_MEMORY;
+          break;
+        }
+        auto dst = MakeSpan((*aSuggestions)[index], dstLen);
+        uint32_t result;
+        size_t read;
+        size_t written;
+        bool hadErrors;
+        Tie(result, read, written, hadErrors) =
+          mDecoder->DecodeToUTF16(src, dst, true);
+        MOZ_ASSERT(result == kInputEmpty);
+        MOZ_ASSERT(read == src.Length());
+        MOZ_ASSERT(written <= dstLen);
+        Unused << hadErrors;
+        (*aSuggestions)[index][written] = 0;
+        mDecoder->Encoding()->NewDecoderWithoutBOMHandlingInto(*mDecoder);
       }
 
       if (NS_FAILED(rv))
         NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(index, *aSuggestions); // free the char16_t strings up to the point at which the error occurred
     }
     else // if (*aSuggestions)
       rv = NS_ERROR_OUT_OF_MEMORY;
   }
--- a/extensions/spellcheck/hunspell/glue/mozHunspell.h
+++ b/extensions/spellcheck/hunspell/glue/mozHunspell.h
@@ -63,18 +63,17 @@
 #include <hunspell.hxx>
 #include "mozISpellCheckingEngine.h"
 #include "mozIPersonalDictionary.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsIMemoryReporter.h"
 #include "nsIObserver.h"
-#include "nsIUnicodeEncoder.h"
-#include "nsIUnicodeDecoder.h"
+#include "mozilla/Encoding.h"
 #include "nsInterfaceHashtable.h"
 #include "nsWeakReference.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozHunspellAllocator.h"
 
 #define MOZ_HUNSPELL_CONTRACTID "@mozilla.org/spellchecker/engine;1"
 #define MOZ_HUNSPELL_CID         \
 /* 56c778e4-1bee-45f3-a689-886692a97fe7 */   \
@@ -102,18 +101,18 @@ public:
   nsresult ConvertCharset(const char16_t* aStr, std::string* aDst);
 
   NS_DECL_NSIMEMORYREPORTER
 
 protected:
   virtual ~mozHunspell();
 
   nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary;
-  nsCOMPtr<nsIUnicodeEncoder>      mEncoder;
-  nsCOMPtr<nsIUnicodeDecoder>      mDecoder;
+  mozilla::UniquePtr<mozilla::Encoder> mEncoder;
+  mozilla::UniquePtr<mozilla::Decoder> mDecoder;
 
   // Hashtable matches dictionary name to .aff file
   nsInterfaceHashtable<nsStringHashKey, nsIFile> mDictionaries;
   nsString  mDictionary;
   nsString  mLanguage;
   nsCString mAffixFileName;
 
   // dynamic dirs used to search for dictionaries
--- a/extensions/spellcheck/hunspell/src/csutil.cxx
+++ b/extensions/spellcheck/hunspell/src/csutil.cxx
@@ -97,22 +97,20 @@ struct unicode_info {
 #ifndef MOZILLA_CLIENT
 #include "utf_info.cxx"
 #define UTF_LST_LEN (sizeof(utf_lst) / (sizeof(unicode_info)))
 #endif
 #endif
 
 #ifdef MOZILLA_CLIENT
 #include "nsCOMPtr.h"
-#include "nsIUnicodeEncoder.h"
-#include "nsIUnicodeDecoder.h"
 #include "nsUnicharUtils.h"
-#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/Encoding.h"
 
-using mozilla::dom::EncodingUtils;
+using namespace mozilla;
 #endif
 
 struct unicode_info2 {
   char cletter;
   unsigned short cupper;
   unsigned short clower;
 };
 
@@ -2301,67 +2299,73 @@ struct cs_info* get_current_cs(const std
   // Initialze the array with dummy data so that we wouldn't need
   // to return null in case of failures.
   for (int i = 0; i <= 0xff; ++i) {
     ccs[i].ccase = false;
     ccs[i].clower = i;
     ccs[i].cupper = i;
   }
 
-  nsCOMPtr<nsIUnicodeEncoder> encoder;
-  nsCOMPtr<nsIUnicodeDecoder> decoder;
-
-  nsresult rv;
-
-  nsAutoCString label(es.c_str());
-  nsAutoCString encoding;
-  if (!EncodingUtils::FindEncodingForLabelNoReplacement(label, encoding)) {
+  auto encoding = Encoding::ForLabelNoReplacement(es);
+  if (!encoding) {
     return ccs;
   }
-  encoder = EncodingUtils::EncoderForEncoding(encoding);
-  decoder = EncodingUtils::DecoderForEncoding(encoding);
-  encoder->SetOutputErrorBehavior(encoder->kOnError_Signal, nullptr, '?');
-  decoder->SetInputErrorBehavior(decoder->kOnError_Signal);
+  auto encoder = encoding->NewEncoder();
+  auto decoder = encoding->NewDecoderWithoutBOMHandling();
 
   for (unsigned int i = 0; i <= 0xff; ++i) {
     bool success = false;
     // We want to find the upper/lowercase equivalents of each byte
     // in this 1-byte character encoding.  Call our encoding/decoding
     // APIs separately for each byte since they may reject some of the
     // bytes, and we want to handle errors separately for each byte.
-    char lower, upper;
+    uint8_t lower, upper;
     do {
       if (i == 0)
         break;
-      const char source = char(i);
-      char16_t uni, uniCased;
-      int32_t charLength = 1, uniLength = 1;
+      uint8_t source = uint8_t(i);
+      char16_t uni[2];
+      char16_t uniCased;
+      uint8_t destination[4];
+      auto src1 = MakeSpan(&source, 1);
+      auto dst1 = MakeSpan(uni);
+      auto src2 = MakeSpan(&uniCased, 1);
+      auto dst2 = MakeSpan(destination);
 
-      rv = decoder->Convert(&source, &charLength, &uni, &uniLength);
-      // Explicitly check NS_OK because we don't want to allow
-      // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT.
-      if (rv != NS_OK || charLength != 1 || uniLength != 1)
+      uint32_t result;
+      size_t read;
+      size_t written;
+      Tie(result, read, written) =
+        decoder->DecodeToUTF16WithoutReplacement(src1, dst1, true);
+      if (result != kInputEmpty || read != 1 || written != 1) {
         break;
-      uniCased = ToLowerCase(uni);
-      rv = encoder->Convert(&uniCased, &uniLength, &lower, &charLength);
-      // Explicitly check NS_OK because we don't want to allow
-      // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT.
-      if (rv != NS_OK || charLength != 1 || uniLength != 1)
-        break;
+      }
 
-      uniCased = ToUpperCase(uni);
-      rv = encoder->Convert(&uniCased, &uniLength, &upper, &charLength);
-      // Explicitly check NS_OK because we don't want to allow
-      // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT.
-      if (rv != NS_OK || charLength != 1 || uniLength != 1)
+      uniCased = ToLowerCase(uni[0]);
+      Tie(result, read, written) =
+        encoder->EncodeFromUTF16WithoutReplacement(src2, dst2, true);
+      if (result != kInputEmpty || read != 1 || written != 1) {
         break;
+      }
+      lower = destination[0];
+
+      uniCased = ToUpperCase(uni[0]);
+      Tie(result, read, written) =
+        encoder->EncodeFromUTF16WithoutReplacement(src2, dst2, true);
+      if (result != kInputEmpty || read != 1 || written != 1) {
+        break;
+      }
+      upper = destination[0];
 
       success = true;
     } while (0);
 
+    encoding->NewEncoderInto(*encoder);
+    encoding->NewDecoderWithoutBOMHandlingInto(*decoder);
+
     if (success) {
       ccs[i].cupper = upper;
       ccs[i].clower = lower;
     } else {
       ccs[i].cupper = i;
       ccs[i].clower = i;
     }
 
--- a/extensions/spellcheck/src/mozEnglishWordUtils.h
+++ b/extensions/spellcheck/src/mozEnglishWordUtils.h
@@ -3,35 +3,37 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozEnglishWordUtils_h__
 #define mozEnglishWordUtils_h__
 
 #include "nsCOMPtr.h"
 #include "mozISpellI18NUtil.h"
-#include "nsIUnicodeEncoder.h"
-#include "nsIUnicodeDecoder.h"
 #include "nsString.h"
 
-#include "mozITXTToHTMLConv.h" 
+#include "mozITXTToHTMLConv.h"
 #include "nsCycleCollectionParticipant.h"
 
 class mozEnglishWordUtils : public mozISpellI18NUtil
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_MOZISPELLI18NUTIL
   NS_DECL_CYCLE_COLLECTION_CLASS(mozEnglishWordUtils)
 
   mozEnglishWordUtils();
   /* additional members */
-  enum myspCapitalization{
-    NoCap,InitCap,AllCap,HuhCap
-  };  
+  enum myspCapitalization
+  {
+    NoCap,
+    InitCap,
+    AllCap,
+    HuhCap
+  };
 
 protected:
   virtual ~mozEnglishWordUtils();
 
   mozEnglishWordUtils::myspCapitalization captype(const nsString &word);
   bool ucIsAlpha(char16_t aChar);
 
   nsString mLanguage;
--- a/extensions/spellcheck/src/mozPersonalDictionary.cpp
+++ b/extensions/spellcheck/src/mozPersonalDictionary.cpp
@@ -35,42 +35,39 @@
  *  and probably should, but I don't see much need for more in terms of interface.
  *
  * Allowing personal words to be associated with only certain dictionaries maybe.
  *
  * TODO:
  * Implement the suggestion record.
  */
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(mozPersonalDictionary)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(mozPersonalDictionary)
+NS_IMPL_ADDREF(mozPersonalDictionary)
+NS_IMPL_RELEASE(mozPersonalDictionary)
 
 NS_INTERFACE_MAP_BEGIN(mozPersonalDictionary)
   NS_INTERFACE_MAP_ENTRY(mozIPersonalDictionary)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIPersonalDictionary)
-  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozPersonalDictionary)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTION(mozPersonalDictionary, mEncoder)
-
 class mozPersonalDictionaryLoader final : public mozilla::Runnable
 {
 public:
   explicit mozPersonalDictionaryLoader(mozPersonalDictionary *dict) : mDict(dict)
   {
   }
 
   NS_IMETHOD Run() override
   {
     mDict->SyncLoad();
 
     // Release the dictionary on the main thread
-    NS_ReleaseOnMainThread(mDict.forget());
+    NS_ReleaseOnMainThread(mDict.forget().downcast<mozIPersonalDictionary>());
 
     return NS_OK;
   }
 
 private:
   RefPtr<mozPersonalDictionary> mDict;
 };
 
@@ -131,17 +128,17 @@ public:
       mDict->mSavePending = false;
       mon.Notify();
 
       // Leaving the block where 'mon' was declared will call the destructor
       // and unlock.
     }
 
     // Release the dictionary on the main thread.
-    NS_ReleaseOnMainThread(mDict.forget());
+    NS_ReleaseOnMainThread(mDict.forget().downcast<mozIPersonalDictionary>());
 
     return NS_OK;
   }
 
 private:
   nsTArray<nsString> mDictWords;
   nsCOMPtr<nsIFile> mFile;
   RefPtr<mozPersonalDictionary> mDict;
--- a/extensions/spellcheck/src/mozPersonalDictionary.h
+++ b/extensions/spellcheck/src/mozPersonalDictionary.h
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozPersonalDictionary_h__
 #define mozPersonalDictionary_h__
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "mozIPersonalDictionary.h"
-#include "nsIUnicodeEncoder.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "nsTHashtable.h"
 #include "nsCRT.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsHashKeys.h"
 #include <mozilla/Monitor.h>
 
@@ -27,20 +26,19 @@ 0X7EF52EAF, 0XB7E1, 0X462B, \
 class mozPersonalDictionaryLoader;
 class mozPersonalDictionarySave;
 
 class mozPersonalDictionary final : public mozIPersonalDictionary,
                                     public nsIObserver,
                                     public nsSupportsWeakReference
 {
 public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_ISUPPORTS
   NS_DECL_MOZIPERSONALDICTIONARY
   NS_DECL_NSIOBSERVER
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozPersonalDictionary, mozIPersonalDictionary)
 
   mozPersonalDictionary();
 
   nsresult Init();
 
 protected:
   virtual ~mozPersonalDictionary();
 
@@ -51,19 +49,16 @@ protected:
   bool mSavePending;
 
   nsCOMPtr<nsIFile> mFile;
   mozilla::Monitor mMonitor;
   mozilla::Monitor mMonitorSave;
   nsTHashtable<nsUnicharPtrHashKey> mDictionaryTable;
   nsTHashtable<nsUnicharPtrHashKey> mIgnoreTable;
 
-  /*Encoder to use to compare with spellchecker word */
-  nsCOMPtr<nsIUnicodeEncoder>  mEncoder;
-
 private:
   /* wait for the asynchronous load of the dictionary to be completed */
   void WaitForLoad();
 
   /* enter the monitor before starting a synchronous load off the main-thread */
   void SyncLoad();
 
   /* launch an asynchrounous load of the dictionary from the main-thread
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -13,17 +13,17 @@
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/Sprintf.h"
 
 #include "nsCOMPtr.h"
 #include "nsIUUIDGenerator.h"
-#include "nsIUnicodeDecoder.h"
+#include "mozilla/Encoding.h"
 
 #include "harfbuzz/hb.h"
 
 #include "plbase64.h"
 #include "mozilla/Logging.h"
 
 #ifdef XP_MACOSX
 #include <CoreFoundation/CoreFoundation.h>
@@ -1433,16 +1433,26 @@ gfxFontUtils::GetCharsetForFontName(uint
             return gMSFontNameCharsets[aScript];
         }
         break;
     }
 
     return nullptr;
 }
 
+template<int N>
+static bool
+StartsWith(const nsACString& string, const char (&prefix)[N])
+{
+  if (N - 1 > string.Length()) {
+    return false;
+  }
+  return memcmp(string.Data(), prefix, N - 1) == 0;
+}
+
 // convert a raw name from the name table to an nsString, if possible;
 // return value indicates whether conversion succeeded
 bool
 gfxFontUtils::DecodeFontName(const char *aNameData, int32_t aByteLen, 
                              uint32_t aPlatformCode, uint32_t aScriptCode,
                              uint32_t aLangCode, nsAString& aName)
 {
     if (aByteLen <= 0) {
@@ -1474,22 +1484,22 @@ gfxFontUtils::DecodeFontName(const char 
         CopySwapUTF16(aNameData, reinterpret_cast<char*>(aName.BeginWriting()),
                       strLen);
 #else
         memcpy(aName.BeginWriting(), aNameData, strLen * 2);
 #endif
         return true;
     }
 
-    nsCOMPtr<nsIUnicodeDecoder> decoder =
-        mozilla::dom::EncodingUtils::DecoderForEncoding(csName);
-    if (!decoder) {
+    nsDependentCString encodingName(csName);
+    if (StartsWith(encodingName, "x-mac-") &&
+        !encodingName.EqualsLiteral("x-mac-cyrillic")) {
 #ifdef XP_MACOSX
         // Special case for macOS only: support legacy Mac encodings
-        // that DecoderForEncoding didn't handle.
+        // that aren't part of the Encoding Standard.
         if (aPlatformCode == PLATFORM_ID_MAC) {
             CFStringRef str =
                 CFStringCreateWithBytes(kCFAllocatorDefault,
                                         (const UInt8*)aNameData, aByteLen,
                                         aScriptCode, false);
             if (str) {
                 CFIndex length = CFStringGetLength(str);
                 aName.SetLength(length);
@@ -1499,34 +1509,20 @@ gfxFontUtils::DecodeFontName(const char 
                 return true;
             }
         }
 #endif
         NS_WARNING("failed to get the decoder for a font name string");
         return false;
     }
 
-    int32_t destLength;
-    nsresult rv = decoder->GetMaxLength(aNameData, aByteLen, &destLength);
-    if (NS_FAILED(rv)) {
-        NS_WARNING("decoder->GetMaxLength failed, invalid font name?");
-        return false;
-    }
-
-    // make space for the converted string
-    aName.SetLength(destLength);
-    rv = decoder->Convert(aNameData, &aByteLen,
-                          aName.BeginWriting(), &destLength);
-    if (NS_FAILED(rv)) {
-        NS_WARNING("decoder->Convert failed, invalid font name?");
-        return false;
-    }
-    aName.Truncate(destLength); // set the actual length
-
-    return true;
+    auto encoding = Encoding::ForName(encodingName);
+    auto rv = encoding->DecodeWithoutBOMHandling(
+      AsBytes(MakeSpan(aNameData, aByteLen)), aName);
+    return NS_SUCCEEDED(rv);
 }
 
 nsresult
 gfxFontUtils::ReadNames(const char *aNameData, uint32_t aDataLen,
                         uint32_t aNameID,
                         int32_t aLangID, int32_t aPlatformID,
                         nsTArray<nsString>& aNames)
 {
new file mode 100644
--- /dev/null
+++ b/intl/Encoding.h
@@ -0,0 +1,1390 @@
+// Copyright 2015-2016 Mozilla Foundation. See the COPYRIGHT
+// file at the top-level directory of this distribution.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Adapted from third_party/rust/encoding_c/include/encoding_rs_cpp.h, so the
+// "top-level directory" in the above notice refers to
+// third_party/rust/encoding_c/.
+
+#ifndef mozilla_Encoding_h
+#define mozilla_Encoding_h
+
+#include "mozilla/CheckedInt.h"
+#include "mozilla/NotNull.h"
+#include "mozilla/Span.h"
+#include "mozilla/Tuple.h"
+#include "nsString.h"
+
+namespace mozilla {
+class Encoding;
+class Decoder;
+class Encoder;
+}; // namespace mozilla
+
+#define ENCODING_RS_ENCODING mozilla::Encoding
+#define ENCODING_RS_ENCODER mozilla::Encoder
+#define ENCODING_RS_DECODER mozilla::Decoder
+
+#include "encoding_rs.h"
+
+extern "C" {
+
+nsresult
+mozilla_encoding_decode_to_nsstring(mozilla::Encoding const** encoding,
+                                    uint8_t const* src,
+                                    size_t src_len,
+                                    nsAString* dst);
+
+nsresult
+mozilla_encoding_decode_to_nsstring_with_bom_removal(
+  mozilla::Encoding const* encoding,
+  uint8_t const* src,
+  size_t src_len,
+  nsAString* dst);
+
+nsresult
+mozilla_encoding_decode_to_nsstring_without_bom_handling(
+  mozilla::Encoding const* encoding,
+  uint8_t const* src,
+  size_t src_len,
+  nsAString* dst);
+
+nsresult
+mozilla_encoding_decode_to_nsstring_without_bom_handling_and_without_replacement(
+  mozilla::Encoding const* encoding,
+  uint8_t const* src,
+  size_t src_len,
+  nsAString* dst);
+
+nsresult
+mozilla_encoding_encode_from_utf16(mozilla::Encoding const** encoding,
+                                   char16_t const* src,
+                                   size_t src_len,
+                                   nsACString* dst);
+
+nsresult
+mozilla_encoding_decode_to_nscstring(mozilla::Encoding const** encoding,
+                                     nsACString const* src,
+                                     nsACString* dst);
+
+nsresult
+mozilla_encoding_decode_to_nscstring_with_bom_removal(
+  mozilla::Encoding const* encoding,
+  nsACString const* src,
+  nsACString* dst);
+
+nsresult
+mozilla_encoding_decode_to_nscstring_without_bom_handling(
+  mozilla::Encoding const* encoding,
+  nsACString const* src,
+  nsACString* dst);
+
+nsresult
+mozilla_encoding_decode_to_nscstring_without_bom_handling_and_without_replacement(
+  mozilla::Encoding const* encoding,
+  nsACString const* src,
+  nsACString* dst);
+
+nsresult
+mozilla_encoding_encode_from_nscstring(mozilla::Encoding const** encoding,
+                                       nsACString const* src,
+                                       nsACString* dst);
+
+} // extern "C"
+
+namespace mozilla {
+
+/**
+ * Return value from `Decoder`/`Encoder` to indicate that input
+ * was exhausted.
+ */
+const uint32_t kInputEmpty = INPUT_EMPTY;
+
+/**
+ * Return value from `Decoder`/`Encoder` to indicate that output
+ * space was insufficient.
+ */
+const uint32_t kOutputFull = OUTPUT_FULL;
+
+/**
+ * An encoding as defined in the Encoding Standard
+ * (https://encoding.spec.whatwg.org/).
+ *
+ * See https://docs.rs/encoding_rs/ for the Rust API docs.
+ *
+ * An _encoding_ defines a mapping from a byte sequence to a Unicode code point
+ * sequence and, in most cases, vice versa. Each encoding has a name, an output
+ * encoding, and one or more labels.
+ *
+ * _Labels_ are ASCII-case-insensitive strings that are used to identify an
+ * encoding in formats and protocols. The _name_ of the encoding is the
+ * preferred label in the case appropriate for returning from the
+ * `characterSet` property of the `Document` DOM interface, except for
+ * the replacement encoding whose name is not one of its labels.
+ *
+ * The _output encoding_ is the encoding used for form submission and URL
+ * parsing on Web pages in the encoding. This is UTF-8 for the replacement,
+ * UTF-16LE and UTF-16BE encodings and the encoding itself for other
+ * encodings.
+ *
+ * # Streaming vs. Non-Streaming
+ *
+ * When you have the entire input in a single buffer, you can use the
+ * methods `Decode()`, `DecodeWithBOMRemoval()`,
+ * `DecodeWithoutBOMHandling()`,
+ * `DecodeWithoutBOMHandlingAndWithoutReplacement()` and
+ * `Encode()`. Unlike the rest of the API (apart from the `NewDecoder()` and
+ * NewEncoder()` methods), these methods perform heap allocations. You should
+ * the `Decoder` and `Encoder` objects when your input is split into multiple
+ * buffers or when you want to control the allocation of the output buffers.
+ *
+ * # Instances
+ *
+ * All instances of `Encoding` are statically allocated and have the process's
+ * lifetime. There is precisely one unique `Encoding` instance for each
+ * encoding defined in the Encoding Standard.
+ *
+ * To obtain a reference to a particular encoding whose identity you know at
+ * compile time, use a `static` that refers to encoding. There is a `static`
+ * for each encoding. The `static`s are named in all caps with hyphens
+ * replaced with underscores and with `_ENCODING` appended to the
+ * name. For example, if you know at compile time that you will want to
+ * decode using the UTF-8 encoding, use the `UTF_8_ENCODING` `static`.
+ *
+ * If you don't know what encoding you need at compile time and need to
+ * dynamically get an encoding by label, use `Encoding::for_label()`.
+ *
+ * Pointers to `Encoding` can be compared with `==` to check for the sameness
+ * of two encodings.
+ *
+ * A pointer to a `mozilla::Encoding` in C++ is the same thing as a pointer
+ * to an `encoding_rs::Encoding` in Rust. When writing FFI code, use
+ * `const mozilla::Encoding*` in the C signature and
+ * `*const encoding_rs::Encoding` is the corresponding Rust signature.
+ */
+class Encoding final
+{
+public:
+  /**
+   * Implements the _get an encoding_ algorithm
+   * (https://encoding.spec.whatwg.org/#concept-encoding-get).
+   *
+   * If, after ASCII-lowercasing and removing leading and trailing
+   * whitespace, the argument matches a label defined in the Encoding
+   * Standard, `const Encoding*` representing the corresponding
+   * encoding is returned. If there is no match, `nullptr` is returned.
+   *
+   * This is the right method to use if the action upon the method returning
+   * `nullptr` is to use a fallback encoding (e.g. `WINDOWS_1252_ENCODING`)
+   * instead. When the action upon the method returning `nullptr` is not to
+   * proceed with a fallback but to refuse processing,
+   * `ForLabelNoReplacement()` is more appropriate.
+  */
+  static inline const Encoding* ForLabel(Span<const char> aLabel)
+  {
+    return encoding_for_label(
+      reinterpret_cast<const uint8_t*>(aLabel.Elements()), aLabel.Length());
+  }
+
+  /**
+   * `nsAString` argument version. See above for docs.
+   */
+  static inline const Encoding* ForLabel(const nsAString& aLabel)
+  {
+    return Encoding::ForLabel(NS_ConvertUTF16toUTF8(aLabel));
+  }
+
+  /**
+   * This method behaves the same as `ForLabel()`, except when `ForLabel()`
+   * would return `REPLACEMENT_ENCODING`, this method returns `nullptr` instead.
+   *
+   * This method is useful in scenarios where a fatal error is required
+   * upon invalid label, because in those cases the caller typically wishes
+   * to treat the labels that map to the replacement encoding as fatal
+   * errors, too.
+   *
+   * It is not OK to use this method when the action upon the method returning
+   * `nullptr` is to use a fallback encoding (e.g. `WINDOWS_1252_ENCODING`). In
+   * such a case, the `ForLabel()` method should be used instead in order to avoid
+   * unsafe fallback for labels that `ForLabel()` maps to `REPLACEMENT_ENCODING`.
+   */
+  static inline const Encoding* ForLabelNoReplacement(Span<const char> aLabel)
+  {
+    return encoding_for_label_no_replacement(
+      reinterpret_cast<const uint8_t*>(aLabel.Elements()), aLabel.Length());
+  }
+
+  /**
+   * `nsAString` argument version. See above for docs.
+   */
+  static inline const Encoding* ForLabelNoReplacement(const nsAString& aLabel)
+  {
+    return Encoding::ForLabelNoReplacement(NS_ConvertUTF16toUTF8(aLabel));
+  }
+
+  /**
+   * Performs non-incremental BOM sniffing.
+   *
+   * The argument must either be a buffer representing the entire input
+   * stream (non-streaming case) or a buffer representing at least the first
+   * three bytes of the input stream (streaming case).
+   *
+   * Returns `MakeTuple(UTF_8_ENCODING, 3)`, `MakeTuple(UTF_16LE_ENCODING, 2)`
+   * or `MakeTuple(UTF_16BE_ENCODING, 3)` if the argument starts with the
+   * UTF-8, UTF-16LE or UTF-16BE BOM or `MakeTuple(nullptr, 0)` otherwise.
+   */
+  static inline Tuple<const Encoding*, size_t> ForBOM(
+    Span<const uint8_t> aBuffer)
+  {
+    size_t len = aBuffer.Length();
+    const Encoding* encoding = encoding_for_bom(aBuffer.Elements(), &len);
+    return MakeTuple(encoding, len);
+  }
+
+  /**
+   * If the argument matches exactly (case-sensitively; no whitespace
+   * removal performed) the name of an encoding, returns
+   * `const Encoding*` representing that encoding. Otherwise `MOZ_CRASH`es.
+   *
+   * The motivating use case for this method is interoperability with
+   * legacy Gecko code that represents encodings as name string instead of
+   * type-safe `Encoding` objects. Using this method for other purposes is
+   * most likely the wrong thing to do.
+   */
+  static inline NotNull<const mozilla::Encoding*> ForName(
+    Span<const char> aName)
+  {
+    return WrapNotNull(encoding_for_name(
+      reinterpret_cast<const uint8_t*>(aName.Elements()), aName.Length()));
+  }
+
+  /**
+   * Writes the name of this encoding into `aName`.
+   *
+   * This name is appropriate to return as-is from the DOM
+   * `document.characterSet` property.
+   */
+  inline void Name(nsACString& aName) const
+  {
+    aName.SetLength(ENCODING_NAME_MAX_LENGTH);
+    size_t length =
+      encoding_name(this, reinterpret_cast<uint8_t*>(aName.BeginWriting()));
+    aName.SetLength(length); // truncation is the 64-bit case is OK
+  }
+
+  /**
+   * Checks whether the _output encoding_ of this encoding can encode every
+   * Unicode code point. (Only true if the output encoding is UTF-8.)
+   */
+  inline bool CanEncodeEverything() const
+  {
+    return encoding_can_encode_everything(this);
+  }
+
+  /**
+   * Checks whether the bytes 0x00...0x7F map exclusively to the characters
+   * U+0000...U+007F and vice versa.
+   */
+  inline bool IsAsciiCompatible() const
+  {
+    return encoding_is_ascii_compatible(this);
+  }
+
+  /**
+   * Returns the _output encoding_ of this encoding. This is UTF-8 for
+   * UTF-16BE, UTF-16LE and replacement and the encoding itself otherwise.
+   */
+  inline NotNull<const mozilla::Encoding*> OutputEncoding() const
+  {
+    return WrapNotNull(encoding_output_encoding(this));
+  }
+
+  /**
+   * Decode complete input to `nsACString` _with BOM sniffing_ and with
+   * malformed sequences replaced with the REPLACEMENT CHARACTER when the
+   * entire input is available as a single buffer (i.e. the end of the
+   * buffer marks the end of the stream).
+   *
+   * This method implements the (non-streaming version of) the
+   * _decode_ (https://encoding.spec.whatwg.org/#decode) spec concept.
+   *
+   * The second item in the returned tuple is the encoding that was actually
+   * used (which may differ from this encoding thanks to BOM sniffing).
+   *
+   * Returns `NS_ERROR_OUT_OF_MEMORY` upon OOM, `NS_OK_HAD_REPLACEMENTS`
+   * if there were malformed sequences (that were replaced with the
+   * REPLACEMENT CHARACTER) and `NS_OK` otherwise as the first item of the
+   * tuple.
+   *
+   * The backing buffer of the string isn't copied if the input buffer
+   * is heap-allocated and decoding from UTF-8 and the input is valid
+   * BOMless UTF-8, decoding from an ASCII-compatible encoding and
+   * the input is valid ASCII or decoding from ISO-2022-JP and the
+   * input stays in the ASCII state of ISO-2022-JP. It is OK to pass
+   * the same string as both arguments.
+   *
+   * _Note:_ It is wrong to use this when the input buffer represents only
+   * a segment of the input instead of the whole input. Use `NewDecoder()`
+   * when decoding segmented input.
+   */
+  inline Tuple<nsresult, NotNull<const mozilla::Encoding*>> Decode(
+    const nsACString& aBytes,
+    nsACString& aOut) const
+  {
+    const Encoding* encoding = this;
+    const nsACString* bytes = &aBytes;
+    nsACString* out = &aOut;
+    nsresult rv;
+    if (bytes == out) {
+      nsAutoCString temp(aBytes);
+      rv = mozilla_encoding_decode_to_nscstring(&encoding, &temp, out);
+    } else {
+      rv = mozilla_encoding_decode_to_nscstring(&encoding, bytes, out);
+    }
+    return MakeTuple(rv, WrapNotNull(encoding));
+  }
+
+  /**
+   * Decode complete input to `nsAString` _with BOM sniffing_ and with
+   * malformed sequences replaced with the REPLACEMENT CHARACTER when the
+   * entire input is available as a single buffer (i.e. the end of the
+   * buffer marks the end of the stream).
+   *
+   * This method implements the (non-streaming version of) the
+   * _decode_ (https://encoding.spec.whatwg.org/#decode) spec concept.
+   *
+   * The second item in the returned tuple is the encoding that was actually
+   * used (which may differ from this encoding thanks to BOM sniffing).
+   *
+   * Returns `NS_ERROR_OUT_OF_MEMORY` upon OOM, `NS_OK_HAD_REPLACEMENTS`
+   * if there were malformed sequences (that were replaced with the
+   * REPLACEMENT CHARACTER) and `NS_OK` otherwise as the first item of the
+   * tuple.
+   *
+   * _Note:_ It is wrong to use this when the input buffer represents only
+   * a segment of the input instead of the whole input. Use `NewDecoder()`
+   * when decoding segmented input.
+   */
+  inline Tuple<nsresult, NotNull<const mozilla::Encoding*>> Decode(
+    Span<const uint8_t> aBytes,
+    nsAString& aOut) const
+  {
+    const Encoding* encoding = this;
+    nsresult rv = mozilla_encoding_decode_to_nsstring(
+      &encoding, aBytes.Elements(), aBytes.Length(), &aOut);
+    return MakeTuple(rv, WrapNotNull(encoding));
+  }
+
+  /**
+   * Decode complete input to `nsACString` _with BOM removal_ and with
+   * malformed sequences replaced with the REPLACEMENT CHARACTER when the
+   * entire input is available as a single buffer (i.e. the end of the
+   * buffer marks the end of the stream).
+   *
+   * When invoked on `UTF_8`, this method implements the (non-streaming
+   * version of) the _UTF-8 decode_
+   * (https://encoding.spec.whatwg.org/#utf-8-decode) spec concept.
+   *
+   * Returns `NS_ERROR_OUT_OF_MEMORY` upon OOM, `NS_OK_HAD_REPLACEMENTS`
+   * if there were malformed sequences (that were replaced with the
+   * REPLACEMENT CHARACTER) and `NS_OK` otherwise.
+   *
+   * The backing buffer of the string isn't copied if the input buffer
+   * is heap-allocated and decoding from UTF-8 and the input is valid
+   * BOMless UTF-8, decoding from an ASCII-compatible encoding and
+   * the input is valid ASCII or decoding from ISO-2022-JP and the
+   * input stays in the ASCII state of ISO-2022-JP. It is OK to pass
+   * the same string as both arguments.
+   *
+   * _Note:_ It is wrong to use this when the input buffer represents only
+   * a segment of the input instead of the whole input. Use
+   * `NewDecoderWithBOMRemoval()` when decoding segmented input.
+   */
+  inline nsresult DecodeWithBOMRemoval(const nsACString& aBytes,
+                                       nsACString& aOut) const
+  {
+    const nsACString* bytes = &aBytes;
+    nsACString* out = &aOut;
+    if (bytes == out) {
+      nsAutoCString temp(aBytes);
+      return mozilla_encoding_decode_to_nscstring_with_bom_removal(
+        this, &temp, out);
+    }
+    return mozilla_encoding_decode_to_nscstring_with_bom_removal(
+      this, bytes, out);
+  }
+
+  /**
+   * Decode complete input to `nsAString` _with BOM removal_ and with
+   * malformed sequences replaced with the REPLACEMENT CHARACTER when the
+   * entire input is available as a single buffer (i.e. the end of the
+   * buffer marks the end of the stream).
+   *
+   * When invoked on `UTF_8`, this method implements the (non-streaming
+   * version of) the _UTF-8 decode_
+   * (https://encoding.spec.whatwg.org/#utf-8-decode) spec concept.
+   *
+   * Returns `NS_ERROR_OUT_OF_MEMORY` upon OOM, `NS_OK_HAD_REPLACEMENTS`
+   * if there were malformed sequences (that were replaced with the
+   * REPLACEMENT CHARACTER) and `NS_OK` otherwise.
+   *
+   * _Note:_ It is wrong to use this when the input buffer represents only
+   * a segment of the input instead of the whole input. Use
+   * `NewDecoderWithBOMRemoval()` when decoding segmented input.
+   */
+  inline nsresult DecodeWithBOMRemoval(Span<const uint8_t> aBytes,
+                                       nsAString& aOut) const
+  {
+    return mozilla_encoding_decode_to_nsstring_with_bom_removal(
+      this, aBytes.Elements(), aBytes.Length(), &aOut);
+  }
+
+  /**
+   * Decode complete input to `nsACString` _without BOM handling_ and
+   * with malformed sequences replaced with the REPLACEMENT CHARACTER when
+   * the entire input is available as a single buffer (i.e. the end of the
+   * buffer marks the end of the stream).
+   *
+   * When invoked on `UTF_8`, this method implements the (non-streaming
+   * version of) the _UTF-8 decode without BOM_
+   * (https://encoding.spec.whatwg.org/#utf-8-decode-without-bom) spec concept.
+   *
+   * Returns `NS_ERROR_OUT_OF_MEMORY` upon OOM, `NS_OK_HAD_REPLACEMENTS`
+   * if there were malformed sequences (that were replaced with the
+   * REPLACEMENT CHARACTER) and `NS_OK` otherwise.
+   *
+   * The backing buffer of the string isn't copied if the input buffer
+   * is heap-allocated and decoding from UTF-8 and the input is valid
+   * UTF-8, decoding from an ASCII-compatible encoding and the input
+   * is valid ASCII or decoding from ISO-2022-JP and the input stays
+   * in the ASCII state of ISO-2022-JP. It is OK to pass the same string
+   * as both arguments.
+   *
+   * _Note:_ It is wrong to use this when the input buffer represents only
+   * a segment of the input instead of the whole input. Use
+   * `NewDecoderWithoutBOMHandling()` when decoding segmented input.
+   */
+  inline nsresult DecodeWithoutBOMHandling(const nsACString& aBytes,
+                                           nsACString& aOut) const
+  {
+    const nsACString* bytes = &aBytes;
+    nsACString* out = &aOut;
+    if (bytes == out) {
+      nsAutoCString temp(aBytes);
+      return mozilla_encoding_decode_to_nscstring_without_bom_handling(
+        this, &temp, out);
+    }
+    return mozilla_encoding_decode_to_nscstring_without_bom_handling(
+      this, bytes, out);
+  }
+
+  /**
+   * Decode complete input to `nsAString` _without BOM handling_ and
+   * with malformed sequences replaced with the REPLACEMENT CHARACTER when
+   * the entire input is available as a single buffer (i.e. the end of the
+   * buffer marks the end of the stream).
+   *
+   * When invoked on `UTF_8`, this method implements the (non-streaming
+   * version of) the _UTF-8 decode without BOM_
+   * (https://encoding.spec.whatwg.org/#utf-8-decode-without-bom) spec concept.
+   *
+   * Returns `NS_ERROR_OUT_OF_MEMORY` upon OOM, `NS_OK_HAD_REPLACEMENTS`
+   * if there were malformed sequences (that were replaced with the
+   * REPLACEMENT CHARACTER) and `NS_OK` otherwise.
+   *
+   * _Note:_ It is wrong to use this when the input buffer represents only
+   * a segment of the input instead of the whole input. Use
+   * `NewDecoderWithoutBOMHandling()` when decoding segmented input.
+   */
+  inline nsresult DecodeWithoutBOMHandling(Span<const uint8_t> aBytes,
+                                           nsAString& aOut) const
+  {
+    return mozilla_encoding_decode_to_nsstring_without_bom_handling(
+      this, aBytes.Elements(), aBytes.Length(), &aOut);
+  }
+
+  /**
+   * Decode complete input to `nsACString` _without BOM handling_ and
+   * _with malformed sequences treated as fatal_ when the entire input is
+   * available as a single buffer (i.e. the end of the buffer marks the end
+   * of the stream).
+   *
+   * When invoked on `UTF_8`, this method implements the (non-streaming
+   * version of) the _UTF-8 decode without BOM or fail_
+   * (https://encoding.spec.whatwg.org/#utf-8-decode-without-bom-or-fail)
+   * spec concept.
+   *
+   * Returns `NS_ERROR_OUT_OF_MEMORY` upon OOM, `NS_ERROR_UDEC_ILLEGALINPUT`
+   * if a malformed sequence was encountered and `NS_OK` otherwise.
+   *
+   * The backing buffer of the string isn't copied if the input buffer
+   * is heap-allocated and decoding from UTF-8 and the input is valid
+   * UTF-8, decoding from an ASCII-compatible encoding and the input
+   * is valid ASCII or decoding from ISO-2022-JP and the input stays
+   * in the ASCII state of ISO-2022-JP. It is OK to pass the same string
+   * as both arguments.
+   *
+   * _Note:_ It is wrong to use this when the input buffer represents only
+   * a segment of the input instead of the whole input. Use
+   * `NewDecoderWithoutBOMHandling()` when decoding segmented input.
+   */
+  inline nsresult DecodeWithoutBOMHandlingAndWithoutReplacement(
+    const nsACString& aBytes,
+    nsACString& aOut) const
+  {
+    const nsACString* bytes = &aBytes;
+    nsACString* out = &aOut;
+    if (bytes == out) {
+      nsAutoCString temp(aBytes);
+      return mozilla_encoding_decode_to_nscstring_without_bom_handling_and_without_replacement(
+        this, &temp, out);
+    }
+    return mozilla_encoding_decode_to_nscstring_without_bom_handling_and_without_replacement(
+      this, bytes, out);
+  }
+
+  /**
+   * Decode complete input to `nsAString` _without BOM handling_ and
+   * _with malformed sequences treated as fatal_ when the entire input is
+   * available as a single buffer (i.e. the end of the buffer marks the end
+   * of the stream).
+   *
+   * When invoked on `UTF_8`, this method implements the (non-streaming
+   * version of) the _UTF-8 decode without BOM or fail_
+   * (https://encoding.spec.whatwg.org/#utf-8-decode-without-bom-or-fail)
+   * spec concept.
+   *
+   * Returns `NS_ERROR_OUT_OF_MEMORY` upon OOM, `NS_ERROR_UDEC_ILLEGALINPUT`
+   * if a malformed sequence was encountered and `NS_OK` otherwise.
+   *
+   * _Note:_ It is wrong to use this when the input buffer represents only
+   * a segment of the input instead of the whole input. Use
+   * `NewDecoderWithoutBOMHandling()` when decoding segmented input.
+   */
+  inline nsresult DecodeWithoutBOMHandlingAndWithoutReplacement(
+    Span<const uint8_t> aBytes,
+    nsAString& aOut) const
+  {
+    return mozilla_encoding_decode_to_nsstring_without_bom_handling_and_without_replacement(
+      this, aBytes.Elements(), aBytes.Length(), &aOut);
+  }
+
+  /**
+   * Encode complete input to `nsACString` with unmappable characters
+   * replaced with decimal numeric character references when the entire input
+   * is available as a single buffer (i.e. the end of the buffer marks the
+   * end of the stream).
+   *
+   * This method implements the (non-streaming version of) the
+   * _encode_ (https://encoding.spec.whatwg.org/#encode) spec concept.
+   *
+   * The second item in the returned tuple is the encoding that was actually
+   * used (which may differ from this encoding thanks to some encodings
+   * having UTF-8 as their output encoding).
+   *
+   * The first item of the returned tuple is `NS_ERROR_UDEC_ILLEGALINPUT` if
+   * the input is not valid UTF-8, `NS_ERROR_OUT_OF_MEMORY` upon OOM,
+   * `NS_OK_HAD_REPLACEMENTS` if there were unmappable code points (that were
+   * replaced with numeric character references) and `NS_OK` otherwise.
+   *
+   * The backing buffer of the string isn't copied if the input buffer
+   * is heap-allocated and encoding to UTF-8 and the input is valid
+   * UTF-8, encoding to an ASCII-compatible encoding and the input
+   * is valid ASCII or encoding from ISO-2022-JP and the input stays
+   * in the ASCII state of ISO-2022-JP. It is OK to pass the same string
+   * as both arguments.
+   *
+   * _Note:_ It is wrong to use this when the input buffer represents only
+   * a segment of the input instead of the whole input. Use `NewEncoder()`
+   * when encoding segmented output.
+   */
+  inline Tuple<nsresult, NotNull<const mozilla::Encoding*>> Encode(
+    const nsACString& aString,
+    nsACString& aOut) const
+  {
+    const Encoding* encoding = this;
+    const nsACString* string = &aString;
+    nsACString* out = &aOut;
+    nsresult rv;
+    if (string == out) {
+      nsAutoCString temp(aString);
+      rv = mozilla_encoding_encode_from_nscstring(&encoding, &temp, out);
+    } else {
+      rv = mozilla_encoding_encode_from_nscstring(&encoding, string, out);
+    }
+    return MakeTuple(rv, WrapNotNull(encoding));
+  }
+
+  /**
+   * Encode complete input to `nsACString` with unmappable characters
+   * replaced with decimal numeric character references when the entire input
+   * is available as a single buffer (i.e. the end of the buffer marks the
+   * end of the stream).
+   *
+   * This method implements the (non-streaming version of) the
+   * _encode_ (https://encoding.spec.whatwg.org/#encode) spec concept.
+   *
+   * The second item in the returned tuple is the encoding that was actually
+   * used (which may differ from this encoding thanks to some encodings
+   * having UTF-8 as their output encoding).
+   *
+   * The first item of the returned tuple is `NS_ERROR_OUT_OF_MEMORY` upon
+   * OOM, `NS_OK_HAD_REPLACEMENTS` if there were unmappable code points (that
+   * were replaced with numeric character references) and `NS_OK` otherwise.
+
+   * _Note:_ It is wrong to use this when the input buffer represents only
+   * a segment of the input instead of the whole input. Use `NewEncoder()`
+   * when encoding segmented output.
+   */
+  inline Tuple<nsresult, NotNull<const mozilla::Encoding*>> Encode(
+    Span<const char16_t> aString,
+    nsACString& aOut) const
+  {
+    const Encoding* encoding = this;
+    nsresult rv = mozilla_encoding_encode_from_utf16(
+      &encoding, aString.Elements(), aString.Length(), &aOut);
+    return MakeTuple(rv, WrapNotNull(encoding));
+  }
+
+  /**
+   * Instantiates a new decoder for this encoding with BOM sniffing enabled.
+   *
+   * BOM sniffing may cause the returned decoder to morph into a decoder
+   * for UTF-8, UTF-16LE or UTF-16BE instead of this encoding.
+   */
+  inline UniquePtr<Decoder> NewDecoder() const
+  {
+    UniquePtr<Decoder> decoder(encoding_new_decoder(this));
+    return decoder;
+  }
+
+  /**
+   * Instantiates a new decoder for this encoding with BOM sniffing enabled
+   * into memory occupied by a previously-instantiated decoder.
+   *
+   * BOM sniffing may cause the returned decoder to morph into a decoder
+   * for UTF-8, UTF-16LE or UTF-16BE instead of this encoding.
+   */
+  inline void NewDecoderInto(Decoder& aDecoder) const
+  {
+    encoding_new_decoder_into(this, &aDecoder);
+  }
+
+  /**
+   * Instantiates a new decoder for this encoding with BOM removal.
+   *
+   * If the input starts with bytes that are the BOM for this encoding,
+   * those bytes are removed. However, the decoder never morphs into a
+   * decoder for another encoding: A BOM for another encoding is treated as
+   * (potentially malformed) input to the decoding algorithm for this
+   * encoding.
+   */
+  inline UniquePtr<Decoder> NewDecoderWithBOMRemoval() const
+  {
+    UniquePtr<Decoder> decoder(encoding_new_decoder_with_bom_removal(this));
+    return decoder;
+  }
+
+  /**
+   * Instantiates a new decoder for this encoding with BOM removal
+   * into memory occupied by a previously-instantiated decoder.
+   *
+   * If the input starts with bytes that are the BOM for this encoding,
+   * those bytes are removed. However, the decoder never morphs into a
+   * decoder for another encoding: A BOM for another encoding is treated as
+   * (potentially malformed) input to the decoding algorithm for this
+   * encoding.
+   */
+  inline void NewDecoderWithBOMRemovalInto(Decoder& aDecoder) const
+  {
+    encoding_new_decoder_with_bom_removal_into(this, &aDecoder);
+  }
+
+  /**
+   * Instantiates a new decoder for this encoding with BOM handling disabled.
+   *
+   * If the input starts with bytes that look like a BOM, those bytes are
+   * not treated as a BOM. (Hence, the decoder never morphs into a decoder
+   * for another encoding.)
+   *
+   * _Note:_ If the caller has performed BOM sniffing on its own but has not
+   * removed the BOM, the caller should use `NewDecoderWithBOMRemoval()`
+   * instead of this method to cause the BOM to be removed.
+   */
+  inline UniquePtr<Decoder> NewDecoderWithoutBOMHandling() const
+  {
+    UniquePtr<Decoder> decoder(encoding_new_decoder_without_bom_handling(this));
+    return decoder;
+  }
+
+  /**
+   * Instantiates a new decoder for this encoding with BOM handling disabled
+   * into memory occupied by a previously-instantiated decoder.
+   *
+   * If the input starts with bytes that look like a BOM, those bytes are
+   * not treated as a BOM. (Hence, the decoder never morphs into a decoder
+   * for another encoding.)
+   *
+   * _Note:_ If the caller has performed BOM sniffing on its own but has not
+   * removed the BOM, the caller should use `NewDecoderWithBOMRemovalInto()`
+   * instead of this method to cause the BOM to be removed.
+   */
+  inline void NewDecoderWithoutBOMHandlingInto(Decoder& aDecoder) const
+  {
+    encoding_new_decoder_without_bom_handling_into(this, &aDecoder);
+  }
+
+  /**
+   * Instantiates a new encoder for the output encoding of this encoding.
+   */
+  inline UniquePtr<Encoder> NewEncoder() const
+  {
+    UniquePtr<Encoder> encoder(encoding_new_encoder(this));
+    return encoder;
+  }
+
+  /**
+   * Instantiates a new encoder for the output encoding of this encoding
+   * into memory occupied by a previously-instantiated encoder.
+   */
+  inline void NewEncoderInto(Encoder& aEncoder) const
+  {
+    encoding_new_encoder_into(this, &aEncoder);
+  }
+
+  /**
+   * Validates UTF-8.
+   *
+   * Returns the index of the first byte that makes the input malformed as
+   * UTF-8 or the length of the input if the input is entirely valid.
+   */
+  static inline size_t UTF8ValidUpTo(Span<const uint8_t> aBuffer)
+  {
+    return encoding_utf8_valid_up_to(aBuffer.Elements(), aBuffer.Length());
+  }
+
+  /**
+   * Validates ASCII.
+   *
+   * Returns the index of the first byte that makes the input malformed as
+   * ASCII or the length of the input if the input is entirely valid.
+   */
+  static inline size_t ASCIIValidUpTo(Span<const uint8_t> aBuffer)
+  {
+    return encoding_ascii_valid_up_to(aBuffer.Elements(), aBuffer.Length());
+  }
+
+  /**
+   * Validates ISO-2022-JP ASCII-state data.
+   *
+   * Returns the index of the first byte that makes the input not
+   * representable in the ASCII state of ISO-2022-JP or the length of the
+   * input if the input is entirely representable in the ASCII state of
+   * ISO-2022-JP.
+   */
+  static inline size_t ISO2022JPASCIIValidUpTo(Span<const uint8_t> aBuffer)
+  {
+    return encoding_iso_2022_jp_ascii_valid_up_to(aBuffer.Elements(),
+                                                  aBuffer.Length());
+  }
+
+private:
+  Encoding() = delete;
+  Encoding(const Encoding&) = delete;
+  Encoding& operator=(const Encoding&) = delete;
+  ~Encoding() = delete;
+
+};
+
+/**
+ * A converter that decodes a byte stream into Unicode according to a
+ * character encoding in a streaming (incremental) manner.
+ *
+ * The various `Decode*` methods take an input buffer (`aSrc`) and an output
+ * buffer `aDst` both of which are caller-allocated. There are variants for
+ * both UTF-8 and UTF-16 output buffers.
+ *
+ * A `Decode*` method decodes bytes from `aSrc` into Unicode characters stored
+ * into `aDst` until one of the following three things happens:
+ *
+ * 1. A malformed byte sequence is encountered (`*WithoutReplacement`
+ *    variants only).
+ *
+ * 2. The output buffer has been filled so near capacity that the decoder
+ *    cannot be sure that processing an additional byte of input wouldn't
+ *    cause so much output that the output buffer would overflow.
+ *
+ * 3. All the input bytes have been processed.
+ *
+ * The `Decode*` method then returns tuple of a status indicating which one
+ * of the three reasons to return happened, how many input bytes were read,
+ * how many output code units (`uint8_t` when decoding into UTF-8 and `char16_t`
+ * when decoding to UTF-16) were written, and in the case of the
+ * variants performing replacement, a boolean indicating whether an error was
+ * replaced with the REPLACEMENT CHARACTER during the call.
+ *
+ * The number of bytes "written" is what's logically written. Garbage may be
+ * written in the output buffer beyond the point logically written to.
+ *
+ * In the case of the `*WithoutReplacement` variants, the status is a
+ * `uint32_t` whose possible values are packed info about a malformed byte
+ * sequence, `kOutputFull` and `kInputEmpty` corresponding to the three cases
+ * listed above).
+ *
+ * Packed info about malformed sequences has the following format:
+ * The lowest 8 bits, which can have the decimal value 0, 1, 2 or 3,
+ * indicate the number of bytes that were consumed after the malformed
+ * sequence and whose next-lowest 8 bits, when shifted right by 8 indicate
+ * the length of the malformed byte sequence (possible decimal values 1, 2,
+ * 3 or 4). The maximum possible sum of the two is 6.
+ *
+ * In the case of methods whose name does not end with
+ * `*WithoutReplacement`, malformed sequences are automatically replaced
+ * with the REPLACEMENT CHARACTER and errors do not cause the methods to
+ * return early.
+ *
+ * When decoding to UTF-8, the output buffer must have at least 4 bytes of
+ * space. When decoding to UTF-16, the output buffer must have at least two
+ * UTF-16 code units (`char16_t`) of space.
+ *
+ * When decoding to UTF-8 without replacement, the methods are guaranteed
+ * not to return indicating that more output space is needed if the length
+ * of the output buffer is at least the length returned by
+ * `MaxUTF8BufferLengthWithoutReplacement()`. When decoding to UTF-8
+ * with replacement, the length of the output buffer that guarantees the
+ * methods not to return indicating that more output space is needed is given
+ * by `MaxUTF8BufferLength()`. When decoding to UTF-16 with
+ * or without replacement, the length of the output buffer that guarantees
+ * the methods not to return indicating that more output space is needed is
+ * given by `MaxUTF16BufferLength()`.
+ *
+ * The output written into `aDst` is guaranteed to be valid UTF-8 or UTF-16,
+ * and the output after each `Decode*` call is guaranteed to consist of
+ * complete characters. (I.e. the code unit sequence for the last character is
+ * guaranteed not to be split across output buffers.)
+ *
+ * The boolean argument `aLast` indicates that the end of the stream is reached
+ * when all the bytes in `aSrc` have been consumed.
+ *
+ * A `Decoder` object can be used to incrementally decode a byte stream.
+ *
+ * During the processing of a single stream, the caller must call `Decode*`
+ * zero or more times with `aLast` set to `false` and then call `Decode*` at
+ * least once with `aLast` set to `true`. If `Decode*` returns `kInputEmpty`,
+ * the processing of the stream has ended. Otherwise, the caller must call
+ * `Decode*` again with `aLast` set to `true` (or treat a malformed result,
+ * i.e. neither `kInputEmpty` nor `kOutputFull`, as a fatal error).
+ *
+ * Once the stream has ended, the `Decoder` object must not be used anymore.
+ * That is, you need to create another one to process another stream.
+ *
+ * When the decoder returns `kOutputFull` or the decoder returns a malformed
+ * result and the caller does not wish to treat it as a fatal error, the input
+ * buffer `aSrc` may not have been completely consumed. In that case, the caller
+ * must pass the unconsumed contents of `aSrc` to `Decode*` again upon the next
+ * call.
+ *
+ * # Infinite loops
+ *
+ * When converting with a fixed-size output buffer whose size is too small to
+ * accommodate one character of output, an infinite loop ensues. When
+ * converting with a fixed-size output buffer, it generally makes sense to
+ * make the buffer fairly large (e.g. couple of kilobytes).
+ */
+class Decoder final
+{
+public:
+  ~Decoder() {}
+  static void operator delete(void* aDecoder)
+  {
+    decoder_free(reinterpret_cast<Decoder*>(aDecoder));
+  }
+
+  /**
+   * The `Encoding` this `Decoder` is for.
+   *
+   * BOM sniffing can change the return value of this method during the life
+   * of the decoder.
+   */
+  inline NotNull<const mozilla::Encoding*> Encoding() const
+  {
+    return WrapNotNull(decoder_encoding(this));
+  }
+
+  /**
+   * Query the worst-case UTF-8 output size _with replacement_.
+   *
+   * Returns the size of the output buffer in UTF-8 code units (`uint8_t`)
+   * that will not overflow given the current state of the decoder and
+   * `aByteLength` number of additional input bytes when decoding with
+   * errors handled by outputting a REPLACEMENT CHARACTER for each malformed
+   * sequence.
+   */
+  inline CheckedInt<size_t> MaxUTF8BufferLength(size_t aByteLength) const
+  {
+    CheckedInt<size_t> max(decoder_max_utf8_buffer_length(this, aByteLength));
+    if (max.value() == MaxValue<size_t>::value) {
+      // Mark invalid by overflowing
+      max++;
+      MOZ_ASSERT(!max.isValid());
+    }
+    return max;
+  }
+
+  /**
+   * Query the worst-case UTF-8 output size _without replacement_.
+   *
+   * Returns the size of the output buffer in UTF-8 code units (`uint8_t`)
+   * that will not overflow given the current state of the decoder and
+   * `aByteLength` number of additional input bytes when decoding without
+   * replacement error handling.
+   *
+   * Note that this value may be too small for the `WithReplacement` case.
+   * Use `MaxUTF8BufferLength()` for that case.
+   */
+  inline CheckedInt<size_t> MaxUTF8BufferLengthWithoutReplacement(
+    size_t aByteLength) const
+  {
+    CheckedInt<size_t> max(
+      decoder_max_utf8_buffer_length_without_replacement(this, aByteLength));
+    if (max.value() == MaxValue<size_t>::value) {
+      // Mark invalid by overflowing
+      max++;
+      MOZ_ASSERT(!max.isValid());
+    }
+    return max;
+  }
+
+  /**
+   * Incrementally decode a byte stream into UTF-8 with malformed sequences
+   * replaced with the REPLACEMENT CHARACTER.
+   *
+   * See the documentation of the class for documentation for `Decode*`
+   * methods collectively.
+   */
+  inline Tuple<uint32_t, size_t, size_t, bool>
+  DecodeToUTF8(Span<const uint8_t> aSrc, Span<uint8_t> aDst, bool aLast)
+  {
+    size_t srcRead = aSrc.Length();
+    size_t dstWritten = aDst.Length();
+    bool hadReplacements;
+    uint32_t result = decoder_decode_to_utf8(this,
+                                             aSrc.Elements(),
+                                             &srcRead,
+                                             aDst.Elements(),
+                                             &dstWritten,
+                                             aLast,
+                                             &hadReplacements);
+    return MakeTuple(result, srcRead, dstWritten, hadReplacements);
+  }
+
+  /**
+   * Incrementally decode a byte stream into UTF-8 _without replacement_.
+   *
+   * See the documentation of the class for documentation for `Decode*`
+   * methods collectively.
+   */
+  inline Tuple<uint32_t, size_t, size_t> DecodeToUTF8WithoutReplacement(
+    Span<const uint8_t> aSrc,
+    Span<uint8_t> aDst,
+    bool aLast)
+  {
+    size_t srcRead = aSrc.Length();
+    size_t dstWritten = aDst.Length();
+    uint32_t result = decoder_decode_to_utf8_without_replacement(
+      this, aSrc.Elements(), &srcRead, aDst.Elements(), &dstWritten, aLast);
+    return MakeTuple(result, srcRead, dstWritten);
+  }
+
+  /**
+   * Query the worst-case UTF-16 output size (with or without replacement).
+   *
+   * Returns the size of the output buffer in UTF-16 code units (`char16_t`)
+   * that will not overflow given the current state of the decoder and
+   * `aByteLength` number of additional input bytes.
+   *
+   * Since the REPLACEMENT CHARACTER fits into one UTF-16 code unit, the
+   * return value of this method applies also in the
+   * `_without_replacement` case.
+   */
+  inline CheckedInt<size_t> MaxUTF16BufferLength(size_t aU16Length) const
+  {
+    CheckedInt<size_t> max(decoder_max_utf16_buffer_length(this, aU16Length));
+    if (max.value() == MaxValue<size_t>::value) {
+      // Mark invalid by overflowing
+      max++;
+      MOZ_ASSERT(!max.isValid());
+    }
+    return max;
+  }
+
+  /**
+   * Incrementally decode a byte stream into UTF-16 with malformed sequences
+   * replaced with the REPLACEMENT CHARACTER.
+   *
+   * See the documentation of the class for documentation for `Decode*`
+   * methods collectively.
+   */
+  inline Tuple<uint32_t, size_t, size_t, bool>
+  DecodeToUTF16(Span<const uint8_t> aSrc, Span<char16_t> aDst, bool aLast)
+  {
+    size_t srcRead = aSrc.Length();
+    size_t dstWritten = aDst.Length();
+    bool hadReplacements;
+    uint32_t result = decoder_decode_to_utf16(this,
+                                              aSrc.Elements(),
+                                              &srcRead,
+                                              aDst.Elements(),
+                                              &dstWritten,
+                                              aLast,
+                                              &hadReplacements);
+    return MakeTuple(result, srcRead, dstWritten, hadReplacements);
+  }
+
+  /**
+   * Incrementally decode a byte stream into UTF-16 _without replacement_.
+   *
+   * See the documentation of the class for documentation for `Decode*`
+   * methods collectively.
+   */
+  inline Tuple<uint32_t, size_t, size_t> DecodeToUTF16WithoutReplacement(
+    Span<const uint8_t> aSrc,
+    Span<char16_t> aDst,
+    bool aLast)
+  {
+    size_t srcRead = aSrc.Length();
+    size_t dstWritten = aDst.Length();
+    uint32_t result = decoder_decode_to_utf16_without_replacement(
+      this, aSrc.Elements(), &srcRead, aDst.Elements(), &dstWritten, aLast);
+    return MakeTuple(result, srcRead, dstWritten);
+  }
+
+private:
+  Decoder() = delete;
+  Decoder(const Decoder&) = delete;
+  Decoder& operator=(const Decoder&) = delete;
+};
+
+/**
+ * A converter that encodes a Unicode stream into bytes according to a
+ * character encoding in a streaming (incremental) manner.
+ *
+ * The various `Encode*` methods take an input buffer (`aSrc`) and an output
+ * buffer `aDst` both of which are caller-allocated. There are variants for
+ * both UTF-8 and UTF-16 input buffers.
+ *
+ * An `Encode*` method encode characters from `aSrc` into bytes characters
+ * stored into `aDst` until one of the following three things happens:
+ *
+ * 1. An unmappable character is encountered (`*WithoutReplacement` variants
+ *    only).
+ *
+ * 2. The output buffer has been filled so near capacity that the decoder
+ *    cannot be sure that processing an additional character of input wouldn't
+ *    cause so much output that the output buffer would overflow.
+ *
+ * 3. All the input characters have been processed.
+ *
+ * The `Encode*` method then returns tuple of a status indicating which one
+ * of the three reasons to return happened, how many input code units (`uint8_t`
+ * when encoding from UTF-8 and `char16_t` when encoding from UTF-16) were read,
+ * how many output bytes were written, and in the case of the variants that
+ * perform replacement, a boolean indicating whether an unmappable
+ * character was replaced with a numeric character reference during the call.
+ *
+ * The number of bytes "written" is what's logically written. Garbage may be
+ * written in the output buffer beyond the point logically written to.
+ *
+ * In the case of the methods whose name ends with
+ * `*WithoutReplacement`, the status is a `uint32_t` whose possible values
+ * are an unmappable code point, `kOutputFull` and `kInputEmpty` corresponding
+ * to the three cases listed above).
+ *
+ * In the case of methods whose name does not end with
+ * `*WithoutReplacement`, unmappable characters are automatically replaced
+ * with the corresponding numeric character references and unmappable
+ * characters do not cause the methods to return early.
+ *
+ * When encoding from UTF-8 without replacement, the methods are guaranteed
+ * not to return indicating that more output space is needed if the length
+ * of the output buffer is at least the length returned by
+ * `MaxBufferLengthFromUTF8WithoutReplacement()`. When encoding from
+ * UTF-8 with replacement, the length of the output buffer that guarantees the
+ * methods not to return indicating that more output space is needed in the
+ * absence of unmappable characters is given by
+ * `MaxBufferLengthFromUTF8IfNoUnmappables()`. When encoding from
+ * UTF-16 without replacement, the methods are guaranteed not to return
+ * indicating that more output space is needed if the length of the output
+ * buffer is at least the length returned by
+ * `MaxBufferLengthFromUTF16WithoutReplacement()`. When encoding
+ * from UTF-16 with replacement, the the length of the output buffer that
+ * guarantees the methods not to return indicating that more output space is
+ * needed in the absence of unmappable characters is given by
+ * `MaxBufferLengthFromUTF16IfNoUnmappables()`.
+ * When encoding with replacement, applications are not expected to size the
+ * buffer for the worst case ahead of time but to resize the buffer if there
+ * are unmappable characters. This is why max length queries are only available
+ * for the case where there are no unmappable characters.
+ *
+ * When encoding from UTF-8, each `aSrc` buffer _must_ be valid UTF-8. When
+ * encoding from UTF-16, unpaired surrogates in the input are treated as U+FFFD
+ * REPLACEMENT CHARACTERS. Therefore, in order for astral characters not to
+ * turn into a pair of REPLACEMENT CHARACTERS, the caller must ensure that
+ * surrogate pairs are not split across input buffer boundaries.
+ *
+ * After an `Encode*` call returns, the output produced so far, taken as a
+ * whole from the start of the stream, is guaranteed to consist of a valid
+ * byte sequence in the target encoding. (I.e. the code unit sequence for a
+ * character is guaranteed not to be split across output buffers. However, due
+ * to the stateful nature of ISO-2022-JP, the stream needs to be considered
+ * from the start for it to be valid. For other encodings, the validity holds
+ * on a per-output buffer basis.)
+ *
+ * The boolean argument `aLast` indicates that the end of the stream is reached
+ * when all the characters in `aSrc` have been consumed. This argument is needed
+ * for ISO-2022-JP and is ignored for other encodings.
+ *
+ * An `Encoder` object can be used to incrementally encode a byte stream.
+ *
+ * During the processing of a single stream, the caller must call `Encode*`
+ * zero or more times with `aLast` set to `false` and then call `Encode*` at
+ * least once with `aLast` set to `true`. If `Encode*` returns `kInputEmpty`,
+ * the processing of the stream has ended. Otherwise, the caller must call
+ * `Encode*` again with `aLast` set to `true` (or treat an unmappable result,
+ * i.e. neither `kInputEmpty` nor `kOutputFull`, as a fatal error).
+ *
+ * Once the stream has ended, the `Encoder` object must not be used anymore.
+ * That is, you need to create another one to process another stream.
+ *
+ * When the encoder returns `kOutputFull` or the encoder returns an unmappable
+ * result and the caller does not wish to treat it as a fatal error, the input
+ * buffer `aSrc` may not have been completely consumed. In that case, the caller
+ * must pass the unconsumed contents of `aSrc` to `Encode*` again upon the next
+ * call.
+ *
+ * # Infinite loops
+ *
+ * When converting with a fixed-size output buffer whose size is too small to
+ * accommodate one character of output, an infinite loop ensues. When
+ * converting with a fixed-size output buffer, it generally makes sense to
+ * make the buffer fairly large (e.g. couple of kilobytes).
+ */
+class Encoder final
+{
+public:
+  ~Encoder() {}
+
+  static void operator delete(void* aEncoder)
+  {
+    encoder_free(reinterpret_cast<Encoder*>(aEncoder));
+  }
+
+  /**
+   * The `Encoding` this `Encoder` is for.
+   */
+  inline NotNull<const mozilla::Encoding*> Encoding() const
+  {
+    return WrapNotNull(encoder_encoding(this));
+  }
+
+  /**
+   * Returns `true` if this is an ISO-2022-JP encoder that's not in the
+   * ASCII state and `false` otherwise.
+   */
+  inline bool HasPendingState() const
+  {
+    return encoder_has_pending_state(this);
+  }
+
+  /**
+   * Query the worst-case output size when encoding from UTF-8 with
+   * replacement.
+   *
+   * Returns the size of the output buffer in bytes that will not overflow
+   * given the current state of the encoder and `aByteLength` number of
+   * additional input code units if there are no unmappable characters in
+   * the input.
+   */
+  inline CheckedInt<size_t> MaxBufferLengthFromUTF8IfNoUnmappables(
+    size_t aByteLength) const
+  {
+    CheckedInt<size_t> max(
+      encoder_max_buffer_length_from_utf8_if_no_unmappables(this, aByteLength));
+    if (max.value() == MaxValue<size_t>::value) {
+      // Mark invalid by overflowing
+      max++;
+      MOZ_ASSERT(!max.isValid());
+    }
+    return max;
+  }
+
+  /**
+   * Query the worst-case output size when encoding from UTF-8 without
+   * replacement.
+   *
+   * Returns the size of the output buffer in bytes that will not overflow
+   * given the current state of the encoder and `aByteLength` number of
+   * additional input code units.
+   */
+  inline CheckedInt<size_t> MaxBufferLengthFromUTF8WithoutReplacement(
+    size_t aByteLength) const
+  {
+    CheckedInt<size_t> max(
+      encoder_max_buffer_length_from_utf8_without_replacement(this,
+                                                              aByteLength));
+    if (max.value() == MaxValue<size_t>::value) {
+      // Mark invalid by overflowing
+      max++;
+      MOZ_ASSERT(!max.isValid());
+    }
+    return max;
+  }
+
+  /**
+   * Incrementally encode into byte stream from UTF-8 with unmappable
+   * characters replaced with HTML (decimal) numeric character references.
+   *
+   * See the documentation of the class for documentation for `Encode*`
+   * methods collectively.
+   *
+   * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING:
+   * The input ***MUST*** be valid UTF-8 or bad things happen! Unless
+   * absolutely sure, use `Encoding::UTF8ValidUpTo()` to check.
+   */
+  inline Tuple<uint32_t, size_t, size_t, bool>
+  EncodeFromUTF8(Span<const uint8_t> aSrc, Span<uint8_t> aDst, bool aLast)
+  {
+    size_t srcRead = aSrc.Length();
+    size_t dstWritten = aDst.Length();
+    bool hadReplacements;
+    uint32_t result = encoder_encode_from_utf8(this,
+                                               aSrc.Elements(),
+                                               &srcRead,
+                                               aDst.Elements(),
+                                               &dstWritten,
+                                               aLast,
+                                               &hadReplacements);
+    return MakeTuple(result, srcRead, dstWritten, hadReplacements);
+  }
+
+  /**
+   * Incrementally encode into byte stream from UTF-8 _without replacement_.
+   *
+   * See the documentation of the class for documentation for `Encode*`
+   * methods collectively.
+   *
+   * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING:
+   * The input ***MUST*** be valid UTF-8 or bad things happen! Unless
+   * absolutely sure, use `Encoding::UTF8ValidUpTo()` to check.
+   */
+  inline Tuple<uint32_t, size_t, size_t> EncodeFromUTF8WithoutReplacement(
+    Span<const uint8_t> aSrc,
+    Span<uint8_t> aDst,
+    bool aLast)
+  {
+    size_t srcRead = aSrc.Length();
+    size_t dstWritten = aDst.Length();
+    uint32_t result = encoder_encode_from_utf8_without_replacement(
+      this, aSrc.Elements(), &srcRead, aDst.Elements(), &dstWritten, aLast);
+    return MakeTuple(result, srcRead, dstWritten);
+  }
+
+  /**
+   * Query the worst-case output size when encoding from UTF-16 with
+   * replacement.
+   *
+   * Returns the size of the output buffer in bytes that will not overflow
+   * given the current state of the encoder and `aU16Length` number of
+   * additional input code units if there are no unmappable characters in
+   * the input.
+   */
+  inline CheckedInt<size_t> MaxBufferLengthFromUTF16IfNoUnmappables(
+    size_t aU16Length) const
+  {
+    CheckedInt<size_t> max(
+      encoder_max_buffer_length_from_utf16_if_no_unmappables(this, aU16Length));
+    if (max.value() == MaxValue<size_t>::value) {
+      // Mark invalid by overflowing
+      max++;
+      MOZ_ASSERT(!max.isValid());
+    }
+    return max;
+  }
+
+  /**
+   * Query the worst-case output size when encoding from UTF-16 without
+   * replacement.
+   *
+   * Returns the size of the output buffer in bytes that will not overflow
+   * given the current state of the encoder and `aU16Length` number of
+   * additional input code units.
+   */
+  inline CheckedInt<size_t> MaxBufferLengthFromUTF16WithoutReplacement(
+    size_t aU16Length) const
+  {
+    CheckedInt<size_t> max(
+      encoder_max_buffer_length_from_utf16_without_replacement(this,
+                                                               aU16Length));
+    if (max.value() == MaxValue<size_t>::value) {
+      // Mark invalid by overflowing
+      max++;
+      MOZ_ASSERT(!max.isValid());
+    }
+    return max;
+  }
+
+  /**
+   * Incrementally encode into byte stream from UTF-16 with unmappable
+   * characters replaced with HTML (decimal) numeric character references.
+   *
+   * See the documentation of the class for documentation for `Encode*`
+   * methods collectively.
+   */
+  inline Tuple<uint32_t, size_t, size_t, bool>
+  EncodeFromUTF16(Span<const char16_t> aSrc, Span<uint8_t> aDst, bool aLast)
+  {
+    size_t srcRead = aSrc.Length();
+    size_t dstWritten = aDst.Length();
+    bool hadReplacements;
+    uint32_t result = encoder_encode_from_utf16(this,
+                                                aSrc.Elements(),
+                                                &srcRead,
+                                                aDst.Elements(),
+                                                &dstWritten,
+                                                aLast,
+                                                &hadReplacements);
+    return MakeTuple(result, srcRead, dstWritten, hadReplacements);
+  }
+
+  /**
+   * Incrementally encode into byte stream from UTF-16 _without replacement_.
+   *
+   * See the documentation of the class for documentation for `Encode*`
+   * methods collectively.
+   */
+  inline Tuple<uint32_t, size_t, size_t> EncodeFromUTF16WithoutReplacement(
+    Span<const char16_t> aSrc,
+    Span<uint8_t> aDst,
+    bool aLast)
+  {
+    size_t srcRead = aSrc.Length();
+    size_t dstWritten = aDst.Length();
+    uint32_t result = encoder_encode_from_utf16_without_replacement(
+      this, aSrc.Elements(), &srcRead, aDst.Elements(), &dstWritten, aLast);
+    return MakeTuple(result, srcRead, dstWritten);
+  }
+
+private:
+  Encoder() = delete;
+  Encoder(const Encoder&) = delete;
+  Encoder& operator=(const Encoder&) = delete;
+};
+
+}; // namespace mozilla
+
+#endif // mozilla_Encoding_h
new file mode 100644
--- /dev/null
+++ b/intl/encoding_glue/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "encoding_glue"
+description = "FFI functions for mozilla::Encoding that aren't appropriate to share on crates.io due to m-c dependencies"
+version = "0.1.0"
+authors = ["Henri Sivonen <hsivonen@hsivonen.fi>"]
+license = "MIT/Apache-2.0"
+
+[features]
+simd-accel = ["encoding_rs/simd-accel"]
+no-static-ideograph-encoder-tables = ["encoding_rs/no-static-ideograph-encoder-tables"]
+parallel-utf8 = ["encoding_rs/parallel-utf8"]
+
+[dependencies]
+encoding_rs = "0.6.5"
+nsstring = { path = "../../xpcom/rust/nsstring" }
+nserror = { path = "../../xpcom/rust/nserror" }
new file mode 100644
--- /dev/null
+++ b/intl/encoding_glue/src/lib.rs
@@ -0,0 +1,580 @@
+// Copyright 2015-2016 Mozilla Foundation. See the COPYRIGHT
+// file at the top-level directory of this distribution.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Adapted from third_party/rust/encoding_rs/src/lib.rs, so the
+// "top-level directory" in the above notice refers to
+// third_party/rust/encoding_rs/.
+
+extern crate encoding_rs;
+extern crate nsstring;
+extern crate nserror;
+
+use std::slice;
+use encoding_rs::*;
+use nsstring::*;
+use nserror::*;
+
+// nsStringBuffer's internal bookkeeping takes 8 bytes from
+// the allocation. Plus one for termination.
+const NS_CSTRING_OVERHEAD: usize = 9;
+
+/// Takes `Option<usize>`, the destination string and a value
+/// to return on failure and tries to set the length of the
+/// destination string to the `usize` wrapped in the first
+/// argument.
+macro_rules! try_dst_set_len {
+    ($needed:expr,
+     $dst:ident,
+     $ret:expr) => (
+    let needed = match $needed {
+        Some(max) => {
+            // XPCOM strings use uint32_t for length.
+            if max > ::std::u32::MAX as usize {
+                return $ret;
+            }
+            max as u32
+        }
+        None => {
+            return $ret;
+        }
+    };
+    unsafe {
+        if $dst.fallible_set_length(needed).is_err() {
+            return $ret;
+        }
+    }
+     )
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn mozilla_encoding_decode_to_nsstring(encoding: *mut *const Encoding,
+                                                             src: *const u8,
+                                                             src_len: usize,
+                                                             dst: *mut nsAString)
+                                                             -> nsresult {
+    let (rv, enc) = decode_to_nsstring(&**encoding, slice::from_raw_parts(src, src_len), &mut *dst);
+    *encoding = enc as *const Encoding;
+    rv
+}
+
+pub fn decode_to_nsstring(encoding: &'static Encoding,
+                          src: &[u8],
+                          dst: &mut nsAString)
+                          -> (nsresult, &'static Encoding) {
+    if let Some((enc, bom_length)) = Encoding::for_bom(src) {
+        return (decode_to_nsstring_without_bom_handling(enc, &src[bom_length..], dst), enc);
+    }
+    (decode_to_nsstring_without_bom_handling(encoding, src, dst), encoding)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn mozilla_encoding_decode_to_nsstring_with_bom_removal(encoding: *const Encoding, src: *const u8, src_len: usize, dst: *mut nsAString) -> nsresult{
+    decode_to_nsstring_with_bom_removal(&*encoding, slice::from_raw_parts(src, src_len), &mut *dst)
+}
+
+pub fn decode_to_nsstring_with_bom_removal(encoding: &'static Encoding,
+                                           src: &[u8],
+                                           dst: &mut nsAString)
+                                           -> nsresult {
+    let without_bom = if encoding == UTF_8 && src.starts_with(b"\xEF\xBB\xBF") {
+        &src[3..]
+    } else if (encoding == UTF_16LE && src.starts_with(b"\xFF\xFE")) ||
+              (encoding == UTF_16BE && src.starts_with(b"\xFE\xFF")) {
+        &src[2..]
+    } else {
+        src
+    };
+    decode_to_nsstring_without_bom_handling(encoding, without_bom, dst)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn mozilla_encoding_decode_to_nsstring_without_bom_handling(encoding: *const Encoding, src: *const u8, src_len: usize, dst: *mut nsAString) -> nsresult{
+    decode_to_nsstring_without_bom_handling(&*encoding,
+                                            slice::from_raw_parts(src, src_len),
+                                            &mut *dst)
+}
+
+pub fn decode_to_nsstring_without_bom_handling(encoding: &'static Encoding,
+                                               src: &[u8],
+                                               dst: &mut nsAString)
+                                               -> nsresult {
+    let mut decoder = encoding.new_decoder_without_bom_handling();
+    try_dst_set_len!(decoder.max_utf16_buffer_length(src.len()),
+                     dst,
+                     NS_ERROR_OUT_OF_MEMORY);
+    // to_mut() shouldn't fail right after setting length.
+    let (result, read, written, had_errors) = decoder.decode_to_utf16(src, dst.to_mut(), true);
+    debug_assert_eq!(result, CoderResult::InputEmpty);
+    debug_assert_eq!(read, src.len());
+    debug_assert!(written <= dst.len());
+    unsafe {
+        if dst.fallible_set_length(written as u32).is_err() {
+            return NS_ERROR_OUT_OF_MEMORY;
+        }
+    }
+    if had_errors {
+        return NS_OK_HAD_REPLACEMENTS;
+    }
+    NS_OK
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn mozilla_encoding_decode_to_nsstring_without_bom_handling_and_without_replacement
+    (encoding: *const Encoding,
+     src: *const u8,
+     src_len: usize,
+     dst: *mut nsAString)
+     -> nsresult {
+    decode_to_nsstring_without_bom_handling_and_without_replacement(&*encoding,
+                                                                    slice::from_raw_parts(src,
+                                                                                          src_len),
+                                                                    &mut *dst)
+}
+
+pub fn decode_to_nsstring_without_bom_handling_and_without_replacement(encoding: &'static Encoding, src: &[u8], dst: &mut nsAString) -> nsresult{
+    let mut decoder = encoding.new_decoder_without_bom_handling();
+    try_dst_set_len!(decoder.max_utf16_buffer_length(src.len()),
+                     dst,
+                     NS_ERROR_OUT_OF_MEMORY);
+    // to_mut() shouldn't fail right after setting length.
+    let (result, read, written) = decoder
+        .decode_to_utf16_without_replacement(src, dst.to_mut(), true);
+    match result {
+        DecoderResult::InputEmpty => {
+            debug_assert_eq!(read, src.len());
+            debug_assert!(written <= dst.len());
+            unsafe {
+                if dst.fallible_set_length(written as u32).is_err() {
+                    return NS_ERROR_OUT_OF_MEMORY;
+                }
+            }
+            NS_OK
+        }
+        DecoderResult::Malformed(_, _) => {
+            dst.truncate();
+            NS_ERROR_UDEC_ILLEGALINPUT
+        }
+        DecoderResult::OutputFull => unreachable!(),
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn mozilla_encoding_encode_from_utf16(encoding: *mut *const Encoding,
+                                                            src: *const u16,
+                                                            src_len: usize,
+                                                            dst: *mut nsACString)
+                                                            -> nsresult {
+    let (rv, enc) = encode_from_utf16(&**encoding, slice::from_raw_parts(src, src_len), &mut *dst);
+    *encoding = enc as *const Encoding;
+    rv
+}
+
+pub fn encode_from_utf16(encoding: &'static Encoding,
+                         src: &[u16],
+                         dst: &mut nsACString)
+                         -> (nsresult, &'static Encoding) {
+    let output_encoding = encoding.output_encoding();
+    let mut encoder = output_encoding.new_encoder();
+    try_dst_set_len!(encoder.max_buffer_length_from_utf16_if_no_unmappables(src.len()),
+                     dst,
+                     (NS_ERROR_OUT_OF_MEMORY, output_encoding));
+
+    let mut total_read = 0;
+    let mut total_written = 0;
+    let mut total_had_errors = false;
+    loop {
+        let (result, read, written, had_errors) = encoder
+            .encode_from_utf16(&src[total_read..],
+                               &mut (dst.to_mut())[total_written..],
+                               true);
+        total_read += read;
+        total_written += written;
+        total_had_errors |= had_errors;
+        match result {
+            CoderResult::InputEmpty => {
+                debug_assert_eq!(total_read, src.len());
+                debug_assert!(total_written <= dst.len());
+                unsafe {
+                    if dst.fallible_set_length(total_written as u32).is_err() {
+                        return (NS_ERROR_OUT_OF_MEMORY, output_encoding);
+                    }
+                }
+                if total_had_errors {
+                    return (NS_OK_HAD_REPLACEMENTS, output_encoding);
+                }
+                return (NS_OK, output_encoding);
+            }
+            CoderResult::OutputFull => {
+                if let Some(needed) =
+                    checked_add(total_written,
+                                encoder.max_buffer_length_from_utf16_if_no_unmappables(src.len() -
+                                                                                       total_read)) {
+                    // Let's round the allocation up in order to avoid repeated
+                    // allocations. Using power-of-two as the approximation of
+                    // available jemalloc buckets, since linking with
+                    // malloc_good_size is messy.
+                    if let Some(with_bookkeeping) = NS_CSTRING_OVERHEAD.checked_add(needed) {
+                        let rounded = with_bookkeeping.next_power_of_two();
+                        let unclowned = rounded - NS_CSTRING_OVERHEAD;
+                        // XPCOM strings use uint32_t for length.
+                        if unclowned <= ::std::u32::MAX as usize {
+                            unsafe {
+                                if dst.fallible_set_length(unclowned as u32).is_ok() {
+                                    continue;
+                                }
+                            }
+                        }
+                    }
+                }
+                return (NS_ERROR_OUT_OF_MEMORY, output_encoding);
+            }
+        }
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn mozilla_encoding_decode_to_nscstring(encoding: *mut *const Encoding,
+                                                              src: *const nsACString,
+                                                              dst: *mut nsACString)
+                                                              -> nsresult {
+    debug_assert_ne!(src as usize, dst as usize);
+    let (rv, enc) = decode_to_nscstring(&**encoding, &*src, &mut *dst);
+    *encoding = enc as *const Encoding;
+    rv
+}
+
+pub fn decode_to_nscstring(encoding: &'static Encoding,
+                           src: &nsACString,
+                           dst: &mut nsACString)
+                           -> (nsresult, &'static Encoding) {
+    if let Some((enc, bom_length)) = Encoding::for_bom(src) {
+        return (decode_from_slice_to_nscstring_without_bom_handling(enc,
+                                                                    &src[bom_length..],
+                                                                    dst,
+                                                                    0),
+                enc);
+    }
+    (decode_to_nscstring_without_bom_handling(encoding, src, dst), encoding)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn mozilla_encoding_decode_to_nscstring_with_bom_removal(encoding: *const Encoding, src: *const nsACString, dst: *mut nsACString) -> nsresult{
+    debug_assert_ne!(src as usize, dst as usize);
+    decode_to_nscstring_with_bom_removal(&*encoding, &*src, &mut *dst)
+}
+
+pub fn decode_to_nscstring_with_bom_removal(encoding: &'static Encoding,
+                                            src: &nsACString,
+                                            dst: &mut nsACString)
+                                            -> nsresult {
+    let without_bom = if encoding == UTF_8 && src.starts_with(b"\xEF\xBB\xBF") {
+        &src[3..]
+    } else if (encoding == UTF_16LE && src.starts_with(b"\xFF\xFE")) ||
+              (encoding == UTF_16BE && src.starts_with(b"\xFE\xFF")) {
+        &src[2..]
+    } else {
+        return decode_to_nscstring_without_bom_handling(encoding, src, dst);
+    };
+    decode_from_slice_to_nscstring_without_bom_handling(encoding, without_bom, dst, 0)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn mozilla_encoding_decode_to_nscstring_without_bom_handling(encoding: *const Encoding, src: *const nsACString, dst: *mut nsACString) -> nsresult{
+    debug_assert_ne!(src as usize, dst as usize);
+    decode_to_nscstring_without_bom_handling(&*encoding, &*src, &mut *dst)
+}
+
+pub fn decode_to_nscstring_without_bom_handling(encoding: &'static Encoding,
+                                                src: &nsACString,
+                                                dst: &mut nsACString)
+                                                -> nsresult {
+    let bytes = &src[..];
+    let valid_up_to = if encoding == UTF_8 {
+        Encoding::utf8_valid_up_to(bytes)
+    } else if encoding.is_ascii_compatible() {
+        Encoding::ascii_valid_up_to(bytes)
+    } else if encoding == ISO_2022_JP {
+        Encoding::iso_2022_jp_ascii_valid_up_to(bytes)
+    } else {
+        return decode_from_slice_to_nscstring_without_bom_handling(encoding, src, dst, 0);
+    };
+    if valid_up_to == bytes.len() {
+        if dst.fallible_assign(src).is_err() {
+            return NS_ERROR_OUT_OF_MEMORY;
+        }
+        return NS_OK;
+    }
+    decode_from_slice_to_nscstring_without_bom_handling(encoding, src, dst, valid_up_to)
+}
+
+fn decode_from_slice_to_nscstring_without_bom_handling(encoding: &'static Encoding,
+                                                       src: &[u8],
+                                                       dst: &mut nsACString,
+                                                       already_validated: usize)
+                                                       -> nsresult {
+    let bytes = src;
+    let mut decoder = encoding.new_decoder_without_bom_handling();
+    let rounded_without_replacement =
+        checked_next_power_of_two(checked_add(already_validated, decoder.max_utf8_buffer_length_without_replacement(bytes.len() - already_validated)));
+    let with_replacement = checked_add(already_validated,
+                                       decoder.max_utf8_buffer_length(bytes.len() -
+                                                                      already_validated));
+    try_dst_set_len!(checked_min(rounded_without_replacement, with_replacement),
+                     dst,
+                     NS_ERROR_OUT_OF_MEMORY);
+
+    if already_validated != 0 {
+        // to_mut() shouldn't fail right after setting length.
+        &mut (dst.to_mut())[..already_validated].copy_from_slice(&bytes[..already_validated]);
+    }
+    let mut total_read = already_validated;
+    let mut total_written = already_validated;
+    let mut total_had_errors = false;
+    loop {
+        // to_mut() shouldn't fail right after setting length.
+        let (result, read, written, had_errors) =
+            decoder.decode_to_utf8(&bytes[total_read..],
+                                   &mut (dst.to_mut())[total_written..],
+                                   true);
+        total_read += read;
+        total_written += written;
+        total_had_errors |= had_errors;
+        match result {
+            CoderResult::InputEmpty => {
+                debug_assert_eq!(total_read, bytes.len());
+                unsafe {
+                    if dst.fallible_set_length(total_written as u32).is_err() {
+                        return NS_ERROR_OUT_OF_MEMORY;
+                    }
+                }
+                if total_had_errors {
+                    return NS_OK_HAD_REPLACEMENTS;
+                }
+                return NS_OK;
+            }
+            CoderResult::OutputFull => {
+                // Allocate for the worst case. That is, we should come
+                // here at most once per invocation of this method.
+                try_dst_set_len!(checked_add(total_written,
+                                             decoder.max_utf8_buffer_length(bytes.len() -
+                                                                            total_read)),
+                                 dst,
+                                 NS_ERROR_OUT_OF_MEMORY);
+                continue;
+            }
+        }
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn mozilla_encoding_decode_to_nscstring_without_bom_handling_and_without_replacement
+    (encoding: *const Encoding,
+     src: *const nsACString,
+     dst: *mut nsACString)
+     -> nsresult {
+    decode_to_nscstring_without_bom_handling_and_without_replacement(&*encoding, &*src, &mut *dst)
+}
+
+pub fn decode_to_nscstring_without_bom_handling_and_without_replacement(encoding: &'static Encoding, src: &nsACString, dst: &mut nsACString) -> nsresult{
+    let bytes = &src[..];
+    if encoding == UTF_8 {
+        let valid_up_to = Encoding::utf8_valid_up_to(bytes);
+        if valid_up_to == bytes.len() {
+            if dst.fallible_assign(src).is_err() {
+                return NS_ERROR_OUT_OF_MEMORY;
+            }
+            return NS_OK;
+        }
+        return NS_ERROR_UDEC_ILLEGALINPUT;
+    }
+    let valid_up_to = if encoding.is_ascii_compatible() {
+        Encoding::ascii_valid_up_to(bytes)
+    } else if encoding == ISO_2022_JP {
+        Encoding::iso_2022_jp_ascii_valid_up_to(bytes)
+    } else {
+        0
+    };
+    if valid_up_to == bytes.len() {
+        if dst.fallible_assign(src).is_err() {
+            return NS_ERROR_OUT_OF_MEMORY;
+        }
+        return NS_OK;
+    }
+    let mut decoder = encoding.new_decoder_without_bom_handling();
+    try_dst_set_len!(checked_add(valid_up_to,
+                                 decoder.max_utf8_buffer_length_without_replacement(bytes.len() -
+                                                                                    valid_up_to)),
+                     dst,
+                     NS_ERROR_OUT_OF_MEMORY);
+    // to_mut() shouldn't fail right after setting length.
+    let (result, read, written) = {
+        let dest = dst.to_mut();
+        dest[..valid_up_to].copy_from_slice(&bytes[..valid_up_to]);
+        decoder
+            .decode_to_utf8_without_replacement(&src[valid_up_to..], &mut dest[valid_up_to..], true)
+    };
+    match result {
+        DecoderResult::InputEmpty => {
+            debug_assert_eq!(valid_up_to + read, src.len());
+            debug_assert!(valid_up_to + written <= dst.len());
+            unsafe {
+                if dst.fallible_set_length((valid_up_to + written) as u32)
+                       .is_err() {
+                    return NS_ERROR_OUT_OF_MEMORY;
+                }
+            }
+            NS_OK
+        }
+        DecoderResult::Malformed(_, _) => {
+            dst.truncate();
+            NS_ERROR_UDEC_ILLEGALINPUT
+        }
+        DecoderResult::OutputFull => unreachable!(),
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn mozilla_encoding_encode_from_nscstring(encoding: *mut *const Encoding,
+                                                                src: *const nsACString,
+                                                                dst: *mut nsACString)
+                                                                -> nsresult {
+    let (rv, enc) = encode_from_nscstring(&**encoding, &*src, &mut *dst);
+    *encoding = enc as *const Encoding;
+    rv
+}
+
+pub fn encode_from_nscstring(encoding: &'static Encoding,
+                             src: &nsACString,
+                             dst: &mut nsACString)
+                             -> (nsresult, &'static Encoding) {
+    let output_encoding = encoding.output_encoding();
+    let bytes = &src[..];
+    if output_encoding == UTF_8 {
+        let valid_up_to = Encoding::utf8_valid_up_to(bytes);
+        if valid_up_to == bytes.len() {
+            if dst.fallible_assign(src).is_err() {
+                return (NS_ERROR_OUT_OF_MEMORY, output_encoding);
+            }
+            return (NS_OK, output_encoding);
+        }
+        return (NS_ERROR_UDEC_ILLEGALINPUT, output_encoding);
+    }
+    let valid_up_to = if output_encoding == ISO_2022_JP {
+        Encoding::iso_2022_jp_ascii_valid_up_to(bytes)
+    } else {
+        debug_assert!(output_encoding.is_ascii_compatible());
+        Encoding::ascii_valid_up_to(bytes)
+    };
+    if valid_up_to == bytes.len() {
+        if dst.fallible_assign(src).is_err() {
+            return (NS_ERROR_OUT_OF_MEMORY, output_encoding);
+        }
+        return (NS_OK, output_encoding);
+    }
+
+    // Encoder requires valid UTF-8. Using std instead of encoding_rs
+    // to avoid unsafe blocks.
+    let trail = if let Ok(trail) = ::std::str::from_utf8(&bytes[valid_up_to..]) {
+        trail
+    } else {
+        return (NS_ERROR_UDEC_ILLEGALINPUT, output_encoding);
+    };
+
+    let mut encoder = output_encoding.new_encoder();
+    try_dst_set_len!(checked_add(valid_up_to,
+                    encoder.max_buffer_length_from_utf8_if_no_unmappables(trail.len())), dst, (NS_ERROR_OUT_OF_MEMORY, output_encoding));
+
+    if valid_up_to != 0 {
+        // to_mut() shouldn't fail right after setting length.
+        &mut (dst.to_mut())[..valid_up_to].copy_from_slice(&bytes[..valid_up_to]);
+    }
+
+    // `total_read` tracks `trail` only but `total_written` tracks the overall situation!
+    // This asymmetry is here, because trail is materialized as `str` without resorting
+    // to unsafe code here.
+    let mut total_read = 0;
+    let mut total_written = valid_up_to;
+    let mut total_had_errors = false;
+    loop {
+        let (result, read, written, had_errors) = encoder
+            .encode_from_utf8(&trail[total_read..],
+                              &mut (dst.to_mut())[total_written..],
+                              true);
+        total_read += read;
+        total_written += written;
+        total_had_errors |= had_errors;
+        match result {
+            CoderResult::InputEmpty => {
+                debug_assert_eq!(valid_up_to + total_read, src.len());
+                debug_assert!(total_written <= dst.len());
+                unsafe {
+                    if dst.fallible_set_length(total_written as u32).is_err() {
+                        return (NS_ERROR_OUT_OF_MEMORY, output_encoding);
+                    }
+                }
+                if total_had_errors {
+                    return (NS_OK_HAD_REPLACEMENTS, output_encoding);
+                }
+                return (NS_OK, output_encoding);
+            }
+            CoderResult::OutputFull => {
+                if let Some(needed) =
+                    checked_add(total_written,
+                                encoder
+                                    .max_buffer_length_from_utf8_if_no_unmappables(trail.len() -
+                                                                                   total_read)) {
+                    // Let's round the allocation up in order to avoid repeated
+                    // allocations. Using power-of-two as the approximation of
+                    // available jemalloc buckets, since linking with
+                    // malloc_good_size is messy.
+                    if let Some(with_bookkeeping) = NS_CSTRING_OVERHEAD.checked_add(needed) {
+                        let rounded = with_bookkeeping.next_power_of_two();
+                        let unclowned = rounded - NS_CSTRING_OVERHEAD;
+                        // XPCOM strings use uint32_t for length.
+                        if unclowned <= ::std::u32::MAX as usize {
+                            unsafe {
+                                if dst.fallible_set_length(unclowned as u32).is_ok() {
+                                    continue;
+                                }
+                            }
+                        }
+                    }
+                }
+                return (NS_ERROR_OUT_OF_MEMORY, output_encoding);
+            }
+        }
+    }
+}
+
+#[inline(always)]
+fn checked_add(num: usize, opt: Option<usize>) -> Option<usize> {
+    if let Some(n) = opt {
+        n.checked_add(num)
+    } else {
+        None
+    }
+}
+
+#[inline(always)]
+fn checked_next_power_of_two(opt: Option<usize>) -> Option<usize> {
+    opt.map(|n| n.next_power_of_two())
+}
+
+#[inline(always)]
+fn checked_min(one: Option<usize>, other: Option<usize>) -> Option<usize> {
+    if let Some(a) = one {
+        if let Some(b) = other {
+            Some(::std::cmp::min(a, b))
+        } else {
+            Some(a)
+        }
+    } else {
+        other
+    }
+}
new file mode 100644
--- /dev/null
+++ b/intl/gtest/TestEncoding.cpp
@@ -0,0 +1,73 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#include "gtest/gtest.h"
+
+#include "mozilla/Encoding.h"
+
+#define ENCODING_TEST(name) TEST(EncodingTest, name)
+
+using namespace mozilla;
+
+// These tests mainly test that the C++ interface seems to
+// reach the Rust code. More thorough testing of the back
+// end is done in Rust.
+
+ENCODING_TEST(ForLabel)
+{
+  nsAutoCString label("  uTf-8   ");
+  ASSERT_EQ(Encoding::ForLabel(label), UTF_8_ENCODING);
+  label.AssignLiteral("   cseucpkdfmTjapanese  ");
+  ASSERT_EQ(Encoding::ForLabel(label), EUC_JP_ENCODING);
+}
+
+ENCODING_TEST(ForName)
+{
+  nsAutoCString encoding("UTF-8");
+  ASSERT_EQ(Encoding::ForName(encoding), UTF_8_ENCODING);
+  encoding.AssignLiteral("EUC-JP");
+  ASSERT_EQ(Encoding::ForName(encoding), EUC_JP_ENCODING);
+}
+
+ENCODING_TEST(ForBOM)
+{
+  nsAutoCString data("\xEF\xBB\xBF\x61");
+  const Encoding* encoding;
+  size_t bomLength;
+  Tie(encoding, bomLength) = Encoding::ForBOM(data);
+  ASSERT_EQ(encoding, UTF_8_ENCODING);
+  ASSERT_EQ(bomLength, 3U);
+  data.AssignLiteral("\xFF\xFE");
+  Tie(encoding, bomLength) = Encoding::ForBOM(data);
+  ASSERT_EQ(encoding, UTF_16LE_ENCODING);
+  ASSERT_EQ(bomLength, 2U);
+  data.AssignLiteral("\xFE\xFF");
+  Tie(encoding, bomLength) = Encoding::ForBOM(data);
+  ASSERT_EQ(encoding, UTF_16BE_ENCODING);
+  ASSERT_EQ(bomLength, 2U);
+  data.AssignLiteral("\xEF\xBB");
+  Tie(encoding, bomLength) = Encoding::ForBOM(data);
+  ASSERT_EQ(encoding, nullptr);
+  ASSERT_EQ(bomLength, 0U);
+}
+
+ENCODING_TEST(Name)
+{
+  nsAutoCString name;
+  UTF_8_ENCODING->Name(name);
+  ASSERT_TRUE(name.EqualsLiteral("UTF-8"));
+  GBK_ENCODING->Name(name);
+  ASSERT_TRUE(name.EqualsLiteral("GBK"));
+}
+
+ENCODING_TEST(CanEncodeEverything)
+{
+  ASSERT_TRUE(UTF_8_ENCODING->CanEncodeEverything());
+  ASSERT_FALSE(GB18030_ENCODING->CanEncodeEverything());
+}
+
+ENCODING_TEST(IsAsciiCompatible)
+{
+  ASSERT_TRUE(UTF_8_ENCODING->IsAsciiCompatible());
+  ASSERT_FALSE(ISO_2022_JP_ENCODING->IsAsciiCompatible());
+}
new file mode 100644
--- /dev/null
+++ b/intl/gtest/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+    'TestEncoding.cpp',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/intl/locale/mac/nsMacCharset.cpp
+++ b/intl/locale/mac/nsMacCharset.cpp
@@ -1,20 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 <Carbon/Carbon.h>
 #include "nsIPlatformCharset.h"
-#include "nsCOMPtr.h"
-#include "nsIServiceManager.h"
-#include "nsReadableUtils.h"
+#include "nsString.h"
 #include "nsPlatformCharset.h"
-#include "nsEncoderDecoderUtils.h"
 
 NS_IMPL_ISUPPORTS(nsPlatformCharset, nsIPlatformCharset)
 
 nsPlatformCharset::nsPlatformCharset()
 {
 }
 nsPlatformCharset::~nsPlatformCharset()
 {
--- a/intl/locale/unix/nsUNIXCharset.cpp
+++ b/intl/locale/unix/nsUNIXCharset.cpp
@@ -6,17 +6,16 @@
 #include <locale.h>
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsIPlatformCharset.h"
 #include "nsUConvPropertySearch.h"
 #include "nsCOMPtr.h"
 #include "nsReadableUtils.h"
-#include "nsEncoderDecoderUtils.h"
 #if HAVE_GNU_LIBC_VERSION_H
 #include <gnu/libc-version.h>
 #endif
 #ifdef HAVE_NL_TYPES_H
 #include <nl_types.h>
 #endif
 #if HAVE_LANGINFO_CODESET
 #include <langinfo.h>
--- a/intl/locale/unix/unixcharset.properties
+++ b/intl/locale/unix/unixcharset.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 ##
 ## NOTE: THIS FILE IS DEPRECATED
-##       except for those *nix systems that do not support 
+##       except for those *nix systems that do not support
 ##       nl_langinfo(CODESET) this file should not be used
 ##
 ## All platform section
 ##   Put the general locale to charset mapping here.
 ##   If somehow two platform use the same locale name with different
 ##   charset, put the least common one in the platform specific section
 ##   This section have lower priority than the platform specific section
 ##
@@ -508,26 +508,26 @@ th_TH=windows-874
 th_TH.TIS620=windows-874
 th=windows-874
 th_TH.UTF-8=UTF-8
 # RedHat 7 reported by Garaschenko Slava <slava@maze.ambernet.kiev.ua bug 70601
 uk_UA=KOI8-U
 zh=gb18030
 zh_CN=gb18030
 zh_CN.EUC=gb18030
-zh.GBK=gbk
+zh.GBK=GBK
 zh_CN.UTF-8=UTF-8
 zh.UTF-8=UTF-8
 zh_TW.BIG5=Big5
 # saw the following name from news://xcin.linux.org.tw/tlug.cle-devel
 zh_CN.gb18030=gb18030
 # AIX
 ZH_CN=UTF-8
 zh_CN.ugb=gb18030
-zh_CN.GBK=gbk
+zh_CN.GBK=GBK
 zh_HK.big5=Big5
 zh_TW.big5=Big5
 zh_TW.big5@chuyin=Big5
 zh_TW.big5@radical=Big5
 zh_TW.big5@stroke=Big5
 # AIX
 Zh_TW.big5=Big5
 # CLE 0.8
--- a/intl/locale/windows/nsWinCharset.cpp
+++ b/intl/locale/windows/nsWinCharset.cpp
@@ -4,21 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsIPlatformCharset.h"
 #include "nsUConvPropertySearch.h"
 #include <windows.h>
 #include "nsWin32Locale.h"
-#include "nsCOMPtr.h"
-#include "nsReadableUtils.h"
-#include "nsServiceManagerUtils.h"
+#include "nsString.h"
 #include "nsPlatformCharset.h"
-#include "nsEncoderDecoderUtils.h"
 
 using namespace mozilla;
 
 static constexpr nsUConvProp kWinCharsets[] = {
 #include "wincharset.properties.h"
 };
 
 NS_IMPL_ISUPPORTS(nsPlatformCharset, nsIPlatformCharset)
--- a/intl/moz.build
+++ b/intl/moz.build
@@ -1,38 +1,51 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+TEST_DIRS += [
+    'gtest',
+]
+
 DIRS += [
     'hyphenation/hyphen',
     'hyphenation/glue',
     'locale',
     'locales',
     'lwbrk',
     'strres',
     'unicharutil',
 ]
 
 DIRS += [
     'uconv',
     'build',
 ]
 
-with Files("**"):
-    BUG_COMPONENT = ("Core", "Internationalization")
-
-with Files("icu/**"):
-    BUG_COMPONENT = ("Core", "JavaScript: Internationalization API")
-
-with Files("icu-patches/**"):
-    BUG_COMPONENT = ("Core", "JavaScript: Internationalization API")
-
-with Files("tzdata/**"):
-    BUG_COMPONENT = ("Core", "JavaScript: Internationalization API")
-
-with Files("update*"):
-    BUG_COMPONENT = ("Core", "JavaScript: Internationalization API")
-
-with Files("icu_sources_data.py"):
-    BUG_COMPONENT = ("Core", "Build Config")
+EXPORTS.mozilla += [
+    'Encoding.h',
+]
+
+EXPORTS += [
+    '../third_party/rust/encoding_c/include/encoding_rs.h',
+    '../third_party/rust/encoding_c/include/encoding_rs_statics.h',
+]
+
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Internationalization")
+
+with Files("icu/**"):
+    BUG_COMPONENT = ("Core", "JavaScript: Internationalization API")
+
+with Files("icu-patches/**"):
+    BUG_COMPONENT = ("Core", "JavaScript: Internationalization API")
+
+with Files("tzdata/**"):
+    BUG_COMPONENT = ("Core", "JavaScript: Internationalization API")
+
+with Files("update*"):
+    BUG_COMPONENT = ("Core", "JavaScript: Internationalization API")
+
+with Files("icu_sources_data.py"):
+    BUG_COMPONENT = ("Core", "Build Config")
--- a/intl/uconv/moz.build
+++ b/intl/uconv/moz.build
@@ -10,162 +10,24 @@ XPIDL_SOURCES += [
     'nsIScriptableUConv.idl',
     'nsITextToSubURI.idl',
     'nsIUTF8ConverterService.idl',
 ]
 
 XPIDL_MODULE = 'uconv'
 
 EXPORTS += [
-    'nsEncoderDecoderUtils.h',
-    'nsIUnicodeDecoder.h',
-    'nsIUnicodeEncoder.h',
-    'nsNCRFallbackEncoderWrapper.h',
-    'nsUConvCID.h',
-    'nsUCSupport.h',
-    'uconvutil.h',
-    'ucvcn/nsUCvCnCID.h',
-    'ucvja/nsUCVJA2CID.h',
-    'ucvja/nsUCVJACID.h',
-    'ucvko/nsUCvKOCID.h',
-    'ucvlatin/nsUCvLatinCID.h',
+	'nsUConvCID.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsConverterInputStream.cpp',
     'nsConverterOutputStream.cpp',
-    'nsCP1252ToUnicode.cpp',
-    'nsMacRomanToUnicode.cpp',
-    'nsNCRFallbackEncoderWrapper.cpp',
-    'nsReplacementToUnicode.cpp',
     'nsScriptableUConv.cpp',
     'nsTextToSubURI.cpp',
     'nsUConvModule.cpp',
-    'nsUnicodeToCP1252.cpp',
-    'nsUnicodeToISO88591.cpp',
-    'nsUnicodeToMacRoman.cpp',
-    'nsUnicodeToUTF8.cpp',
-    'nsUTF8ConverterService.cpp',
-    'nsUTF8ToUnicode.cpp',
-]
-
-UNIFIED_SOURCES += [
-    'ucvcn/nsGBKConvUtil.cpp',
-    'ucvcn/nsGBKToUnicode.cpp',
-    'ucvcn/nsUnicodeToGBK.cpp',
-]
-
-UNIFIED_SOURCES += [
-    'ucvja/nsJapaneseToUnicode.cpp',
-    'ucvja/nsUnicodeToEUCJP.cpp',
-    'ucvja/nsUnicodeToISO2022JP.cpp',
-    'ucvja/nsUnicodeToSJIS.cpp',
-]
-
-UNIFIED_SOURCES += [
-    'ucvko/nsCP949ToUnicode.cpp',
-    'ucvko/nsUnicodeToCP949.cpp',
-]
-
-UNIFIED_SOURCES += [
-    'ucvlatin/nsCP1250ToUnicode.cpp',
-    'ucvlatin/nsCP1251ToUnicode.cpp',
-    'ucvlatin/nsCP1253ToUnicode.cpp',
-    'ucvlatin/nsCP1254ToUnicode.cpp',
-    'ucvlatin/nsCP1255ToUnicode.cpp',
-    'ucvlatin/nsCP1256ToUnicode.cpp',
-    'ucvlatin/nsCP1257ToUnicode.cpp',
-    'ucvlatin/nsCP1258ToUnicode.cpp',
-    'ucvlatin/nsCP866ToUnicode.cpp',
-    'ucvlatin/nsCP874ToUnicode.cpp',
-    'ucvlatin/nsISO885910ToUnicode.cpp',
-    'ucvlatin/nsISO885913ToUnicode.cpp',
-    'ucvlatin/nsISO885914ToUnicode.cpp',
-    'ucvlatin/nsISO885915ToUnicode.cpp',
-    'ucvlatin/nsISO885916ToUnicode.cpp',
-    'ucvlatin/nsISO88592ToUnicode.cpp',
-    'ucvlatin/nsISO88593ToUnicode.cpp',
-    'ucvlatin/nsISO88594ToUnicode.cpp',
-    'ucvlatin/nsISO88595ToUnicode.cpp',
-    'ucvlatin/nsISO88596ToUnicode.cpp',
-    'ucvlatin/nsISO88597ToUnicode.cpp',
-    'ucvlatin/nsISO88598IToUnicode.cpp',
-    'ucvlatin/nsISO88598ToUnicode.cpp',
-    'ucvlatin/nsKOI8RToUnicode.cpp',
-    'ucvlatin/nsKOI8UToUnicode.cpp',
-    'ucvlatin/nsMacArabicToUnicode.cpp',
-    'ucvlatin/nsMacCEToUnicode.cpp',
-    'ucvlatin/nsMacCroatianToUnicode.cpp',
-    'ucvlatin/nsMacCyrillicToUnicode.cpp',
-    'ucvlatin/nsMacDevanagariToUnicode.cpp',
-    'ucvlatin/nsMacFarsiToUnicode.cpp',
-    'ucvlatin/nsMacGreekToUnicode.cpp',
-    'ucvlatin/nsMacGujaratiToUnicode.cpp',
-    'ucvlatin/nsMacGurmukhiToUnicode.cpp',
-    'ucvlatin/nsMacHebrewToUnicode.cpp',
-    'ucvlatin/nsMacIcelandicToUnicode.cpp',
-    'ucvlatin/nsMacRomanianToUnicode.cpp',
-    'ucvlatin/nsMacTurkishToUnicode.cpp',
-    'ucvlatin/nsUnicodeToCP1250.cpp',
-    'ucvlatin/nsUnicodeToCP1251.cpp',
-    'ucvlatin/nsUnicodeToCP1253.cpp',
-    'ucvlatin/nsUnicodeToCP1254.cpp',
-    'ucvlatin/nsUnicodeToCP1255.cpp',
-    'ucvlatin/nsUnicodeToCP1256.cpp',
-    'ucvlatin/nsUnicodeToCP1257.cpp',
-    'ucvlatin/nsUnicodeToCP1258.cpp',
-    'ucvlatin/nsUnicodeToCP866.cpp',
-    'ucvlatin/nsUnicodeToCP874.cpp',
-    'ucvlatin/nsUnicodeToISO885910.cpp',
-    'ucvlatin/nsUnicodeToISO885913.cpp',
-    'ucvlatin/nsUnicodeToISO885914.cpp',
-    'ucvlatin/nsUnicodeToISO885915.cpp',
-    'ucvlatin/nsUnicodeToISO885916.cpp',
-    'ucvlatin/nsUnicodeToISO88592.cpp',
-    'ucvlatin/nsUnicodeToISO88593.cpp',
-    'ucvlatin/nsUnicodeToISO88594.cpp',
-    'ucvlatin/nsUnicodeToISO88595.cpp',
-    'ucvlatin/nsUnicodeToISO88596.cpp',
-    'ucvlatin/nsUnicodeToISO88597.cpp',
-    'ucvlatin/nsUnicodeToISO88598.cpp',
-    'ucvlatin/nsUnicodeToISO88598I.cpp',
-    'ucvlatin/nsUnicodeToKOI8R.cpp',
-    'ucvlatin/nsUnicodeToKOI8U.cpp',
-    'ucvlatin/nsUnicodeToMacCyrillic.cpp',
-    'ucvlatin/nsUnicodeToUserDefined.cpp',
-    'ucvlatin/nsUnicodeToUTF16.cpp',
-    'ucvlatin/nsUserDefinedToUnicode.cpp',
-    'ucvlatin/nsUTF16ToUnicode.cpp',
-]
-
-UNIFIED_SOURCES += [
-    'ucvtw/nsBIG5Data.cpp',
-    'ucvtw/nsBIG5ToUnicode.cpp',
-    'ucvtw/nsUnicodeToBIG5.cpp',
-]
-
-UNIFIED_SOURCES += [
-    'util/nsUCConstructors.cpp',
-    'util/nsUCSupport.cpp',
-    'util/nsUnicodeDecodeHelper.cpp',
-    'util/nsUnicodeEncodeHelper.cpp',
-    'util/ugen.c',
-    'util/umap.c',
-    'util/uscan.c',
-]
-
-if CONFIG['INTEL_ARCHITECTURE']:
-    SOURCES += ['nsUTF8ToUnicodeSSE2.cpp']
-    SOURCES['nsUTF8ToUnicodeSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
-
-LOCAL_INCLUDES += [
-    'ucvcn',
-    'ucvja',
-    'ucvko',
-    'ucvlatin',
-    'ucvtw',
-    'util',
+    'nsUTF8ConverterService.cpp'
 ]
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
--- a/intl/uconv/nsConverterInputStream.cpp
+++ b/intl/uconv/nsConverterInputStream.cpp
@@ -3,19 +3,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsConverterInputStream.h"
 #include "nsIInputStream.h"
 #include "nsReadLine.h"
 #include "nsStreamUtils.h"
 #include <algorithm>
-#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/Unused.h"
 
-using mozilla::dom::EncodingUtils;
+using namespace mozilla;
 
 #define CONVERTER_BUFFER_SIZE 8192
 
 NS_IMPL_ISUPPORTS(nsConverterInputStream, nsIConverterInputStream,
                   nsIUnicharInputStream, nsIUnicharLineInputStream)
 
 
 NS_IMETHODIMP
@@ -26,42 +26,51 @@ nsConverterInputStream::Init(nsIInputStr
 {
     nsAutoCString label;
     if (!aCharset) {
         label.AssignLiteral("UTF-8");
     } else {
         label = aCharset;
     }
 
-    if (aBufferSize <=0) aBufferSize=CONVERTER_BUFFER_SIZE;
-    
-    // get the decoder
-    nsAutoCString encoding;
-    if (label.EqualsLiteral("UTF-16")) {
-        // Compat with old test cases. Unclear if any extensions really care.
-        encoding.Assign(label);
-    } else if (!EncodingUtils::FindEncodingForLabelNoReplacement(label,
-                                                                 encoding)) {
+    auto encoding = Encoding::ForLabelNoReplacement(label);
+    if (!encoding) {
       return NS_ERROR_UCONV_NOCONV;
     }
-    mConverter = EncodingUtils::DecoderForEncoding(encoding);
- 
-    // set up our buffers
+    // Previously, the implementation auto-switched only
+    // between the two UTF-16 variants and only when
+    // initialized with an endianness-unspecific label.
+    mConverter = encoding->NewDecoder();
+
+    size_t outputBufferSize;
+    if (aBufferSize <= 0) {
+      aBufferSize = CONVERTER_BUFFER_SIZE;
+      outputBufferSize = CONVERTER_BUFFER_SIZE;
+    } else {
+      // NetUtil.jsm assumes that if buffer size equals
+      // the input size, the whole stream will be processed
+      // as one readString. This is not true with encoding_rs,
+      // because encoding_rs might want to see space for a
+      // surrogate pair, so let's compute a larger output
+      // buffer length.
+      CheckedInt<size_t> needed = mConverter->MaxUTF16BufferLength(aBufferSize);
+      if (!needed.isValid()) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      outputBufferSize = needed.value();
+    }
+
+    // set up our buffers.
     if (!mByteData.SetCapacity(aBufferSize, mozilla::fallible) ||
-        !mUnicharData.SetCapacity(aBufferSize, mozilla::fallible)) {
+        !mUnicharData.SetLength(outputBufferSize, mozilla::fallible)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     mInput = aStream;
-    mReplacementChar = aReplacementChar;
-    if (!aReplacementChar ||
-        aReplacementChar != mConverter->GetCharacterForUnMapped()) {
-        mConverter->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal);
-    }
-
+    mErrorsAreFatal = !aReplacementChar;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsConverterInputStream::Close()
 {
     nsresult rv = mInput ? mInput->Close() : NS_OK;
     mLineBuffer = nullptr;
@@ -108,36 +117,35 @@ nsConverterInputStream::ReadSegments(nsW
   if (0 == bytesToWrite) {
     // Fill the unichar buffer
     bytesToWrite = Fill(&rv);
     if (bytesToWrite <= 0) {
       *aReadCount = 0;
       return rv;
     }
   }
-  
+
   if (bytesToWrite > aCount)
     bytesToWrite = aCount;
-  
+
   uint32_t bytesWritten;
   uint32_t totalBytesWritten = 0;
 
   while (bytesToWrite) {
     rv = aWriter(this, aClosure,
                  mUnicharData.Elements() + mUnicharDataOffset,
                  totalBytesWritten, bytesToWrite, &bytesWritten);
     if (NS_FAILED(rv)) {
       // don't propagate errors to the caller
       break;
     }
-    
+
     bytesToWrite -= bytesWritten;
     totalBytesWritten += bytesWritten;
     mUnicharDataOffset += bytesWritten;
-    
   }
 
   *aReadCount = totalBytesWritten;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -174,69 +182,67 @@ nsConverterInputStream::Fill(nsresult * 
   }
 
   if (NS_FAILED(mLastErrorCode)) {
     // We failed to completely convert last time, and error-recovery
     // is disabled.  We will fare no better this time, so...
     *aErrorCode = mLastErrorCode;
     return 0;
   }
-  
+
   // We assume a many to one conversion and are using equal sizes for
   // the two buffers.  However if an error happens at the very start
   // of a byte buffer we may end up in a situation where n bytes lead
   // to n+1 unicode chars.  Thus we need to keep track of the leftover
   // bytes as we convert.
-  
+
   uint32_t nb;
   *aErrorCode = NS_FillArray(mByteData, mInput, mLeftOverBytes, &nb);
   if (nb == 0 && mLeftOverBytes == 0) {
-    // No more data 
+    // No more data
     *aErrorCode = NS_OK;
     return 0;
   }
 
   NS_ASSERTION(uint32_t(nb) + mLeftOverBytes == mByteData.Length(),
                "mByteData is lying to us somewhere");
 
   // Now convert as much of the byte buffer to unicode as possible
-  mUnicharDataOffset = 0;
+  auto src = AsBytes(MakeSpan(mByteData));
+  auto dst = MakeSpan(mUnicharData);
+  // mUnicharData.Length() is the buffer length, not the fill status.
+  // mUnicharDataLength reflects the current fill status.
   mUnicharDataLength = 0;
-  uint32_t srcConsumed = 0;
-  do {
-    int32_t srcLen = mByteData.Length() - srcConsumed;
-    int32_t dstLen = mUnicharData.Capacity() - mUnicharDataLength;
-    *aErrorCode = mConverter->Convert(mByteData.Elements()+srcConsumed,
-                                      &srcLen,
-                                      mUnicharData.Elements()+mUnicharDataLength,
-                                      &dstLen);
-    mUnicharDataLength += dstLen;
-    // XXX if srcLen is negative, we want to drop the _first_ byte in
-    // the erroneous byte sequence and try again.  This is not quite
-    // possible right now -- see bug 160784
-    srcConsumed += srcLen;
-    if (NS_FAILED(*aErrorCode) && mReplacementChar) {
-      NS_ASSERTION(0 < mUnicharData.Capacity() - mUnicharDataLength,
-                   "Decoder returned an error but filled the output buffer! "
-                   "Should not happen.");
-      mUnicharData.Elements()[mUnicharDataLength++] = mReplacementChar;
-      ++srcConsumed;
-      // XXX this is needed to make sure we don't underrun our buffer;
-      // bug 160784 again
-      srcConsumed = std::max<uint32_t>(srcConsumed, 0);
-      mConverter->Reset();
-    }
-    NS_ASSERTION(srcConsumed <= mByteData.Length(),
-                 "Whoa.  The converter should have returned NS_OK_UDEC_MOREINPUT before this point!");
-  } while (mReplacementChar &&
-           NS_FAILED(*aErrorCode) &&
-           mUnicharData.Capacity() > mUnicharDataLength);
-
-  mLeftOverBytes = mByteData.Length() - srcConsumed;
-
+  // Whenever we convert, mUnicharData is logically empty.
+  mUnicharDataOffset = 0;
+  // Truncation from size_t to uint32_t below is OK, because the sizes
+  // are bounded by the lengths of mByteData and mUnicharData.
+  uint32_t result;
+  size_t read;
+  size_t written;
+  bool hadErrors;
+  // The design of this class is fundamentally bogus in that trailing
+  // errors are ignored. Always passing false as the last argument to
+  // Decode* calls below.
+  if (mErrorsAreFatal) {
+    Tie(result, read, written) =
+      mConverter->DecodeToUTF16WithoutReplacement(src, dst, false);
+  } else {
+    Tie(result, read, written, hadErrors) =
+      mConverter->DecodeToUTF16(src, dst, false);
+  }
+  Unused << hadErrors;
+  mLeftOverBytes = mByteData.Length() - read;
+  mUnicharDataLength = written;
+  if (result == kInputEmpty || result == kOutputFull) {
+    *aErrorCode = NS_OK;
+  } else {
+    MOZ_ASSERT(mErrorsAreFatal, "How come DecodeToUTF16() reported error?");
+    *aErrorCode = NS_ERROR_UDEC_ILLEGALINPUT;
+  }
   return mUnicharDataLength;
 }
 
 NS_IMETHODIMP
 nsConverterInputStream::ReadLine(nsAString& aLine, bool* aResult)
 {
   if (!mLineBuffer) {
     mLineBuffer = new nsLineBuffer<char16_t>;
--- a/intl/uconv/nsConverterInputStream.h
+++ b/intl/uconv/nsConverterInputStream.h
@@ -7,18 +7,18 @@
 #define nsConverterInputStream_h
 
 #include "nsIInputStream.h"
 #include "nsIConverterInputStream.h"
 #include "nsIUnicharLineInputStream.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
-#include "nsIUnicodeDecoder.h"
 #include "nsReadLine.h"
+#include "mozilla/Encoding.h"
 
 #define NS_CONVERTERINPUTSTREAM_CONTRACTID "@mozilla.org/intl/converter-input-stream;1"
 
 // {2BC2AD62-AD5D-4b7b-A9DB-F74AE203C527}
 #define NS_CONVERTERINPUTSTREAM_CID \
   { 0x2bc2ad62, 0xad5d, 0x4b7b, \
    { 0xa9, 0xdb, 0xf7, 0x4a, 0xe2, 0x3, 0xc5, 0x27 } }
 
@@ -28,37 +28,39 @@ class nsConverterInputStream : public ns
                                public nsIUnicharLineInputStream {
 
  public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIUNICHARINPUTSTREAM
     NS_DECL_NSIUNICHARLINEINPUTSTREAM
     NS_DECL_NSICONVERTERINPUTSTREAM
 
-    nsConverterInputStream() :
-        mLastErrorCode(NS_OK),
-        mLeftOverBytes(0),
-        mUnicharDataOffset(0),
-        mUnicharDataLength(0),
-        mReplacementChar(DEFAULT_REPLACEMENT_CHARACTER),
-        mLineBuffer(nullptr) { }
+    nsConverterInputStream()
+      : mLastErrorCode(NS_OK)
+      , mLeftOverBytes(0)
+      , mUnicharDataOffset(0)
+      , mUnicharDataLength(0)
+      , mErrorsAreFatal(false)
+      , mLineBuffer(nullptr)
+    {
+    }
 
- private:
+  private:
     virtual ~nsConverterInputStream() { Close(); }
 
     uint32_t Fill(nsresult *aErrorCode);
-    
-    nsCOMPtr<nsIUnicodeDecoder> mConverter;
+
+    mozilla::UniquePtr<mozilla::Decoder> mConverter;
     FallibleTArray<char> mByteData;
     FallibleTArray<char16_t> mUnicharData;
     nsCOMPtr<nsIInputStream> mInput;
 
     nsresult  mLastErrorCode;
     uint32_t  mLeftOverBytes;
     uint32_t  mUnicharDataOffset;
     uint32_t  mUnicharDataLength;
-    char16_t mReplacementChar;
+    bool mErrorsAreFatal;
 
     nsAutoPtr<nsLineBuffer<char16_t> > mLineBuffer;
 };
 
 #endif
 
--- a/intl/uconv/nsConverterOutputStream.cpp
+++ b/intl/uconv/nsConverterOutputStream.cpp
@@ -4,102 +4,88 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsCOMPtr.h"
 
 #include "nsIOutputStream.h"
 #include "nsString.h"
 
 #include "nsConverterOutputStream.h"
-#include "nsIUnicodeEncoder.h"
-#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/Encoding.h"
+#include "mozilla/Unused.h"
 
-using mozilla::dom::EncodingUtils;
+using namespace mozilla;
 
 NS_IMPL_ISUPPORTS(nsConverterOutputStream,
                   nsIUnicharOutputStream,
                   nsIConverterOutputStream)
 
 nsConverterOutputStream::~nsConverterOutputStream()
 {
     Close();
 }
 
 NS_IMETHODIMP
 nsConverterOutputStream::Init(nsIOutputStream* aOutStream,
-                              const char*      aCharset,
-                              uint32_t         aBufferSize /* ignored */,
-                              char16_t        aReplacementChar)
+                              const char* aCharset,
+                              uint32_t aBufferSize /* ignored */,
+                              char16_t aReplacementChar) /* ignored */
 {
     NS_PRECONDITION(aOutStream, "Null output stream!");
 
-    nsAutoCString label;
+    const Encoding* encoding;
     if (!aCharset) {
-        label.AssignLiteral("UTF-8");
+      encoding = UTF_8_ENCODING;
     } else {
-        label = aCharset;
+      encoding = Encoding::ForLabelNoReplacement(MakeStringSpan(aCharset));
+      if (!encoding || encoding == UTF_16LE_ENCODING ||
+          encoding == UTF_16BE_ENCODING) {
+        return NS_ERROR_UCONV_NOCONV;
+      }
     }
 
-    nsAutoCString encoding;
-    if (label.EqualsLiteral("UTF-16")) {
-        // Make sure to output a BOM when UTF-16 requested
-        encoding.Assign(label);
-    } else if (!EncodingUtils::FindEncodingForLabelNoReplacement(label,
-                                                                 encoding)) {
-      return NS_ERROR_UCONV_NOCONV;
-    }
-    mConverter = EncodingUtils::EncoderForEncoding(encoding);
+    mConverter = encoding->NewEncoder();
 
     mOutStream = aOutStream;
 
-    int32_t behaviour = aReplacementChar ? nsIUnicodeEncoder::kOnError_Replace
-                                         : nsIUnicodeEncoder::kOnError_Signal;
-    return mConverter->
-        SetOutputErrorBehavior(behaviour,
-                               nullptr,
-                               aReplacementChar);
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsConverterOutputStream::Write(uint32_t aCount, const char16_t* aChars,
                                bool* aSuccess)
 {
     if (!mOutStream) {
         NS_ASSERTION(!mConverter, "Closed streams shouldn't have converters");
         return NS_BASE_STREAM_CLOSED;
     }
-    NS_ASSERTION(mConverter, "Must have a converter when not closed");
-
-    int32_t inLen = aCount;
-
-    int32_t maxLen;
-    nsresult rv = mConverter->GetMaxLength(aChars, inLen, &maxLen);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsAutoCString buf;
-    buf.SetLength(maxLen);
-    if (buf.Length() != (uint32_t) maxLen)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    int32_t outLen = maxLen;
-    rv = mConverter->Convert(aChars, &inLen, buf.BeginWriting(), &outLen);
-    if (NS_FAILED(rv))
+    MOZ_ASSERT(mConverter, "Must have a converter when not closed");
+    uint8_t buffer[4096];
+    auto dst = MakeSpan(buffer);
+    auto src = MakeSpan(aChars, aCount);
+    for (;;) {
+      uint32_t result;
+      size_t read;
+      size_t written;
+      bool hadErrors;
+      Tie(result, read, written, hadErrors) =
+        mConverter->EncodeFromUTF16(src, dst, false);
+      Unused << hadErrors;
+      src = src.From(read);
+      uint32_t streamWritten;
+      nsresult rv = mOutStream->Write(
+        reinterpret_cast<char*>(dst.Elements()), written, &streamWritten);
+      *aSuccess = NS_SUCCEEDED(rv) && written == streamWritten;
+      if (!(*aSuccess)) {
         return rv;
-    if (rv == NS_ERROR_UENC_NOMAPPING) {
-        // Yes, NS_ERROR_UENC_NOMAPPING is a success code
-        return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
+      }
+      if (result == kInputEmpty) {
+        return NS_OK;
+      }
     }
-    NS_ASSERTION((uint32_t) inLen == aCount,
-                 "Converter didn't consume all the data!");
-
-    uint32_t written;
-    rv = mOutStream->Write(buf.get(), outLen, &written);
-    *aSuccess = NS_SUCCEEDED(rv) && written == uint32_t(outLen);
-    return rv;
-
 }
 
 NS_IMETHODIMP
 nsConverterOutputStream::WriteString(const nsAString& aString, bool* aSuccess)
 {
     int32_t inLen = aString.Length();
     nsAString::const_iterator i;
     aString.BeginReading(i);
@@ -107,37 +93,37 @@ nsConverterOutputStream::WriteString(con
 }
 
 NS_IMETHODIMP
 nsConverterOutputStream::Flush()
 {
     if (!mOutStream)
         return NS_OK; // Already closed.
 
-    char buf[1024];
-    int32_t size = sizeof(buf);
-    nsresult rv = mConverter->Finish(buf, &size);
-    NS_ASSERTION(rv != NS_OK_UENC_MOREOUTPUT,
-                 "1024 bytes ought to be enough for everyone");
-    if (NS_FAILED(rv))
-        return rv;
-    if (size == 0)
-        return NS_OK;
-
-    uint32_t written;
-    rv = mOutStream->Write(buf, size, &written);
-    if (NS_FAILED(rv)) {
-        NS_WARNING("Flush() lost data!");
-        return rv;
+    // If we are encoding to ISO-2022-JP, potentially
+    // transition back to the ASCII state. The buffer
+    // needs to be large enough for an additional NCR,
+    // though.
+    uint8_t buffer[12];
+    auto dst = MakeSpan(buffer);
+    Span<char16_t> src(nullptr);
+    uint32_t result;
+    size_t read;
+    size_t written;
+    bool hadErrors;
+    Tie(result, read, written, hadErrors) =
+      mConverter->EncodeFromUTF16(src, dst, true);
+    Unused << hadErrors;
+    MOZ_ASSERT(result == kInputEmpty);
+    uint32_t streamWritten;
+    if (!written) {
+      return NS_OK;
     }
-    if (written != uint32_t(size)) {
-        NS_WARNING("Flush() lost data!");
-        return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
-    }
-    return rv;
+    return mOutStream->Write(
+      reinterpret_cast<char*>(dst.Elements()), written, &streamWritten);
 }
 
 NS_IMETHODIMP
 nsConverterOutputStream::Close()
 {
     if (!mOutStream)
         return NS_OK; // Already closed.
 
--- a/intl/uconv/nsConverterOutputStream.h
+++ b/intl/uconv/nsConverterOutputStream.h
@@ -4,18 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NSCONVERTEROUTPUTSTREAM_H_
 #define NSCONVERTEROUTPUTSTREAM_H_
 
 #include "nsIConverterOutputStream.h"
 #include "nsCOMPtr.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/Encoding.h"
 
-class nsIUnicodeEncoder;
 class nsIOutputStream;
 
 /* ff8780a5-bbb1-4bc5-8ee7-057e7bc5c925 */
 #define NS_CONVERTEROUTPUTSTREAM_CID \
 { 0xff8780a5, 0xbbb1, 0x4bc5, \
   { 0x8e, 0xe7, 0x05, 0x7e, 0x7b, 0xc5, 0xc9, 0x25 } }
 
 class nsConverterOutputStream final : public nsIConverterOutputStream {
@@ -24,13 +24,13 @@ class nsConverterOutputStream final : pu
 
         NS_DECL_ISUPPORTS
         NS_DECL_NSIUNICHAROUTPUTSTREAM
         NS_DECL_NSICONVERTEROUTPUTSTREAM
 
     private:
         ~nsConverterOutputStream();
 
-        nsCOMPtr<nsIUnicodeEncoder> mConverter;
+        mozilla::UniquePtr<mozilla::Encoder> mConverter;
         nsCOMPtr<nsIOutputStream>   mOutStream;
 };
 
 #endif
--- a/intl/uconv/nsIScriptableUConv.idl
+++ b/intl/uconv/nsIScriptableUConv.idl
@@ -63,21 +63,15 @@ interface nsIScriptableUnicodeConverter 
   nsIInputStream convertToInputStream(in AString aString);
 
   /**
    * Current character set.
    *
    * @throw NS_ERROR_UCONV_NOCONV
    *        The requested charset is not supported.
    */
-  attribute string charset;
+  attribute ACString charset;
 
   /**
-   * Internal use
-   *
-   * When this attribute is set, all encodings may be accessed. Alias
-   * resolution is not performed for non-Encoding Standard encodings.
-   * MailNews callers should perform alias resolution first (e.g. using
-   * nsICharsetConverterManager::getCharsetAlias()) and use the result
-   * with this API.
+   * Meaningless
    */
   attribute boolean isInternal;
 };
--- a/intl/uconv/nsITextToSubURI.idl
+++ b/intl/uconv/nsITextToSubURI.idl
@@ -10,43 +10,43 @@
 // {8B042E22-6F87-11d3-B3C8-00805F8A6670}
 #define NS_TEXTTOSUBURI_CID { 0x8b042e22, 0x6f87, 0x11d3, { 0xb3, 0xc8, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70 } }
 #define NS_ITEXTTOSUBURI_CONTRACTID "@mozilla.org/intl/texttosuburi;1"
 %}
 
 [scriptable, uuid(8B042E24-6F87-11d3-B3C8-00805F8A6670)]
 interface nsITextToSubURI : nsISupports
 {
-	string ConvertAndEscape(in string charset, in wstring text);
-	wstring UnEscapeAndConvert(in string charset, in string text);
+	ACString ConvertAndEscape(in ACString charset, in AString text);
+	AString UnEscapeAndConvert(in ACString charset, in ACString text);
 
   /**
    * Unescapes the given URI fragment (for UI purpose only)
-   * Note: 
+   * Note:
    * <ul>
-   *  <li> escaping back the result (unescaped string) is not guaranteed to 
+   *  <li> escaping back the result (unescaped string) is not guaranteed to
    *       give the original escaped string
-   *  <li> In case of a conversion error, the URI fragment (escaped) is 
+   *  <li> In case of a conversion error, the URI fragment (escaped) is
    *       assumed to be in UTF-8 and converted to AString (UTF-16)
    *  <li> In case of successful conversion any resulting character listed
    *       in network.IDN.blacklist_chars (except space) is escaped
    *  <li> Always succeeeds (callers don't need to do error checking)
    * </ul>
    *
    * @param aCharset the charset to convert from
    * @param aURIFragment the URI (or URI fragment) to unescape
    * @return Unescaped aURIFragment  converted to unicode
    */
   AString unEscapeURIForUI(in ACString aCharset, in AUTF8String aURIFragment);
 
   /**
-   * Unescapes only non ASCII characters in the given URI fragment 
-   * note: this method assumes the URI as UTF-8 and fallbacks to the given 
-   * charset if the charset is an ASCII superset 
+   * Unescapes only non ASCII characters in the given URI fragment
+   * note: this method assumes the URI as UTF-8 and fallbacks to the given
+   * charset if the charset is an ASCII superset
    *
    * @param aCharset the charset to convert from
    * @param aURIFragment the URI (or URI fragment) to unescape
    * @return Unescaped aURIFragment  converted to unicode
    * @throws NS_ERROR_UCONV_NOCONV when there is no decoder for aCharset
-   *         or error code of nsIUnicodeDecoder in case of conversion failure
+   *         or NS_ERROR_UDEC_ILLEGALINPUT in case of conversion failure
    */
   AString unEscapeNonAsciiURI(in ACString aCharset, in AUTF8String aURIFragment);
 };
--- a/intl/uconv/nsIUTF8ConverterService.idl
+++ b/intl/uconv/nsIUTF8ConverterService.idl
@@ -6,63 +6,63 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 [scriptable, uuid(249f52a3-2599-4b00-ba40-0481364831a2)]
 interface nsIUTF8ConverterService : nsISupports
 {
   /**
-   * Ensure that |aString| is encoded in UTF-8.  If not, 
+   * Ensure that |aString| is encoded in UTF-8.  If not,
    * convert to UTF-8 assuming it's encoded in |aCharset|
    * and return the converted string in UTF-8.
    *
    * @param aString a string to  ensure its UTF8ness
    * @param aCharset the charset to convert from if |aString| is not in UTF-8
-   * @param aSkipCheck determines whether or not to skip 'ASCIIness' and 
-   *        'UTF8ness' check. Set this to PR_TRUE only if you suspect that 
-   *        aString can be mistaken for ASCII / UTF-8 but is actually NOT 
+   * @param aSkipCheck determines whether or not to skip 'ASCIIness' and
+   *        'UTF8ness' check. Set this to PR_TRUE only if you suspect that
+   *        aString can be mistaken for ASCII / UTF-8 but is actually NOT
    *        in ASCII / UTF-8 so that aString has to go through the conversion.
    *        skipping ASCIIness/UTF8ness check.
    *        The most common case is the input is in 7bit non-ASCII charsets
    *        like ISO-2022-JP, HZ or UTF-7 (in its original form or
    *        a modified form used in IMAP folder names).
    * @param aAllowSubstitution when true, allow the decoder to substitute
    *        invalid input sequences by replacement characters (defaults to
    *        true)
    * @return the converted string in UTF-8.
    * @throws NS_ERROR_UCONV_NOCONV when there is no decoder for aCharset
-   *         or error code of nsIUnicodeDecoder in case of conversion failure
+   *         or NS_ERROR_UDEC_ILLEGALINPUT in case of conversion failure
    */
 
     [optional_argc]
-    AUTF8String convertStringToUTF8(in ACString aString, 
+    AUTF8String convertStringToUTF8(in ACString aString,
                                     in string   aCharset,
                                     in boolean  aSkipCheck,
                                     [optional] in boolean aAllowSubstitution);
 
 /* XXX : To-be-added. convertStringFromUTF8 */
-   
+
   /**
-   * Ensure that |aSpec| (after URL-unescaping it) is encoded in UTF-8.  
-   * If not,  convert it to UTF-8, assuming it's encoded in |aCharset|,  
+   * Ensure that |aSpec| (after URL-unescaping it) is encoded in UTF-8.
+   * If not,  convert it to UTF-8, assuming it's encoded in |aCharset|,
    * and return the result.
    *
-   * <p>Make sure that all characters outside US-ASCII in your input spec 
-   * are url-escaped if  your spec is not in UTF-8 (before url-escaping) 
+   * <p>Make sure that all characters outside US-ASCII in your input spec
+   * are url-escaped if  your spec is not in UTF-8 (before url-escaping)
    * because the presence of non-ASCII characters is <strong>blindly</strong>
    * regarded as an indication that your input spec is in unescaped UTF-8
    * and it will be returned without further processing. No valid spec
-   * going around in Mozilla code would break this assumption. 
+   * going around in Mozilla code would break this assumption.
    *
    * <p>XXX The above may change in the future depending on the usage pattern.
    *
    * @param aSpec an url-escaped URI spec to  ensure its UTF8ness
    * @param aCharset the charset to convert from if |aSpec| is not in UTF-8
    * @return the converted spec in UTF-8.
    * @throws NS_ERROR_UCONV_NOCONV when there is no decoder for aCharset
-   *         or error code of nsIUnicodeDecoder in case of conversion failure
+   *         or NS_ERROR_UDEC_ILLEGALINPUT in case of conversion failure
    */
 
-    AUTF8String convertURISpecToUTF8(in ACString aSpec, 
+    AUTF8String convertURISpecToUTF8(in ACString aSpec,
                                      in string   aCharset);
 };
 
--- a/intl/uconv/nsScriptableUConv.cpp
+++ b/intl/uconv/nsScriptableUConv.cpp
@@ -1,201 +1,219 @@
+
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsString.h"
 #include "nsIScriptableUConv.h"
 #include "nsScriptableUConv.h"
 #include "nsIStringStream.h"
 #include "nsComponentManagerUtils.h"
-#include "nsIUnicodeDecoder.h"
-#include "nsIUnicodeEncoder.h"
-#include "mozilla/dom/EncodingUtils.h"
 
-using mozilla::dom::EncodingUtils;
+using namespace mozilla;
 
 /* Implementation file */
 NS_IMPL_ISUPPORTS(nsScriptableUnicodeConverter, nsIScriptableUnicodeConverter)
 
 nsScriptableUnicodeConverter::nsScriptableUnicodeConverter()
 : mIsInternal(false)
 {
 }
 
 nsScriptableUnicodeConverter::~nsScriptableUnicodeConverter()
 {
 }
 
-nsresult
-nsScriptableUnicodeConverter::ConvertFromUnicodeWithLength(const nsAString& aSrc,
-                                                           int32_t* aOutLen,
-                                                           char **_retval)
-{
-  if (!mEncoder)
-    return NS_ERROR_FAILURE;
-
-  nsresult rv = NS_OK;
-  int32_t inLength = aSrc.Length();
-  const nsAFlatString& flatSrc = PromiseFlatString(aSrc);
-  rv = mEncoder->GetMaxLength(flatSrc.get(), inLength, aOutLen);
-  if (NS_SUCCEEDED(rv)) {
-    *_retval = (char*)malloc(*aOutLen+1);
-    if (!*_retval)
-      return NS_ERROR_OUT_OF_MEMORY;
-
-    rv = mEncoder->Convert(flatSrc.get(), &inLength, *_retval, aOutLen);
-    if (NS_SUCCEEDED(rv))
-    {
-      (*_retval)[*aOutLen] = '\0';
-      return NS_OK;
-    }
-    free(*_retval);
-  }
-  *_retval = nullptr;
-  return NS_ERROR_FAILURE;
-}
-
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::ConvertFromUnicode(const nsAString& aSrc,
                                                  nsACString& _retval)
 {
-  int32_t len;
-  char* str;
-  nsresult rv = ConvertFromUnicodeWithLength(aSrc, &len, &str);
-  if (NS_SUCCEEDED(rv)) {
-    // No Adopt on nsACString :(
-    if (!_retval.Assign(str, len, mozilla::fallible)) {
-      rv = NS_ERROR_OUT_OF_MEMORY;
-    }
-    free(str);
-  }
-  return rv;
-}
-
-nsresult
-nsScriptableUnicodeConverter::FinishWithLength(char **_retval, int32_t* aLength)
-{
   if (!mEncoder)
     return NS_ERROR_FAILURE;
 
-  int32_t finLength = 32;
+  // We can compute the length without replacement, because the
+  // the replacement is only one byte long and a mappable character
+  // would always output something, i.e. at least one byte.
+  // When encoding to ISO-2022-JP, unmappables shouldn't be able
+  // to cause more escape sequences to be emitted than the mappable
+  // worst case where every input character causes an escape into
+  // a different state.
+  CheckedInt<size_t> needed =
+    mEncoder->MaxBufferLengthFromUTF16WithoutReplacement(aSrc.Length());
+  if (!needed.isValid() || needed.value() > UINT32_MAX) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
-  *_retval = (char *)malloc(finLength);
-  if (!*_retval)
+  if (!_retval.SetLength(needed.value(), fallible)) {
     return NS_ERROR_OUT_OF_MEMORY;
+  }
 
-  nsresult rv = mEncoder->Finish(*_retval, &finLength);
-  if (NS_SUCCEEDED(rv))
-    *aLength = finLength;
-  else
-    free(*_retval);
-
-  return rv;
-
+  auto src = MakeSpan(aSrc);
+  auto dst = AsWritableBytes(MakeSpan(_retval));
+  size_t totalWritten = 0;
+  for (;;) {
+    uint32_t result;
+    size_t read;
+    size_t written;
+    Tie(result, read, written) =
+      mEncoder->EncodeFromUTF16WithoutReplacement(src, dst, false);
+    if (result != kInputEmpty && result != kOutputFull) {
+      MOZ_RELEASE_ASSERT(written < dst.Length(),
+        "Unmappables with one-byte replacement should not exceed mappable worst case.");
+      dst[written++] = '?';
+    }
+    totalWritten += written;
+    if (result == kInputEmpty) {
+      MOZ_ASSERT(totalWritten <= UINT32_MAX);
+      if (!_retval.SetLength(totalWritten, fallible)) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      return NS_OK;
+    }
+    src = src.From(read);
+    dst = dst.From(written);
+  }
 }
 
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::Finish(nsACString& _retval)
 {
   // The documentation for this method says it should be called after
   // ConvertFromUnicode(). However, our own tests called it after
   // convertFromByteArray(), i.e. when *decoding*.
   // Assuming that there exists extensions that similarly call
   // this at the wrong time, let's deal. In general, it is a design
   // error for this class to handle conversions in both directions.
   if (!mEncoder) {
     _retval.Truncate();
+    mDecoder->Encoding()->NewDecoderWithBOMRemovalInto(*mDecoder);
     return NS_OK;
   }
-  int32_t len;
-  char* str;
-  nsresult rv = FinishWithLength(&str, &len);
-  if (NS_SUCCEEDED(rv)) {
-    // No Adopt on nsACString :(
-    if (!_retval.Assign(str, len, mozilla::fallible)) {
-      rv = NS_ERROR_OUT_OF_MEMORY;
-    }
-    free(str);
-  }
-  return rv;
+  // If we are encoding to ISO-2022-JP, potentially
+  // transition back to the ASCII state. The buffer
+  // needs to be large enough for an additional NCR,
+  // though.
+  _retval.SetLength(13);
+  Span<char16_t> src(nullptr);
+  uint32_t result;
+  size_t read;
+  size_t written;
+  bool hadErrors;
+  Tie(result, read, written, hadErrors) =
+    mEncoder->EncodeFromUTF16(src, _retval, true);
+  Unused << hadErrors;
+  MOZ_ASSERT(!read);
+  MOZ_ASSERT(result == kInputEmpty);
+  _retval.SetLength(written);
+
+  mDecoder->Encoding()->NewDecoderWithBOMRemovalInto(*mDecoder);
+  mEncoder->Encoding()->NewEncoderInto(*mEncoder);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::ConvertToUnicode(const nsACString& aSrc, nsAString& _retval)
 {
-  nsACString::const_iterator i;
-  aSrc.BeginReading(i);
-  return ConvertFromByteArray(reinterpret_cast<const uint8_t*>(i.get()),
-                              aSrc.Length(),
-                              _retval);
+  return ConvertFromByteArray(
+    reinterpret_cast<const uint8_t*>(aSrc.BeginReading()),
+    aSrc.Length(),
+    _retval);
 }
 
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::ConvertFromByteArray(const uint8_t* aData,
                                                    uint32_t aCount,
                                                    nsAString& _retval)
 {
   if (!mDecoder)
     return NS_ERROR_FAILURE;
 
-  nsresult rv = NS_OK;
-  int32_t inLength = aCount;
-  int32_t outLength;
-  rv = mDecoder->GetMaxLength(reinterpret_cast<const char*>(aData),
-                              inLength, &outLength);
-  if (NS_SUCCEEDED(rv))
-  {
-    char16_t* buf = (char16_t*)malloc((outLength+1) * sizeof(char16_t));
-    if (!buf)
-      return NS_ERROR_OUT_OF_MEMORY;
+  CheckedInt<size_t> needed = mDecoder->MaxUTF16BufferLength(aCount);
+  if (!needed.isValid() || needed.value() > UINT32_MAX) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (!_retval.SetLength(needed.value(), fallible)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
-    rv = mDecoder->Convert(reinterpret_cast<const char*>(aData),
-                           &inLength, buf, &outLength);
-    if (NS_SUCCEEDED(rv))
-    {
-      buf[outLength] = 0;
-      if (!_retval.Assign(buf, outLength, mozilla::fallible)) {
-        rv = NS_ERROR_OUT_OF_MEMORY;
-      }
+  auto src = MakeSpan(aData, aCount);
+  uint32_t result;
+  size_t read;
+  size_t written;
+  bool hadErrors;
+  // The UTF-8 decoder used to throw regardless of the error behavior.
+  // Simulating the old behavior for compatibility with legacy callers.
+  // If callers want control over the behavior, they should switch to
+  // TextDecoder.
+  if (mDecoder->Encoding() == UTF_8_ENCODING) {
+    Tie(result, read, written) =
+      mDecoder->DecodeToUTF16WithoutReplacement(src, _retval, false);
+    if (result != kInputEmpty) {
+      return NS_ERROR_UDEC_ILLEGALINPUT;
     }
-    free(buf);
-    return rv;
+  } else {
+    Tie(result, read, written, hadErrors) =
+      mDecoder->DecodeToUTF16(src, _retval, false);
   }
-  return NS_ERROR_FAILURE;
-
+  MOZ_ASSERT(result == kInputEmpty);
+  MOZ_ASSERT(read == aCount);
+  MOZ_ASSERT(written <= needed.value());
+  Unused << hadErrors;
+  if (!_retval.SetLength(written, fallible)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::ConvertToByteArray(const nsAString& aString,
                                                  uint32_t* aLen,
                                                  uint8_t** _aData)
 {
-  char* data;
-  int32_t len;
-  nsresult rv = ConvertFromUnicodeWithLength(aString, &len, &data);
-  if (NS_FAILED(rv))
-    return rv;
-  nsXPIDLCString str;
-  str.Adopt(data, len); // NOTE: This uses the XPIDLCString as a byte array
+  if (!mEncoder)
+    return NS_ERROR_FAILURE;
+
+  CheckedInt<size_t> needed =
+    mEncoder->MaxBufferLengthFromUTF16WithoutReplacement(aString.Length());
+  if (!needed.isValid() || needed.value() > UINT32_MAX) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
-  rv = FinishWithLength(&data, &len);
-  if (NS_FAILED(rv))
-    return rv;
-
-  str.Append(data, len);
-  free(data);
-  // NOTE: this being a byte array, it needs no null termination
-  *_aData = reinterpret_cast<uint8_t*>(malloc(str.Length()));
-  if (!*_aData)
+  uint8_t* data = (uint8_t*)malloc(needed.value());
+  if (!data) {
     return NS_ERROR_OUT_OF_MEMORY;
-  memcpy(*_aData, str.get(), str.Length());
-  *aLen = str.Length();
-  return NS_OK;
+  }
+  auto src = MakeSpan(aString);
+  auto dst = MakeSpan(data, needed.value());
+  size_t totalWritten = 0;
+  for (;;) {
+    uint32_t result;
+    size_t read;
+    size_t written;
+    Tie(result, read, written) =
+      mEncoder->EncodeFromUTF16WithoutReplacement(src, dst, true);
+    if (result != kInputEmpty && result != kOutputFull) {
+      // There's always room for one byte in the case of
+      // an unmappable character, because otherwise
+      // we'd have gotten `kOutputFull`.
+      dst[written++] = '?';
+    }
+    totalWritten += written;
+    if (result == kInputEmpty) {
+      *_aData = data;
+      MOZ_ASSERT(totalWritten <= UINT32_MAX);
+      *aLen = totalWritten;
+      return NS_OK;
+    }
+    src = src.From(read);
+    dst = dst.From(written);
+  }
 }
 
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::ConvertToInputStream(const nsAString& aString,
                                                    nsIInputStream** _retval)
 {
   nsresult rv;
   nsCOMPtr<nsIStringInputStream> inputStream =
@@ -215,30 +233,30 @@ nsScriptableUnicodeConverter::ConvertToI
     return rv;
   }
 
   NS_ADDREF(*_retval = inputStream);
   return rv;
 }
 
 NS_IMETHODIMP
-nsScriptableUnicodeConverter::GetCharset(char * *aCharset)
+nsScriptableUnicodeConverter::GetCharset(nsACString& aCharset)
 {
-  *aCharset = ToNewCString(mCharset);
-  if (!*aCharset)
-    return NS_ERROR_OUT_OF_MEMORY;
-
+  if (!mDecoder) {
+    aCharset.Truncate();
+  } else {
+    mDecoder->Encoding()->Name(aCharset);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsScriptableUnicodeConverter::SetCharset(const char * aCharset)
+nsScriptableUnicodeConverter::SetCharset(const nsACString& aCharset)
 {
-  mCharset.Assign(aCharset);
-  return InitConverter();
+  return InitConverter(aCharset);
 }
 
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::GetIsInternal(bool *aIsInternal)
 {
   *aIsInternal = mIsInternal;
   return NS_OK;
 }
@@ -246,75 +264,23 @@ nsScriptableUnicodeConverter::GetIsInter
 NS_IMETHODIMP
 nsScriptableUnicodeConverter::SetIsInternal(const bool aIsInternal)
 {
   mIsInternal = aIsInternal;
   return NS_OK;
 }
 
 nsresult
-nsScriptableUnicodeConverter::InitConverter()
+nsScriptableUnicodeConverter::InitConverter(const nsACString& aCharset)
 {
   mEncoder = nullptr;
   mDecoder = nullptr;
 
-  nsAutoCString encoding;
-  if (mIsInternal) {
-    // For compatibility with legacy extensions, let's try to see if the label
-    // happens to be ASCII-case-insensitively an encoding. This should allow
-    // for things like "utf-7" and "x-Mac-Hebrew".
-    nsAutoCString contractId;
-    nsAutoCString label(mCharset);
-    EncodingUtils::TrimSpaceCharacters(label);
-    // Let's try in lower case if we didn't get an decoder. E.g. x-mac-ce
-    // and x-imap4-modified-utf7 are all lower case.
-    ToLowerCase(label);
-    if (label.EqualsLiteral("replacement")) {
-      // reject "replacement"
-      return NS_ERROR_UCONV_NOCONV;
-    }
-    contractId.AssignLiteral(NS_UNICODEENCODER_CONTRACTID_BASE);
-    contractId.Append(label);
-    mEncoder = do_CreateInstance(contractId.get());
-    contractId.AssignLiteral(NS_UNICODEDECODER_CONTRACTID_BASE);
-    contractId.Append(label);
-    mDecoder = do_CreateInstance(contractId.get());
-    if (!mDecoder) {
-      // The old code seemed to want both a decoder and an encoder. Since some
-      // internal encodings will be decoder-only in the future, let's relax
-      // this. Note that the other methods check mEncoder for null anyway.
-      // Let's try the upper case. E.g. UTF-7 and ISO-2022-CN have upper
-      // case Gecko-canonical names.
-      ToUpperCase(label);
-      contractId.AssignLiteral(NS_UNICODEENCODER_CONTRACTID_BASE);
-      contractId.Append(label);
-      mEncoder = do_CreateInstance(contractId.get());
-      contractId.AssignLiteral(NS_UNICODEDECODER_CONTRACTID_BASE);
-      contractId.Append(label);
-      mDecoder = do_CreateInstance(contractId.get());
-      // If still no decoder, use the normal non-internal case below.
-    }
+  auto encoding = Encoding::ForLabelNoReplacement(aCharset);
+  if (!encoding) {
+    return NS_ERROR_UCONV_NOCONV;
   }
-
-  if (!mDecoder) {
-    if (!EncodingUtils::FindEncodingForLabelNoReplacement(mCharset, encoding)) {
-      return NS_ERROR_UCONV_NOCONV;
-    }
-    mEncoder = EncodingUtils::EncoderForEncoding(encoding);
-    mDecoder = EncodingUtils::DecoderForEncoding(encoding);
+  if (!(encoding == UTF_16LE_ENCODING || encoding == UTF_16BE_ENCODING)) {
+    mEncoder = encoding->NewEncoder();
   }
-
-  // The UTF-8 decoder used to throw regardless of the error behavior.
-  // Simulating the old behavior for compatibility with legacy callers
-  // (including addons). If callers want a control over the behavior,
-  // they should switch to TextDecoder.
-  if (encoding.EqualsLiteral("UTF-8")) {
-    mDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal);
-  }
-
-  if (!mEncoder) {
-    return NS_OK;
-  }
-
-  return mEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace,
-                                          nullptr,
-                                          (char16_t)'?');
+  mDecoder = encoding->NewDecoderWithBOMRemoval();
+  return NS_OK;
 }
--- a/intl/uconv/nsScriptableUConv.h
+++ b/intl/uconv/nsScriptableUConv.h
@@ -4,37 +4,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #ifndef __nsScriptableUConv_h_
 #define __nsScriptableUConv_h_
 
 #include "nsIScriptableUConv.h"
 #include "nsCOMPtr.h"
-#include "nsIUnicodeDecoder.h"
-#include "nsIUnicodeEncoder.h"
+#include "mozilla/Encoding.h"
 
 class nsScriptableUnicodeConverter : public nsIScriptableUnicodeConverter
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISCRIPTABLEUNICODECONVERTER
 
   nsScriptableUnicodeConverter();
 
 protected:
   virtual ~nsScriptableUnicodeConverter();
 
-  nsCString mCharset;
-  nsCOMPtr<nsIUnicodeEncoder> mEncoder;
-  nsCOMPtr<nsIUnicodeDecoder> mDecoder;
+  mozilla::UniquePtr<mozilla::Encoder> mEncoder;
+  mozilla::UniquePtr<mozilla::Decoder> mDecoder;
   bool mIsInternal;
 
   nsresult FinishWithLength(char **_retval, int32_t* aLength);
   nsresult ConvertFromUnicodeWithLength(const nsAString& aSrc,
                                         int32_t* aOutLen,
                                         char **_retval);
 
-
-  nsresult InitConverter();
+  nsresult InitConverter(const nsACString& aCharset);
 };
 
 #endif
--- a/intl/uconv/nsTextToSubURI.cpp
+++ b/intl/uconv/nsTextToSubURI.cpp
@@ -1,24 +1,22 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsString.h"
-#include "nsIUnicodeEncoder.h"
-#include "nsIUnicodeDecoder.h"
 #include "nsITextToSubURI.h"
 #include "nsEscape.h"
 #include "nsTextToSubURI.h"
 #include "nsCRT.h"
-#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/Encoding.h"
 #include "mozilla/Preferences.h"
 #include "nsISupportsPrimitives.h"
 
-using mozilla::dom::EncodingUtils;
+using namespace mozilla;
 
 // Fallback value for the pref "network.IDN.blacklist_chars".
 // UnEscapeURIForUI allows unescaped space; other than that, this is
 // the same as the default "network.IDN.blacklist_chars" value.
 static const char16_t sNetworkIDNBlacklistChars[] =
 {
 /*0x0020,*/
           0x00A0, 0x00BC, 0x00BD, 0x00BE, 0x01C3, 0x02D0, 0x0337,
@@ -39,186 +37,103 @@ static const char16_t sNetworkIDNBlackli
 };
 
 nsTextToSubURI::~nsTextToSubURI()
 {
 }
 
 NS_IMPL_ISUPPORTS(nsTextToSubURI, nsITextToSubURI)
 
-NS_IMETHODIMP  nsTextToSubURI::ConvertAndEscape(
-  const char *charset, const char16_t *text, char **_retval) 
+NS_IMETHODIMP
+nsTextToSubURI::ConvertAndEscape(const nsACString& aCharset,
+                                 const nsAString& aText,
+                                 nsACString& aOut)
 {
-  if (!_retval) {
-    return NS_ERROR_NULL_POINTER;
-  }
-  *_retval = nullptr;
-  nsresult rv = NS_OK;
-  
-  if (!charset) {
-    return NS_ERROR_NULL_POINTER;
-  }
-
-  nsDependentCString label(charset);
-  nsAutoCString encoding;
-  if (!EncodingUtils::FindEncodingForLabelNoReplacement(label, encoding)) {
+  auto encoding = Encoding::ForLabelNoReplacement(aCharset);
+  if (!encoding) {
+    aOut.Truncate();
     return NS_ERROR_UCONV_NOCONV;
   }
-  nsCOMPtr<nsIUnicodeEncoder> encoder =
-    EncodingUtils::EncoderForEncoding(encoding);
-  rv = encoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nullptr, (char16_t)'?');
-  if (NS_SUCCEEDED(rv) ) {
-    char buf[256];
-    char *pBuf = buf;
-    int32_t ulen = text ? NS_strlen(text) : 0;
-    int32_t outlen = 0;
-    if (NS_SUCCEEDED(rv = encoder->GetMaxLength(text, ulen, &outlen))) {
-      if (outlen >= 256) {
-        pBuf = (char*)moz_xmalloc(outlen+1);
-      }
-      if (nullptr == pBuf) {
-        outlen = 255;
-        pBuf = buf;
-      }
-      int32_t bufLen = outlen;
-      if (NS_SUCCEEDED(rv = encoder->Convert(text,&ulen, pBuf, &outlen))) {
-        // put termination characters (e.g. ESC(B of ISO-2022-JP) if necessary
-        int32_t finLen = bufLen - outlen;
-        if (finLen > 0) {
-          if (NS_SUCCEEDED(encoder->Finish((char *)(pBuf+outlen), &finLen))) {
-            outlen += finLen;
-          }
-        }
-        *_retval = nsEscape(pBuf, outlen, nullptr, url_XPAlphas);
-        if (nullptr == *_retval) {
-          rv = NS_ERROR_OUT_OF_MEMORY;
-        }
-      }
-    }
-    if (pBuf != buf) {
-      free(pBuf);
-    }
+  nsresult rv;
+  const Encoding* actualEncoding;
+  nsAutoCString intermediate;
+  Tie(rv, actualEncoding) = encoding->Encode(aText, intermediate);
+  Unused << actualEncoding;
+  if (NS_FAILED(rv)) {
+    aOut.Truncate();
+    return rv;
   }
-  
-  return rv;
+  bool ok = NS_Escape(intermediate, aOut, url_XPAlphas);
+  if (!ok) {
+    aOut.Truncate();
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  return NS_OK;
 }
 
-NS_IMETHODIMP  nsTextToSubURI::UnEscapeAndConvert(
-  const char *charset, const char *text, char16_t **_retval) 
+NS_IMETHODIMP
+nsTextToSubURI::UnEscapeAndConvert(const nsACString& aCharset,
+                                   const nsACString& aText,
+                                   nsAString& aOut)
 {
-  if(nullptr == _retval)
-    return NS_ERROR_NULL_POINTER;
-  if(nullptr == text) {
-    // set empty string instead of returning error
-    // due to compatibility for old version
-    text = "";
-  }
-  *_retval = nullptr;
-  nsresult rv = NS_OK;
-  
-  if (!charset) {
-    return NS_ERROR_NULL_POINTER;
-  }
-
-
-  // unescape the string, unescape changes the input
-  char *unescaped = NS_strdup(text);
-  if (nullptr == unescaped)
-    return NS_ERROR_OUT_OF_MEMORY;
-  unescaped = nsUnescape(unescaped);
-  NS_ASSERTION(unescaped, "nsUnescape returned null");
-
-  nsDependentCString label(charset);
-  nsAutoCString encoding;
-  if (!EncodingUtils::FindEncodingForLabelNoReplacement(label, encoding)) {
-    free(unescaped);
+  auto encoding = Encoding::ForLabelNoReplacement(aCharset);
+  if (!encoding) {
+    aOut.Truncate();
     return NS_ERROR_UCONV_NOCONV;
   }
-  nsCOMPtr<nsIUnicodeDecoder> decoder =
-    EncodingUtils::DecoderForEncoding(encoding);
-  char16_t *pBuf = nullptr;
-  int32_t len = strlen(unescaped);
-  int32_t outlen = 0;
-  if (NS_SUCCEEDED(rv = decoder->GetMaxLength(unescaped, len, &outlen))) {
-    pBuf = (char16_t *) moz_xmalloc((outlen+1)*sizeof(char16_t));
-    if (nullptr == pBuf) {
-      rv = NS_ERROR_OUT_OF_MEMORY;
-    } else {
-      if (NS_SUCCEEDED(rv = decoder->Convert(unescaped, &len, pBuf, &outlen))) {
-        pBuf[outlen] = 0;
-        *_retval = pBuf;
-      } else {
-        free(pBuf);
-      }
-    }
+  nsAutoCString unescaped(aText);
+  NS_UnescapeURL(unescaped);
+  auto rv = encoding->DecodeWithoutBOMHandling(unescaped, aOut);
+  if (NS_SUCCEEDED(rv)) {
+    return NS_OK;
   }
-  free(unescaped);
-
   return rv;
 }
 
 static bool statefulCharset(const char *charset)
 {
   // HZ, UTF-7 and the CN and KR ISO-2022 variants are no longer in
   // mozilla-central but keeping them here just in case for the benefit of
   // comm-central.
   if (!nsCRT::strncasecmp(charset, "ISO-2022-", sizeof("ISO-2022-")-1) ||
       !nsCRT::strcasecmp(charset, "UTF-7") ||
       !nsCRT::strcasecmp(charset, "HZ-GB-2312"))
     return true;
 
   return false;
 }
 
-nsresult nsTextToSubURI::convertURItoUnicode(const nsAFlatCString &aCharset,
-                                             const nsAFlatCString &aURI, 
-                                             nsAString &_retval)
+nsresult
+nsTextToSubURI::convertURItoUnicode(const nsAFlatCString& aCharset,
+                                    const nsAFlatCString& aURI,
+                                    nsAString& aOut)
 {
   // check for 7bit encoding the data may not be ASCII after we decode
   bool isStatefulCharset = statefulCharset(aCharset.get());
 
   if (!isStatefulCharset) {
     if (IsASCII(aURI)) {
-      CopyASCIItoUTF16(aURI, _retval);
+      CopyASCIItoUTF16(aURI, aOut);
       return NS_OK;
     }
     if (IsUTF8(aURI)) {
-      CopyUTF8toUTF16(aURI, _retval);
+      CopyUTF8toUTF16(aURI, aOut);
       return NS_OK;
     }
   }
 
   // empty charset could indicate UTF-8, but aURI turns out not to be UTF-8.
   NS_ENSURE_FALSE(aCharset.IsEmpty(), NS_ERROR_INVALID_ARG);
 
-  nsAutoCString encoding;
-  if (!EncodingUtils::FindEncodingForLabelNoReplacement(aCharset, encoding)) {
+  auto encoding = Encoding::ForLabelNoReplacement(aCharset);
+  if (!encoding) {
+    aOut.Truncate();
     return NS_ERROR_UCONV_NOCONV;
   }
-  nsCOMPtr<nsIUnicodeDecoder> unicodeDecoder =
-    EncodingUtils::DecoderForEncoding(encoding);
-
-  unicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal);
-
-  int32_t srcLen = aURI.Length();
-  int32_t dstLen;
-  nsresult rv = unicodeDecoder->GetMaxLength(aURI.get(), srcLen, &dstLen);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  char16_t *ustr = (char16_t *) moz_xmalloc(dstLen * sizeof(char16_t));
-  NS_ENSURE_TRUE(ustr, NS_ERROR_OUT_OF_MEMORY);
-
-  rv = unicodeDecoder->Convert(aURI.get(), &srcLen, ustr, &dstLen);
-
-  if (NS_SUCCEEDED(rv))
-    _retval.Assign(ustr, dstLen);
-  
-  free(ustr);
-
-  return rv;
+  return encoding->DecodeWithoutBOMHandlingAndWithoutReplacement(aURI, aOut);
 }
 
 NS_IMETHODIMP  nsTextToSubURI::UnEscapeURIForUI(const nsACString & aCharset, 
                                                 const nsACString &aURIFragment, 
                                                 nsAString &_retval)
 {
   nsAutoCString unescapedSpec;
   // skip control octets (0x00 - 0x1f and 0x7f) when unescaping
--- a/intl/uconv/nsUConvModule.cpp
+++ b/intl/uconv/nsUConvModule.cpp
@@ -1,582 +1,52 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "mozilla/ModuleUtils.h"
-#include "nsEncoderDecoderUtils.h"
-#include "nsIUnicodeDecoder.h"
-#include "nsIUnicodeEncoder.h"
 
-#include "nsUConvCID.h"
 #include "nsTextToSubURI.h"
 #include "nsUTF8ConverterService.h"
 #include "nsConverterInputStream.h"
 #include "nsConverterOutputStream.h"
 #include "nsScriptableUConv.h"
 #include "nsIOutputStream.h"
 #include "nsITextToSubURI.h"
-
-#include "nsISO88591ToUnicode.h"
-#include "nsCP1252ToUnicode.h"
-#include "nsMacRomanToUnicode.h"
-#include "nsReplacementToUnicode.h"
-#include "nsUTF8ToUnicode.h"
-#include "nsUnicodeToISO88591.h"
-#include "nsUnicodeToCP1252.h"
-#include "nsUnicodeToMacRoman.h"
-#include "nsUnicodeToUTF8.h"
-
-// ucvlatin
-#include "nsUCvLatinCID.h"
-#include "nsISO88592ToUnicode.h"
-#include "nsISO88593ToUnicode.h"
-#include "nsISO88594ToUnicode.h"
-#include "nsISO88595ToUnicode.h"
-#include "nsISO88596ToUnicode.h"
-#include "nsISO88597ToUnicode.h"
-#include "nsISO88598ToUnicode.h"
-#include "nsISO88598IToUnicode.h"
-#include "nsISO885910ToUnicode.h"
-#include "nsISO885913ToUnicode.h"
-#include "nsISO885914ToUnicode.h"
-#include "nsISO885915ToUnicode.h"
-#include "nsISO885916ToUnicode.h"
-#include "nsCP1250ToUnicode.h"
-#include "nsCP1251ToUnicode.h"
-#include "nsCP1253ToUnicode.h"
-#include "nsCP1254ToUnicode.h"
-#include "nsCP1255ToUnicode.h"
-#include "nsCP1256ToUnicode.h"
-#include "nsCP1257ToUnicode.h"
-#include "nsCP1258ToUnicode.h"
-#include "nsCP874ToUnicode.h"
-#include "nsCP866ToUnicode.h"
-#include "nsKOI8RToUnicode.h"
-#include "nsKOI8UToUnicode.h"
-#include "nsMacCEToUnicode.h"
-#include "nsMacGreekToUnicode.h"
-#include "nsMacTurkishToUnicode.h"
-#include "nsMacCroatianToUnicode.h"
-#include "nsMacRomanianToUnicode.h"
-#include "nsMacCyrillicToUnicode.h"
-#include "nsMacIcelandicToUnicode.h"
-#include "nsUTF16ToUnicode.h"
-#include "nsUserDefinedToUnicode.h"
-#include "nsUnicodeToISO88592.h"
-#include "nsUnicodeToISO88593.h"
-#include "nsUnicodeToISO88594.h"
-#include "nsUnicodeToISO88595.h"
-#include "nsUnicodeToISO88596.h"
-#include "nsUnicodeToISO88597.h"
-#include "nsUnicodeToISO88598.h"
-#include "nsUnicodeToISO88598I.h"
-#include "nsUnicodeToISO885910.h"
-#include "nsUnicodeToISO885913.h"
-#include "nsUnicodeToISO885914.h"
-#include "nsUnicodeToISO885915.h"
-#include "nsUnicodeToISO885916.h"
-#include "nsUnicodeToCP1250.h"
-#include "nsUnicodeToCP1251.h"
-#include "nsUnicodeToCP1253.h"
-#include "nsUnicodeToCP1254.h"
-#include "nsUnicodeToCP1255.h"
-#include "nsUnicodeToCP1256.h"
-#include "nsUnicodeToCP1257.h"
-#include "nsUnicodeToCP1258.h"
-#include "nsUnicodeToCP874.h"
-#include "nsUnicodeToCP866.h"
-#include "nsUnicodeToKOI8R.h"
-#include "nsUnicodeToKOI8U.h"
-#include "nsUnicodeToMacCyrillic.h"
-#include "nsUnicodeToUTF16.h"
-#include "nsUnicodeToUserDefined.h"
-#include "nsMacArabicToUnicode.h"
-#include "nsMacDevanagariToUnicode.h"
-#include "nsMacFarsiToUnicode.h"
-#include "nsMacGujaratiToUnicode.h"
-#include "nsMacGurmukhiToUnicode.h"
-#include "nsMacHebrewToUnicode.h"
-
-// ucvja
-#include "nsUCVJACID.h"
-#include "nsUCVJA2CID.h"
-#include "nsUCVJADll.h"
-#include "nsJapaneseToUnicode.h"
-#include "nsUnicodeToSJIS.h"
-#include "nsUnicodeToEUCJP.h"
-#include "nsUnicodeToISO2022JP.h"
-
-// ucvtw
-#include "nsBIG5ToUnicode.h"
-#include "nsUnicodeToBIG5.h"
-
-// ucvko
-#include "nsUCvKOCID.h"
-#include "nsUCvKODll.h"
-#include "nsCP949ToUnicode.h"
-#include "nsUnicodeToCP949.h"
-
-// ucvcn
-#include "nsUCvCnCID.h"
-#include "nsGBKToUnicode.h"
-#include "nsUnicodeToGBK.h"
-#include "gbku.h"
-
-NS_CONVERTER_REGISTRY_START
-NS_UCONV_REG_UNREG("ISO-8859-1", NS_ISO88591TOUNICODE_CID, NS_UNICODETOISO88591_CID)
-NS_UCONV_REG_UNREG("windows-1252", NS_CP1252TOUNICODE_CID, NS_UNICODETOCP1252_CID)
-NS_UCONV_REG_UNREG("macintosh", NS_MACROMANTOUNICODE_CID, NS_UNICODETOMACROMAN_CID)
-NS_UCONV_REG_UNREG("UTF-8", NS_UTF8TOUNICODE_CID, NS_UNICODETOUTF8_CID)
-NS_UCONV_REG_UNREG("replacement", NS_REPLACEMENTTOUNICODE_CID, NS_UNICODETOUTF8_CID)
-
-  // ucvlatin
-NS_UCONV_REG_UNREG("ISO-8859-2", NS_ISO88592TOUNICODE_CID, NS_UNICODETOISO88592_CID)
-NS_UCONV_REG_UNREG("ISO-8859-3", NS_ISO88593TOUNICODE_CID, NS_UNICODETOISO88593_CID)
-NS_UCONV_REG_UNREG("ISO-8859-4", NS_ISO88594TOUNICODE_CID, NS_UNICODETOISO88594_CID)
-NS_UCONV_REG_UNREG("ISO-8859-5", NS_ISO88595TOUNICODE_CID, NS_UNICODETOISO88595_CID)
-NS_UCONV_REG_UNREG("ISO-8859-6", NS_ISO88596TOUNICODE_CID, NS_UNICODETOISO88596_CID)
-NS_UCONV_REG_UNREG("ISO-8859-7", NS_ISO88597TOUNICODE_CID, NS_UNICODETOISO88597_CID)
-NS_UCONV_REG_UNREG("ISO-8859-8", NS_ISO88598TOUNICODE_CID, NS_UNICODETOISO88598_CID)
-NS_UCONV_REG_UNREG("ISO-8859-8-I", NS_ISO88598ITOUNICODE_CID, NS_UNICODETOISO88598I_CID)
-NS_UCONV_REG_UNREG("ISO-8859-10", NS_ISO885910TOUNICODE_CID, NS_UNICODETOISO885910_CID)
-NS_UCONV_REG_UNREG("ISO-8859-13", NS_ISO885913TOUNICODE_CID, NS_UNICODETOISO885913_CID)
-NS_UCONV_REG_UNREG("ISO-8859-14", NS_ISO885914TOUNICODE_CID, NS_UNICODETOISO885914_CID)
-NS_UCONV_REG_UNREG("ISO-8859-15", NS_ISO885915TOUNICODE_CID, NS_UNICODETOISO885915_CID)
-NS_UCONV_REG_UNREG("ISO-8859-16", NS_ISO885916TOUNICODE_CID, NS_UNICODETOISO885916_CID)
-NS_UCONV_REG_UNREG("windows-1250", NS_CP1250TOUNICODE_CID, NS_UNICODETOCP1250_CID)
-NS_UCONV_REG_UNREG("windows-1251", NS_CP1251TOUNICODE_CID, NS_UNICODETOCP1251_CID)
-NS_UCONV_REG_UNREG("windows-1253", NS_CP1253TOUNICODE_CID, NS_UNICODETOCP1253_CID)
-NS_UCONV_REG_UNREG("windows-1254", NS_CP1254TOUNICODE_CID, NS_UNICODETOCP1254_CID)
-NS_UCONV_REG_UNREG("windows-1255", NS_CP1255TOUNICODE_CID, NS_UNICODETOCP1255_CID)
-NS_UCONV_REG_UNREG("windows-1256", NS_CP1256TOUNICODE_CID, NS_UNICODETOCP1256_CID)
-NS_UCONV_REG_UNREG("windows-1257", NS_CP1257TOUNICODE_CID, NS_UNICODETOCP1257_CID)
-NS_UCONV_REG_UNREG("windows-1258", NS_CP1258TOUNICODE_CID, NS_UNICODETOCP1258_CID)
-NS_UCONV_REG_UNREG("windows-874", NS_CP874TOUNICODE_CID, NS_UNICODETOCP874_CID)
-NS_UCONV_REG_UNREG("IBM866", NS_CP866TOUNICODE_CID, NS_UNICODETOCP866_CID)
-NS_UCONV_REG_UNREG("KOI8-R", NS_KOI8RTOUNICODE_CID, NS_UNICODETOKOI8R_CID)
-NS_UCONV_REG_UNREG("KOI8-U", NS_KOI8UTOUNICODE_CID, NS_UNICODETOKOI8U_CID)
-NS_UCONV_REG_UNREG_DECODER("x-mac-ce", NS_MACCETOUNICODE_CID)
-NS_UCONV_REG_UNREG_DECODER("x-mac-greek", NS_MACGREEKTOUNICODE_CID)
-NS_UCONV_REG_UNREG_DECODER("x-mac-turkish", NS_MACTURKISHTOUNICODE_CID)
-NS_UCONV_REG_UNREG_DECODER("x-mac-croatian", NS_MACCROATIANTOUNICODE_CID)
-NS_UCONV_REG_UNREG_DECODER("x-mac-romanian", NS_MACROMANIANTOUNICODE_CID)
-NS_UCONV_REG_UNREG("x-mac-cyrillic", NS_MACCYRILLICTOUNICODE_CID, NS_UNICODETOMACCYRILLIC_CID)
-NS_UCONV_REG_UNREG_DECODER("x-mac-icelandic", NS_MACICELANDICTOUNICODE_CID)
-NS_UCONV_REG_UNREG("UTF-16", NS_UTF16TOUNICODE_CID, NS_UNICODETOUTF16_CID)
-NS_UCONV_REG_UNREG("UTF-16BE", NS_UTF16BETOUNICODE_C