Backed out 3 changesets (bug 1393230) because it touches the servo directory.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 07 Sep 2017 09:33:53 -0400
changeset 429045 7e21f710ecec4a16f1f23e7489bbb0eb3e45e462
parent 429044 9115364cd4aa078c49bba7911069f8178e55166f
child 429046 13327b54fbd559d86d39e38244f1315754f76280
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1393230
milestone57.0a1
backs out9115364cd4aa078c49bba7911069f8178e55166f
f762f605dd83fc6331161a33e1ef5d54cafbd08d
c366bfc13e86c94389d58ffc41c9f4a3e573d406
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
Backed out 3 changesets (bug 1393230) because it touches the servo directory. Backed out changeset 9115364cd4aa (bug 1393230) Backed out changeset f762f605dd83 (bug 1393230) Backed out changeset c366bfc13e86 (bug 1393230)
build/valgrind/cross-architecture.sup
dom/ipc/ProcessHangMonitor.h
js/src/devtools/rootAnalysis/analyzeHeapWrites.js
layout/style/ServoBindings.toml
security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
servo/components/style/build_gecko.rs
toolkit/system/osxproxy/ProxyUtils.mm
toolkit/system/windowsproxy/ProxyUtils.cpp
toolkit/xre/nsGDKErrorHandler.cpp
widget/windows/IMMHandler.cpp
xpcom/io/nsLocalFileWin.cpp
xpcom/string/moz.build
xpcom/string/nsAString.h
xpcom/string/nsDependentString.cpp
xpcom/string/nsDependentString.h
xpcom/string/nsDependentSubstring.cpp
xpcom/string/nsDependentSubstring.h
xpcom/string/nsLiteralString.h
xpcom/string/nsPromiseFlatString.cpp
xpcom/string/nsPromiseFlatString.h
xpcom/string/nsString.cpp
xpcom/string/nsString.h
xpcom/string/nsStringComparator.cpp
xpcom/string/nsStringFlags.h
xpcom/string/nsStringFwd.h
xpcom/string/nsStringIterator.h
xpcom/string/nsStringObsolete.cpp
xpcom/string/nsSubstring.cpp
xpcom/string/nsSubstringTuple.cpp
xpcom/string/nsSubstringTuple.h
xpcom/string/nsTDependentString.cpp
xpcom/string/nsTDependentString.h
xpcom/string/nsTDependentSubstring.cpp
xpcom/string/nsTDependentSubstring.h
xpcom/string/nsTLiteralString.h
xpcom/string/nsTPromiseFlatString.cpp
xpcom/string/nsTPromiseFlatString.h
xpcom/string/nsTString.cpp
xpcom/string/nsTString.h
xpcom/string/nsTStringComparator.cpp
xpcom/string/nsTStringObsolete.cpp
xpcom/string/nsTStringRepr.h
xpcom/string/nsTSubstring.cpp
xpcom/string/nsTSubstring.h
xpcom/string/nsTSubstringTuple.cpp
xpcom/string/nsTSubstringTuple.h
xpcom/string/precompiled_templates.cpp
xpcom/string/string-template-def-char.h
xpcom/string/string-template-def-unichar.h
xpcom/string/string-template-undef.h
--- a/build/valgrind/cross-architecture.sup
+++ b/build/valgrind/cross-architecture.sup
@@ -10,17 +10,17 @@
    ...
    fun:_ZL9SaveToEnvPKc
    ...
 }
 {
    PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 793549.)
    Memcheck:Leak
    ...
-   fun:_ZL13SaveWordToEnvPKcRK12nsTSubstringIcE
+   fun:_ZL13SaveWordToEnvPKcRK10nsACString
    ...
 }
 {
    PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 944133.)
    Memcheck:Leak
    ...
    fun:_ZN13CrashReporter14SetRestartArgsEiPPc
    ...
--- a/dom/ipc/ProcessHangMonitor.h
+++ b/dom/ipc/ProcessHangMonitor.h
@@ -6,21 +6,21 @@
 
 #ifndef mozilla_ProcessHangMonitor_h
 #define mozilla_ProcessHangMonitor_h
 
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/Atomics.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
-#include "nsStringFwd.h"
 
 class nsIRunnable;
 class nsITabChild;
 class nsIThread;
+class nsString;
 
 namespace mozilla {
 
 namespace dom {
 class ContentParent;
 class TabParent;
 } // namespace dom
 
--- a/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
+++ b/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
@@ -440,27 +440,27 @@ function ignoreContents(entry)
             /nsTArray_Impl.*?::AppendElement/,
             /nsTArray_Impl.*?::RemoveElementsAt/,
             /nsTArray_Impl.*?::ReplaceElementsAt/,
             /nsTArray_Impl.*?::InsertElementsAt/,
             /nsTArray_Impl.*?::SetCapacity/,
             /nsTArray_base.*?::EnsureCapacity/,
             /nsTArray_base.*?::ShiftData/,
             /AutoTArray.*?::Init/,
-            /nsTSubstring<T>::SetCapacity/,
-            /nsTSubstring<T>::SetLength/,
-            /nsTSubstring<T>::Assign/,
-            /nsTSubstring<T>::Append/,
-            /nsTSubstring<T>::Replace/,
-            /nsTSubstring<T>::Trim/,
-            /nsTSubstring<T>::Truncate/,
-            /nsTSubstring<T>::StripTaggedASCII/,
-            /nsTSubstring<T>::operator=/,
-            /nsTAutoStringN<T, N>::nsTAutoStringN/,
-            /nsTFixedString<T>::nsTFixedString/,
+            /nsAC?String::SetCapacity/,
+            /nsAC?String::SetLength/,
+            /nsAC?String::Assign/,
+            /nsAC?String::Append/,
+            /nsAC?String::Replace/,
+            /nsAC?String::Trim/,
+            /nsAC?String::Truncate/,
+            /nsAString::StripTaggedASCII/,
+            /nsAC?String::operator=/,
+            /nsAutoString::nsAutoString/,
+            /nsFixedCString::nsFixedCString/,
 
             // Similar for some other data structures
             /nsCOMArray_base::SetCapacity/,
             /nsCOMArray_base::Clear/,
             /nsCOMArray_base::AppendElement/,
 
             // UniquePtr is similar.
             /mozilla::UniquePtr/,
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -338,18 +338,17 @@ mapped-generic-types = [
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
     { generic = false, gecko = "mozilla::ServoVisitedStyle", servo = "Option<::servo_arc::RawOffsetArc<::properties::ComputedValues>>" },
     { generic = false, gecko = "mozilla::ServoComputedValueFlags", servo = "::properties::computed_value_flags::ComputedValueFlags" },
     { generic = true, gecko = "mozilla::ServoRawOffsetArc", servo = "::servo_arc::RawOffsetArc" },
     { generic = false, gecko = "ServoStyleContextStrong", servo = "::gecko_bindings::sugar::ownership::Strong<::properties::ComputedValues>" },
 ]
 fixups = [
-    { pat = "\\broot::nsString\\b", rep = "::nsstring::nsStringRepr" },
-    { pat = "\\broot::nsTString<u16>", rep = "::nsstring::nsStringRepr" },
+    { pat = "root::nsString", rep = "::nsstring::nsStringRepr" },
 ]
 
 [bindings]
 headers = ["mozilla/ServoBindings.h"]
 hide-types = [
     "nsACString_internal",
     "nsAString_internal",
     "ServoStyleContextBorrowed",
@@ -537,13 +536,11 @@ servo-borrow-types = [
     "RawGeckoPropertyValuePairList",
     "RawGeckoComputedKeyframeValuesList",
     "RawGeckoFontFaceRuleList",
     "RawGeckoServoStyleRuleList",
     "RawGeckoServoAnimationValueList",
     "RawGeckoStyleChildrenIterator",
 ]
 fixups = [
-    # Remap the templated string type to the helper type
-    { pat = "\\bnsTString<u16>", rep = "nsString" },
     # hack for gecko-owned string
-    { pat = "\\b<nsString\\b", rep = "<nsStringRepr" },
+    { pat = "<nsString", rep = "<nsStringRepr" },
 ]
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -88,17 +88,17 @@ CacheDirAndAutoClear(nsIProperties* aDir
     return;
   }
 
   *cacheVar = MakeUnique<nsString>();
   ClearOnShutdown(cacheVar);
   MOZ_ALWAYS_SUCCEEDS(dirToCache->GetPath(**cacheVar));
 
   // Convert network share path to format for sandbox policy.
-  if (Substring(**cacheVar, 0, 2).Equals(NS_LITERAL_STRING("\\\\"))) {
+  if (Substring(**cacheVar, 0, 2).Equals(L"\\\\")) {
     (*cacheVar)->InsertLiteral(u"??\\UNC", 1);
   }
 }
 
 /* static */
 void
 SandboxBroker::CacheRulesDirectories()
 {
--- a/servo/components/style/build_gecko.rs
+++ b/servo/components/style/build_gecko.rs
@@ -287,17 +287,17 @@ mod bindings {
         let result = builder.generate();
         let mut result = match result {
             Ok(bindings) => bindings.to_string(),
             Err(_) => {
                 panic!("Failed to generate bindings, flags: {:?}", command_line_opts);
             },
         };
         for fixup in fixups.iter() {
-            result = Regex::new(&fixup.pat).unwrap().replace_all(&result, &*fixup.rep)
+            result = Regex::new(&format!(r"\b{}\b", fixup.pat)).unwrap().replace_all(&result, fixup.rep.as_str())
                 .into_owned().into();
         }
         let bytes = result.into_bytes();
         File::create(&out_file).unwrap().write_all(&bytes).expect("Unable to write output");
     }
 
     fn get_arc_types() -> Vec<String> {
         // Read the file
@@ -421,17 +421,17 @@ mod bindings {
                 builder.parse_callbacks(Box::new(Callbacks(map)))
             })
             .handle_table_items("mapped-generic-types", |builder, item| {
                 let generic = item["generic"].as_bool().unwrap();
                 let gecko = item["gecko"].as_str().unwrap();
                 let servo = item["servo"].as_str().unwrap();
                 let gecko_name = gecko.rsplit("::").next().unwrap();
                 fixups.push(Fixup {
-                    pat: format!("\\broot::{}\\b", gecko),
+                    pat: format!("root::{}", gecko),
                     rep: format!("::gecko_bindings::structs::{}", gecko_name)
                 });
                 builder.hide_type(gecko)
                     .raw_line(format!("pub type {0}{2} = {1}{2};", gecko_name, servo,
                                       if generic { "<T>" } else { "" }))
             })
             .get_builder();
         write_binding_file(builder, structs_file(build_type), &fixups);
--- a/toolkit/system/osxproxy/ProxyUtils.mm
+++ b/toolkit/system/osxproxy/ProxyUtils.mm
@@ -153,17 +153,17 @@ IsMatchWildcard(const nsACString& aHost,
           return true;
         }
       }
     } else {
       if (tokenEnd == -1) {
         tokenEnd = overrideLength; // no '*' char, match rest of string
       }
       nsAutoCString token(Substring(override, tokenStart, tokenEnd - tokenStart));
-      offset = host.Find(token, /* aIgnoreCase = */ false, offset);
+      offset = host.Find(token, offset);
       if (offset == -1 || (!star && offset)) {
         return false;
       }
       star = false;
       tokenStart = tokenEnd;
       offset += token.Length();
     }
   }
--- a/toolkit/system/windowsproxy/ProxyUtils.cpp
+++ b/toolkit/system/windowsproxy/ProxyUtils.cpp
@@ -153,17 +153,17 @@ IsMatchWildcard(const nsACString& aHost,
           return true;
         }
       }
     } else {
       if (tokenEnd == -1) {
         tokenEnd = overrideLength; // no '*' char, match rest of string
       }
       nsAutoCString token(Substring(override, tokenStart, tokenEnd - tokenStart));
-      offset = host.Find(token, /* aIgnoreCase = */ false, offset);
+      offset = host.Find(token, offset);
       if (offset == -1 || (!star && offset)) {
         return false;
       }
       star = false;
       tokenStart = tokenEnd;
       offset += token.Length();
     }
   }
--- a/toolkit/xre/nsGDKErrorHandler.cpp
+++ b/toolkit/xre/nsGDKErrorHandler.cpp
@@ -62,18 +62,17 @@ GdkErrorHandler(const gchar *log_domain,
       NS_RUNTIMEABORT(message);
 
     errno = 0;
     event.request_code = strtol(endptr + requestCodeString.Length(), &endptr, 10);
     if (errno)
       NS_RUNTIMEABORT(message);
 
     NS_NAMED_LITERAL_CSTRING(minorCodeString, " minor_code ");
-    start = buffer.Find(minorCodeString, /* aIgnoreCase = */ false,
-                        endptr - buffer.BeginReading());
+    start = buffer.Find(minorCodeString, endptr - buffer.BeginReading());
     if (!start)
       NS_RUNTIMEABORT(message);
 
     errno = 0;
     event.minor_code = strtol(buffer.BeginReading() + start + minorCodeString.Length(), nullptr, 10);
     if (errno)
       NS_RUNTIMEABORT(message);
 
--- a/widget/windows/IMMHandler.cpp
+++ b/widget/windows/IMMHandler.cpp
@@ -297,18 +297,18 @@ IMMHandler::IsJapanist2003Active()
 }
 
 // static
 bool
 IMMHandler::IsGoogleJapaneseInputActive()
 {
   // NOTE: Even on Windows for en-US, the name of Google Japanese Input is
   //       written in Japanese.
-  return sIMEName.Equals(u"Google \x65E5\x672C\x8A9E\x5165\x529B "
-                         u"IMM32 \x30E2\x30B8\x30E5\x30FC\x30EB");
+  return sIMEName.Equals(L"Google \x65E5\x672C\x8A9E\x5165\x529B "
+                         L"IMM32 \x30E2\x30B8\x30E5\x30FC\x30EB");
 }
 
 // static
 bool
 IMMHandler::ShouldDrawCompositionStringOurselves()
 {
   // If current IME has special UI or its composition window should not
   // positioned to caret position, we should now draw composition string
--- a/xpcom/io/nsLocalFileWin.cpp
+++ b/xpcom/io/nsLocalFileWin.cpp
@@ -1019,19 +1019,19 @@ nsLocalFile::ResolveAndStat()
   // we can't resolve/stat anything that isn't a valid NSPR addressable path
   if (mWorkingPath.IsEmpty()) {
     return NS_ERROR_FILE_INVALID_PATH;
   }
 
   // this is usually correct
   mResolvedPath.Assign(mWorkingPath);
 
-  // Make sure root paths have a trailing slash.
-  nsAutoString nsprPath(mWorkingPath);
-  if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == u':') {
+  // slutty hack designed to work around bug 134796 until it is fixed
+  nsAutoString nsprPath(mWorkingPath.get());
+  if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == L':') {
     nsprPath.Append('\\');
   }
 
   // first we will see if the working path exists. If it doesn't then
   // there is nothing more that can be done
   nsresult rv = GetFileInfo(nsprPath, &mFileInfo64);
   if (NS_FAILED(rv)) {
     return rv;
--- a/xpcom/string/moz.build
+++ b/xpcom/string/moz.build
@@ -14,46 +14,46 @@ EXPORTS += [
     'nsDependentString.h',
     'nsDependentSubstring.h',
     'nsLiteralString.h',
     'nsPrintfCString.h',
     'nsPromiseFlatString.h',
     'nsReadableUtils.h',
     'nsString.h',
     'nsStringBuffer.h',
-    'nsStringFlags.h',
     'nsStringFwd.h',
     'nsStringIterator.h',
     'nsSubstring.h',
     'nsSubstringTuple.h',
     'nsTDependentString.h',
     'nsTDependentSubstring.h',
     'nsTextFormatter.h',
     'nsTLiteralString.h',
     'nsTPromiseFlatString.h',
     'nsTString.h',
-    'nsTStringRepr.h',
     'nsTSubstring.h',
     'nsTSubstringTuple.h',
     'nsUTF8Utils.h',
+    'string-template-def-char.h',
+    'string-template-def-unichar.h',
+    'string-template-undef.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsASCIIMask.cpp',
     'nsDependentString.cpp',
     'nsDependentSubstring.cpp',
     'nsPromiseFlatString.cpp',
     'nsReadableUtils.cpp',
     'nsString.cpp',
     'nsStringComparator.cpp',
     'nsStringObsolete.cpp',
     'nsSubstring.cpp',
     'nsSubstringTuple.cpp',
     'nsTextFormatter.cpp',
-    'precompiled_templates.cpp',
 ]
 
 # Are we targeting x86 or x86-64?  If so, compile the SSE2 functions for
 # nsUTF8Utils.cpp and nsReadableUtils.cpp.
 if CONFIG['INTEL_ARCHITECTURE']:
     SOURCES += ['nsUTF8UtilsSSE2.cpp']
     SOURCES['nsUTF8UtilsSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
     SOURCES += ['nsReadableUtilsSSE2.cpp']
--- a/xpcom/string/nsAString.h
+++ b/xpcom/string/nsAString.h
@@ -12,19 +12,89 @@
 #include "nsStringIterator.h"
 #include "mozilla/TypedEnumBits.h"
 
 #include <string.h>
 #include <stdarg.h>
 
 #define kNotFound -1
 
-#include "nsStringFlags.h"
-#include "nsTStringRepr.h"
+namespace mozilla {
+namespace detail {
+// NOTE: these flags are declared public _only_ for convenience inside
+// the string implementation.  And they are outside of the string
+// class so that the type is the same for both narrow and wide
+// strings.
+
+// bits for mDataFlags
+enum class StringDataFlags : uint16_t
+{
+  // Some terminology:
+  //
+  //   "dependent buffer"    A dependent buffer is one that the string class
+  //                         does not own.  The string class relies on some
+  //                         external code to ensure the lifetime of the
+  //                         dependent buffer.
+  //
+  //   "shared buffer"       A shared buffer is one that the string class
+  //                         allocates.  When it allocates a shared string
+  //                         buffer, it allocates some additional space at
+  //                         the beginning of the buffer for additional
+  //                         fields, including a reference count and a
+  //                         buffer length.  See nsStringHeader.
+  //
+  //   "adopted buffer"      An adopted buffer is a raw string buffer
+  //                         allocated on the heap (using moz_xmalloc)
+  //                         of which the string class subsumes ownership.
+  //
+  // Some comments about the string data flags:
+  //
+  //   SHARED, OWNED, and FIXED are all mutually exlusive.  They
+  //   indicate the allocation type of mData.  If none of these flags
+  //   are set, then the string buffer is dependent.
+  //
+  //   SHARED, OWNED, or FIXED imply TERMINATED.  This is because
+  //   the string classes always allocate null-terminated buffers, and
+  //   non-terminated substrings are always dependent.
+  //
+  //   VOIDED implies TERMINATED, and moreover it implies that mData
+  //   points to char_traits::sEmptyBuffer.  Therefore, VOIDED is
+  //   mutually exclusive with SHARED, OWNED, and FIXED.
+
+  TERMINATED   = 1 << 0,  // IsTerminated returns true
+  VOIDED       = 1 << 1,  // IsVoid returns true
+  SHARED       = 1 << 2,  // mData points to a heap-allocated, shared buffer
+  OWNED        = 1 << 3,  // mData points to a heap-allocated, raw buffer
+  FIXED        = 1 << 4,  // mData points to a fixed-size writable, dependent buffer
+  LITERAL      = 1 << 5   // mData points to a string literal; DataFlags::TERMINATED will also be set
+};
+
+// bits for mClassFlags
+enum class StringClassFlags : uint16_t
+{
+  FIXED           = 1 << 0, // |this| is of type nsTFixedString
+  NULL_TERMINATED = 1 << 1  // |this| requires its buffer is null-terminated
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StringDataFlags)
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StringClassFlags)
+
+} // namespace detail
+} // namespace mozilla
+
+// declare nsAString
+#include "string-template-def-unichar.h"
 #include "nsTSubstring.h"
+#include "string-template-undef.h"
+
+// declare nsACString
+#include "string-template-def-char.h"
+#include "nsTSubstring.h"
+#include "string-template-undef.h"
+
 
 /**
  * ASCII case-insensitive comparator.  (for Unicode case-insensitive
  * comparision, see nsUnicharUtils.h)
  */
 class nsCaseInsensitiveCStringComparator
   : public nsCStringComparator
 {
--- a/xpcom/string/nsDependentString.cpp
+++ b/xpcom/string/nsDependentString.cpp
@@ -2,9 +2,17 @@
 /* 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 "nsDependentString.h"
 #include "nsAlgorithm.h"
 
+// define nsDependentString
+#include "string-template-def-unichar.h"
 #include "nsTDependentString.cpp"
+#include "string-template-undef.h"
+
+// define nsDependentCString
+#include "string-template-def-char.h"
+#include "nsTDependentString.cpp"
+#include "string-template-undef.h"
--- a/xpcom/string/nsDependentString.h
+++ b/xpcom/string/nsDependentString.h
@@ -5,12 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsDependentString_h___
 #define nsDependentString_h___
 
 #include "nsString.h"
 #include "nsDebug.h"
 
+// declare nsDependentString
+#include "string-template-def-unichar.h"
 #include "nsTDependentString.h"
+#include "string-template-undef.h"
 
+// declare nsDependentCString
+#include "string-template-def-char.h"
+#include "nsTDependentString.h"
+#include "string-template-undef.h"
 
 #endif /* !defined(nsDependentString_h___) */
--- a/xpcom/string/nsDependentSubstring.cpp
+++ b/xpcom/string/nsDependentSubstring.cpp
@@ -2,9 +2,17 @@
 /* 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 "nsDependentSubstring.h"
 #include "nsAlgorithm.h"
 
+// define nsDependentSubstring
+#include "string-template-def-unichar.h"
 #include "nsTDependentSubstring.cpp"
+#include "string-template-undef.h"
+
+// define nsDependentCSubstring
+#include "string-template-def-char.h"
+#include "nsTDependentSubstring.cpp"
+#include "string-template-undef.h"
--- a/xpcom/string/nsDependentSubstring.h
+++ b/xpcom/string/nsDependentSubstring.h
@@ -3,11 +3,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsDependentSubstring_h___
 #define nsDependentSubstring_h___
 
 #include "nsSubstring.h"
+
+// declare nsDependentSubstring
+#include "string-template-def-unichar.h"
 #include "nsTDependentSubstring.h"
+#include "string-template-undef.h"
+
+// declare nsDependentCSubstring
+#include "string-template-def-char.h"
+#include "nsTDependentSubstring.h"
+#include "string-template-undef.h"
 
 #endif /* !defined(nsDependentSubstring_h___) */
--- a/xpcom/string/nsLiteralString.h
+++ b/xpcom/string/nsLiteralString.h
@@ -5,17 +5,25 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsLiteralString_h___
 #define nsLiteralString_h___
 
 #include "nscore.h"
 #include "nsString.h"
 
+// declare nsLiteralString
+#include "string-template-def-unichar.h"
 #include "nsTLiteralString.h"
+#include "string-template-undef.h"
+
+// declare nsLiteralCString
+#include "string-template-def-char.h"
+#include "nsTLiteralString.h"
+#include "string-template-undef.h"
 
 #include "mozilla/Char16.h"
 
 #define NS_LITERAL_STRING(s)                      static_cast<const nsLiteralString&>(nsLiteralString(u"" s))
 #define NS_NAMED_LITERAL_STRING(n,s)              const nsLiteralString n(u"" s)
 
 #define NS_LITERAL_CSTRING(s)                     static_cast<const nsLiteralCString&>(nsLiteralCString("" s))
 #define NS_NAMED_LITERAL_CSTRING(n,s)             const nsLiteralCString n("" s)
--- a/xpcom/string/nsPromiseFlatString.cpp
+++ b/xpcom/string/nsPromiseFlatString.cpp
@@ -1,9 +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 "nsPromiseFlatString.h"
 
+// define nsPromiseFlatString
+#include "string-template-def-unichar.h"
 #include "nsTPromiseFlatString.cpp"
+#include "string-template-undef.h"
+
+// define nsPromiseFlatCString
+#include "string-template-def-char.h"
+#include "nsTPromiseFlatString.cpp"
+#include "string-template-undef.h"
--- a/xpcom/string/nsPromiseFlatString.h
+++ b/xpcom/string/nsPromiseFlatString.h
@@ -4,11 +4,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsPromiseFlatString_h___
 #define nsPromiseFlatString_h___
 
 #include "nsString.h"
 
+// declare nsPromiseFlatString
+#include "string-template-def-unichar.h"
 #include "nsTPromiseFlatString.h"
+#include "string-template-undef.h"
+
+// declare nsPromiseFlatCString
+#include "string-template-def-char.h"
+#include "nsTPromiseFlatString.h"
+#include "string-template-undef.h"
 
 #endif /* !defined(nsPromiseFlatString_h___) */
--- a/xpcom/string/nsString.cpp
+++ b/xpcom/string/nsString.cpp
@@ -1,9 +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 "nsString.h"
 
+// define nsString
+#include "string-template-def-unichar.h"
 #include "nsTString.cpp"
+#include "string-template-undef.h"
+
+// define nsCString
+#include "string-template-def-char.h"
+#include "nsTString.cpp"
+#include "string-template-undef.h"
--- a/xpcom/string/nsString.h
+++ b/xpcom/string/nsString.h
@@ -4,18 +4,16 @@
  * 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 nsString_h___
 #define nsString_h___
 
 #include "mozilla/Attributes.h"
 
-#include "nsStringFwd.h"
-
 #include "nsSubstring.h"
 #include "nsDependentSubstring.h"
 #include "nsReadableUtils.h"
 
 #include <new>
 
 // enable support for the obsolete string API if not explicitly disabled
 #ifndef MOZ_STRING_WITH_OBSOLETE_API
@@ -24,49 +22,50 @@
 
 #if MOZ_STRING_WITH_OBSOLETE_API
 // radix values for ToInteger/AppendInt
 #define kRadix10        (10)
 #define kRadix16        (16)
 #define kAutoDetect     (100)
 #endif
 
+
+// declare nsString, et. al.
+#include "string-template-def-unichar.h"
 #include "nsTString.h"
+#include "string-template-undef.h"
+
+// declare nsCString, et. al.
+#include "string-template-def-char.h"
+#include "nsTString.h"
+#include "string-template-undef.h"
 
 static_assert(sizeof(char16_t) == 2, "size of char16_t must be 2");
 static_assert(sizeof(nsString::char_type) == 2,
               "size of nsString::char_type must be 2");
 static_assert(nsString::char_type(-1) > nsString::char_type(0),
               "nsString::char_type must be unsigned");
 static_assert(sizeof(nsCString::char_type) == 1,
               "size of nsCString::char_type must be 1");
 
-static_assert(sizeof(nsTLiteralString<char>) == sizeof(nsTString<char>),
-              "nsLiteralCString can masquerade as nsCString, "
-              "so they must have identical layout");
-
-static_assert(sizeof(nsTLiteralString<char16_t>) == sizeof(nsTString<char16_t>),
-              "nsTLiteralString can masquerade as nsString, "
-              "so they must have identical layout");
-
 
 /**
  * A helper class that converts a UTF-16 string to ASCII in a lossy manner
  */
 class NS_LossyConvertUTF16toASCII : public nsAutoCString
 {
 public:
   explicit NS_LossyConvertUTF16toASCII(const char16ptr_t aString)
   {
     LossyAppendUTF16toASCII(aString, *this);
   }
 
   NS_LossyConvertUTF16toASCII(const char16ptr_t aString, uint32_t aLength)
   {
-    LossyAppendUTF16toASCII(Substring(static_cast<const char16_t*>(aString), aLength), *this);
+    LossyAppendUTF16toASCII(Substring(aString, aLength), *this);
   }
 
   explicit NS_LossyConvertUTF16toASCII(const nsAString& aString)
   {
     LossyAppendUTF16toASCII(aString, *this);
   }
 
 private:
@@ -107,17 +106,17 @@ class NS_ConvertUTF16toUTF8 : public nsA
 public:
   explicit NS_ConvertUTF16toUTF8(const char16ptr_t aString)
   {
     AppendUTF16toUTF8(aString, *this);
   }
 
   NS_ConvertUTF16toUTF8(const char16ptr_t aString, uint32_t aLength)
   {
-    AppendUTF16toUTF8(Substring(static_cast<const char16_t*>(aString), aLength), *this);
+    AppendUTF16toUTF8(Substring(aString, aLength), *this);
   }
 
   explicit NS_ConvertUTF16toUTF8(const nsAString& aString)
   {
     AppendUTF16toUTF8(aString, *this);
   }
 
 private:
--- a/xpcom/string/nsStringComparator.cpp
+++ b/xpcom/string/nsStringComparator.cpp
@@ -3,17 +3,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <ctype.h>
 #include "nsAString.h"
 #include "plstr.h"
 
+
+// define nsStringComparator
+#include "string-template-def-unichar.h"
 #include "nsTStringComparator.cpp"
+#include "string-template-undef.h"
+
+// define nsCStringComparator
+#include "string-template-def-char.h"
+#include "nsTStringComparator.cpp"
+#include "string-template-undef.h"
 
 
 int
 nsCaseInsensitiveCStringComparator::operator()(const char_type* aLhs,
                                                const char_type* aRhs,
                                                uint32_t aLhsLength,
                                                uint32_t aRhsLength) const
 {
deleted file mode 100644
--- a/xpcom/string/nsStringFlags.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsStringFlags_h
-#define nsStringFlags_h
-
-#include <stdint.h>
-#include "mozilla/TypedEnumBits.h"
-
-namespace mozilla {
-namespace detail {
-// NOTE: these flags are declared public _only_ for convenience inside
-// the string implementation.  And they are outside of the string
-// class so that the type is the same for both narrow and wide
-// strings.
-
-// bits for mDataFlags
-enum class StringDataFlags : uint16_t
-{
-  // Some terminology:
-  //
-  //   "dependent buffer"    A dependent buffer is one that the string class
-  //                         does not own.  The string class relies on some
-  //                         external code to ensure the lifetime of the
-  //                         dependent buffer.
-  //
-  //   "shared buffer"       A shared buffer is one that the string class
-  //                         allocates.  When it allocates a shared string
-  //                         buffer, it allocates some additional space at
-  //                         the beginning of the buffer for additional
-  //                         fields, including a reference count and a
-  //                         buffer length.  See nsStringHeader.
-  //
-  //   "adopted buffer"      An adopted buffer is a raw string buffer
-  //                         allocated on the heap (using moz_xmalloc)
-  //                         of which the string class subsumes ownership.
-  //
-  // Some comments about the string data flags:
-  //
-  //   SHARED, OWNED, and FIXED are all mutually exlusive.  They
-  //   indicate the allocation type of mData.  If none of these flags
-  //   are set, then the string buffer is dependent.
-  //
-  //   SHARED, OWNED, or FIXED imply TERMINATED.  This is because
-  //   the string classes always allocate null-terminated buffers, and
-  //   non-terminated substrings are always dependent.
-  //
-  //   VOIDED implies TERMINATED, and moreover it implies that mData
-  //   points to char_traits::sEmptyBuffer.  Therefore, VOIDED is
-  //   mutually exclusive with SHARED, OWNED, and FIXED.
-
-  TERMINATED   = 1 << 0,  // IsTerminated returns true
-  VOIDED       = 1 << 1,  // IsVoid returns true
-  SHARED       = 1 << 2,  // mData points to a heap-allocated, shared buffer
-  OWNED        = 1 << 3,  // mData points to a heap-allocated, raw buffer
-  FIXED        = 1 << 4,  // mData points to a fixed-size writable, dependent buffer
-  LITERAL      = 1 << 5   // mData points to a string literal; DataFlags::TERMINATED will also be set
-};
-
-// bits for mClassFlags
-enum class StringClassFlags : uint16_t
-{
-  FIXED           = 1 << 0, // |this| is of type nsTFixedString
-  NULL_TERMINATED = 1 << 1  // |this| requires its buffer is null-terminated
-};
-
-MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StringDataFlags)
-MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StringClassFlags)
-
-} // namespace detail
-} // namespace mozilla
-
-#endif
--- a/xpcom/string/nsStringFwd.h
+++ b/xpcom/string/nsStringFwd.h
@@ -9,67 +9,41 @@
 #ifndef nsStringFwd_h
 #define nsStringFwd_h
 
 #include "nscore.h"
 
 namespace mozilla {
 namespace detail {
 
-template <typename T> class nsTStringRepr;
-
-using nsStringRepr = nsTStringRepr<char16_t>;
-using nsCStringRepr = nsTStringRepr<char>;
+class nsStringRepr;
+class nsCStringRepr;
 
 } // namespace detail
 } // namespace mozilla
 
 static const size_t AutoStringDefaultStorageSize = 64;
 
-template <typename T> class nsTSubstring;
-template <typename T> class nsTSubstringTuple;
-template <typename T> class nsTString;
-template <typename T, size_t N> class nsTAutoStringN;
-template <typename T> class nsTDependentString;
-template <typename T> class nsTDependentSubstring;
-template <typename T> class nsTPromiseFlatString;
-template <typename T> class nsTStringComparator;
-template <typename T> class nsTDefaultStringComparator;
-template <typename T> class nsTLiteralString;
-template <typename T> class nsTFixedString;
-
-// We define this version without a size param instead of providing a
-// default value for N so that so there is a default typename that doesn't
-// require angle brackets.
-template <typename T> using nsTAutoString = nsTAutoStringN<T, AutoStringDefaultStorageSize>;
-
-
 // Double-byte (char16_t) string types.
-
-using nsAString = nsTSubstring<char16_t>;
-using nsSubstringTuple = nsTSubstringTuple<char16_t>;
-using nsString = nsTString<char16_t>;
-using nsAutoString = nsTAutoString<char16_t>;
-template <size_t N> using nsAutoStringN = nsTAutoStringN<char16_t, N>;
-using nsDependentString = nsTDependentString<char16_t>;
-using nsDependentSubstring = nsTDependentSubstring<char16_t>;
-using nsPromiseFlatString = nsTPromiseFlatString<char16_t>;
-using nsStringComparator = nsTStringComparator<char16_t>;
-using nsDefaultStringComparator = nsTDefaultStringComparator<char16_t>;
-using nsLiteralString = nsTLiteralString<char16_t>;
-using nsFixedString = nsTFixedString<char16_t>;
+class nsAString;
+class nsSubstringTuple;
+class nsString;
+template<size_t N> class nsAutoStringN;
+using nsAutoString = nsAutoStringN<AutoStringDefaultStorageSize>;
+class nsDependentString;
+class nsDependentSubstring;
+class nsPromiseFlatString;
+class nsStringComparator;
+class nsDefaultStringComparator;
 
 // Single-byte (char) string types.
-
-using nsACString = nsTSubstring<char>;
-using nsCSubstringTuple = nsTSubstringTuple<char>;
-using nsCString = nsTString<char>;
-using nsAutoCString = nsTAutoString<char>;
-template <size_t N> using nsAutoCStringN = nsTAutoStringN<char, N>;
-using nsDependentCString = nsTDependentString<char>;
-using nsDependentCSubstring = nsTDependentSubstring<char>;
-using nsPromiseFlatCString = nsTPromiseFlatString<char>;
-using nsCStringComparator = nsTStringComparator<char>;
-using nsDefaultCStringComparator = nsTDefaultStringComparator<char>;
-using nsLiteralCString = nsTLiteralString<char>;
-using nsFixedCString = nsTFixedString<char>;
+class nsACString;
+class nsCSubstringTuple;
+class nsCString;
+template<size_t N> class nsAutoCStringN;
+using nsAutoCString = nsAutoCStringN<AutoStringDefaultStorageSize>;
+class nsDependentCString;
+class nsDependentCSubstring;
+class nsPromiseFlatCString;
+class nsCStringComparator;
+class nsDefaultCStringComparator;
 
 #endif /* !defined(nsStringFwd_h) */
--- a/xpcom/string/nsStringIterator.h
+++ b/xpcom/string/nsStringIterator.h
@@ -22,17 +22,18 @@ public:
   typedef nsReadingIterator<CharT>    self_type;
   typedef ptrdiff_t                   difference_type;
   typedef size_t                      size_type;
   typedef CharT                       value_type;
   typedef const CharT*                pointer;
   typedef const CharT&                reference;
 
 private:
-  friend class mozilla::detail::nsTStringRepr<CharT>;
+  friend class mozilla::detail::nsStringRepr;
+  friend class mozilla::detail::nsCStringRepr;
 
   // unfortunately, the API for nsReadingIterator requires that the
   // iterator know its start and end positions.  this was needed when
   // we supported multi-fragment strings, but now it is really just
   // extra baggage.  we should remove mStart and mEnd at some point.
 
   const CharT* mStart;
   const CharT* mEnd;
@@ -124,17 +125,18 @@ public:
   typedef nsWritingIterator<CharT>   self_type;
   typedef ptrdiff_t                  difference_type;
   typedef size_t                     size_type;
   typedef CharT                      value_type;
   typedef CharT*                     pointer;
   typedef CharT&                     reference;
 
 private:
-  friend class nsTSubstring<CharT>;
+  friend class nsAString;
+  friend class nsACString;
 
   // unfortunately, the API for nsWritingIterator requires that the
   // iterator know its start and end positions.  this was needed when
   // we supported multi-fragment strings, but now it is really just
   // extra baggage.  we should remove mStart and mEnd at some point.
 
   CharT* mStart;
   CharT* mEnd;
--- a/xpcom/string/nsStringObsolete.cpp
+++ b/xpcom/string/nsStringObsolete.cpp
@@ -844,96 +844,88 @@ RFind_ComputeSearchRange( uint32_t bigLe
     start = 0;
 
   count = offset + littleLen - start;
   offset = start;
 }
 
 //-----------------------------------------------------------------------------
 
+// define nsString obsolete methods
+#include "string-template-def-unichar.h"
 #include "nsTStringObsolete.cpp"
+#include "string-template-undef.h"
+
+// define nsCString obsolete methods
+#include "string-template-def-char.h"
+#include "nsTStringObsolete.cpp"
+#include "string-template-undef.h"
 
 //-----------------------------------------------------------------------------
 
 // specialized methods:
 
-template <typename T>
-template <typename EnableIfChar16>
 int32_t
-nsTString<T>::Find(const self_type& aString, int32_t aOffset, int32_t aCount) const
+nsString::Find( const nsString& aString, int32_t aOffset, int32_t aCount ) const
 {
   // this method changes the meaning of aOffset and aCount:
-  Find_ComputeSearchRange(this->mLength, aString.Length(), aOffset, aCount);
+  Find_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
 
-  // Capture the raw buffer locally to help msvc deduce the type.
-  const char_type* str = aString.get();
-  int32_t result = FindSubstring(this->mData + aOffset, aCount, str, aString.Length(), false);
+  int32_t result = FindSubstring(mData + aOffset, aCount, static_cast<const char16_t*>(aString.get()), aString.Length(), false);
   if (result != kNotFound)
     result += aOffset;
   return result;
 }
 
-template <typename T>
-template <typename EnableIfChar16>
 int32_t
-nsTString<T>::Find(const char_type* aString, int32_t aOffset, int32_t aCount) const
+nsString::Find( const char16_t* aString, int32_t aOffset, int32_t aCount ) const
 {
-  return Find(nsTDependentString<T>(aString), aOffset, aCount);
+  return Find(nsDependentString(aString), aOffset, aCount);
 }
 
-template <typename T>
-template <typename EnableIfChar16>
 int32_t
-nsTString<T>::RFind(const self_type& aString, int32_t aOffset, int32_t aCount) const
+nsString::RFind( const nsString& aString, int32_t aOffset, int32_t aCount ) const
 {
   // this method changes the meaning of aOffset and aCount:
-  RFind_ComputeSearchRange(this->mLength, aString.Length(), aOffset, aCount);
+  RFind_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
 
-  // Capture the raw buffer locally to help msvc deduce the type.
-  const char_type* str = aString.get();
-  int32_t result = RFindSubstring(this->mData + aOffset, aCount, str, aString.Length(), false);
+  int32_t result = RFindSubstring(mData + aOffset, aCount, static_cast<const char16_t*>(aString.get()), aString.Length(), false);
   if (result != kNotFound)
     result += aOffset;
   return result;
 }
 
-template <typename T>
-template <typename EnableIfChar16>
 int32_t
-nsTString<T>::RFind(const char_type* aString, int32_t aOffset, int32_t aCount) const
+nsString::RFind( const char16_t* aString, int32_t aOffset, int32_t aCount ) const
 {
-  return RFind(nsTDependentString<T>(aString), aOffset, aCount);
+  return RFind(nsDependentString(aString), aOffset, aCount);
 }
 
-template <typename T>
-template <typename EnableIfChar16>
 int32_t
-nsTString<T>::FindCharInSet(const char* aSet, int32_t aOffset) const
+nsString::FindCharInSet( const char16_t* aSet, int32_t aOffset ) const
 {
   if (aOffset < 0)
     aOffset = 0;
-  else if (aOffset >= int32_t(this->mLength))
+  else if (aOffset >= int32_t(mLength))
     return kNotFound;
 
-  int32_t result = ::FindCharInSet(this->mData + aOffset, this->mLength - aOffset, aSet);
+  int32_t result = ::FindCharInSet(mData + aOffset, mLength - aOffset, aSet);
   if (result != kNotFound)
     result += aOffset;
   return result;
 }
 
-template <typename T>
-template <typename EnableIfChar16>
 void
-nsTString<T>::ReplaceChar(const char* aSet, char16_t aNewChar)
+nsString::ReplaceChar( const char16_t* aSet, char16_t aNewChar )
 {
-  if (!this->EnsureMutable()) // XXX do this lazily?
-    this->AllocFailed(this->mLength);
+  if (!EnsureMutable()) // XXX do this lazily?
+    AllocFailed(mLength);
 
-  char16_t* data = this->mData;
-  uint32_t lenRemaining = this->mLength;
+  char16_t* data = mData;
+  uint32_t lenRemaining = mLength;
 
   while (lenRemaining)
   {
     int32_t i = ::FindCharInSet(data, lenRemaining, aSet);
     if (i == kNotFound)
       break;
 
     data[i++] = aNewChar;
@@ -942,117 +934,101 @@ nsTString<T>::ReplaceChar(const char* aS
   }
 }
 
 
 /**
  * nsTString::Compare,CompareWithConversion,etc.
  */
 
-template <typename T>
-template <typename EnableIfChar>
 int32_t
-nsTString<T>::Compare(const char_type* aString, bool aIgnoreCase, int32_t aCount) const
+nsCString::Compare( const char* aString, bool aIgnoreCase, int32_t aCount ) const
 {
   uint32_t strLen = char_traits::length(aString);
 
-  int32_t maxCount = int32_t(XPCOM_MIN(this->mLength, strLen));
+  int32_t maxCount = int32_t(XPCOM_MIN(mLength, strLen));
 
   int32_t compareCount;
   if (aCount < 0 || aCount > maxCount)
     compareCount = maxCount;
   else
     compareCount = aCount;
 
   int32_t result =
-    nsBufferRoutines<T>::compare(this->mData, aString, compareCount, aIgnoreCase);
+    nsBufferRoutines<char>::compare(mData, aString, compareCount, aIgnoreCase);
 
   if (result == 0 &&
-      (aCount < 0 || strLen < uint32_t(aCount) || this->mLength < uint32_t(aCount)))
+      (aCount < 0 || strLen < uint32_t(aCount) || mLength < uint32_t(aCount)))
   {
     // Since the caller didn't give us a length to test, or strings shorter
     // than aCount, and compareCount characters matched, we have to assume
     // that the longer string is greater.
 
-    if (this->mLength != strLen)
-      result = (this->mLength < strLen) ? -1 : 1;
+    if (mLength != strLen)
+      result = (mLength < strLen) ? -1 : 1;
   }
   return result;
 }
 
-template <typename T>
-template <typename EnableIfChar16>
 bool
-nsTString<T>::EqualsIgnoreCase(const incompatible_char_type* aString, int32_t aCount) const
+nsString::EqualsIgnoreCase( const char* aString, int32_t aCount ) const
 {
   uint32_t strLen = nsCharTraits<char>::length(aString);
 
-  int32_t maxCount = int32_t(XPCOM_MIN(this->mLength, strLen));
+  int32_t maxCount = int32_t(XPCOM_MIN(mLength, strLen));
 
   int32_t compareCount;
   if (aCount < 0 || aCount > maxCount)
     compareCount = maxCount;
   else
     compareCount = aCount;
 
   int32_t result =
-    nsBufferRoutines<T>::compare(this->mData, aString, compareCount, true);
+    nsBufferRoutines<char16_t>::compare(mData, aString, compareCount, true);
 
   if (result == 0 &&
-      (aCount < 0 || strLen < uint32_t(aCount) || this->mLength < uint32_t(aCount)))
+      (aCount < 0 || strLen < uint32_t(aCount) || mLength < uint32_t(aCount)))
   {
     // Since the caller didn't give us a length to test, or strings shorter
     // than aCount, and compareCount characters matched, we have to assume
     // that the longer string is greater.
 
-    if (this->mLength != strLen)
+    if (mLength != strLen)
       result = 1; // Arbitrarily using any number != 0
   }
   return result == 0;
 }
 
 
 /**
  * nsTString::ToDouble
  */
 
-template <>
 double
-nsTString<char>::ToDouble(nsresult* aErrorCode) const
+nsCString::ToDouble(nsresult* aErrorCode) const
 {
   double res = 0.0;
-  if (this->mLength > 0)
+  if (mLength > 0)
   {
     char *conv_stopped;
-    const char *str = this->mData;
+    const char *str = mData;
     // Use PR_strtod, not strtod, since we don't want locale involved.
     res = PR_strtod(str, &conv_stopped);
-    if (conv_stopped == str+this->mLength)
+    if (conv_stopped == str+mLength)
       *aErrorCode = NS_OK;
     else // Not all the string was scanned
       *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
   }
   else
   {
     // The string was too short (0 characters)
     *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
   }
   return res;
 }
 
-template <>
 double
-nsTString<char16_t>::ToDouble(nsresult* aErrorCode) const
+nsString::ToDouble(nsresult* aErrorCode) const
 {
   return NS_LossyConvertUTF16toASCII(*this).ToDouble(aErrorCode);
 }
 
-template <typename T>
-float
-nsTString<T>::ToFloat(nsresult* aErrorCode) const
-{
-  return (float)ToDouble(aErrorCode);
-}
-
-template class nsTString<char>;
-template class nsTString<char16_t>;
-
 #endif // !MOZ_STRING_WITH_OBSOLETE_API
--- a/xpcom/string/nsSubstring.cpp
+++ b/xpcom/string/nsSubstring.cpp
@@ -347,17 +347,24 @@ size_t
 nsStringBuffer::SizeOfIncludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this);
 }
 
 // ---------------------------------------------------------------------------
 
 // define nsAString
+#include "string-template-def-unichar.h"
 #include "nsTSubstring.cpp"
+#include "string-template-undef.h"
+
+// define nsACString
+#include "string-template-def-char.h"
+#include "nsTSubstring.cpp"
+#include "string-template-undef.h"
 
 // Provide rust bindings to the nsA[C]String types
 extern "C" {
 
 // This is a no-op on release, so we ifdef it out such that using it in release
 // results in a linker error.
 #ifdef DEBUG
 void Gecko_IncrementStringAdoptCount(void* aData)
--- a/xpcom/string/nsSubstringTuple.cpp
+++ b/xpcom/string/nsSubstringTuple.cpp
@@ -1,9 +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 "nsSubstringTuple.h"
 
+// define nsSubstringTuple
+#include "string-template-def-unichar.h"
 #include "nsTSubstringTuple.cpp"
+#include "string-template-undef.h"
+
+// define nsCSubstringTuple
+#include "string-template-def-char.h"
+#include "nsTSubstringTuple.cpp"
+#include "string-template-undef.h"
--- a/xpcom/string/nsSubstringTuple.h
+++ b/xpcom/string/nsSubstringTuple.h
@@ -4,11 +4,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsSubstringTuple_h___
 #define nsSubstringTuple_h___
 
 #include "nsSubstring.h"
 
+// declare nsSubstringTuple
+#include "string-template-def-unichar.h"
 #include "nsTSubstringTuple.h"
+#include "string-template-undef.h"
+
+// declare nsCSubstringTuple
+#include "string-template-def-char.h"
+#include "nsTSubstringTuple.h"
+#include "string-template-undef.h"
 
 #endif // !defined(nsSubstringTuple_h___)
--- a/xpcom/string/nsTDependentString.cpp
+++ b/xpcom/string/nsTDependentString.cpp
@@ -1,45 +1,42 @@
 /* -*- 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/. */
 
-template <typename T>
-nsTDependentString<T>::nsTDependentString(const char_type* aStart,
-                                          const char_type* aEnd)
+nsTDependentString_CharT::nsTDependentString_CharT(const char_type* aStart,
+                                                   const char_type* aEnd)
   : string_type(const_cast<char_type*>(aStart), uint32_t(aEnd - aStart),
                 DataFlags::TERMINATED, ClassFlags(0))
 {
   MOZ_RELEASE_ASSERT(aStart <= aEnd, "Overflow!");
-  this->AssertValidDependentString();
+  AssertValidDependentString();
 }
 
-template <typename T>
 void
-nsTDependentString<T>::Rebind(const string_type& str, uint32_t startPos)
+nsTDependentString_CharT::Rebind(const string_type& str, uint32_t startPos)
 {
   MOZ_ASSERT(str.GetDataFlags() & DataFlags::TERMINATED, "Unterminated flat string");
 
   // If we currently own a buffer, release it.
-  this->Finalize();
+  Finalize();
 
   size_type strLength = str.Length();
 
   if (startPos > strLength) {
     startPos = strLength;
   }
 
   char_type* newData =
     const_cast<char_type*>(static_cast<const char_type*>(str.Data())) + startPos;
   size_type newLen = strLength - startPos;
   DataFlags newDataFlags = str.GetDataFlags() & (DataFlags::TERMINATED | DataFlags::LITERAL);
-  this->SetData(newData, newLen, newDataFlags);
+  SetData(newData, newLen, newDataFlags);
 }
 
-template <typename T>
 void
-nsTDependentString<T>::Rebind(const char_type* aStart, const char_type* aEnd)
+nsTDependentString_CharT::Rebind(const char_type* aStart, const char_type* aEnd)
 {
   MOZ_RELEASE_ASSERT(aStart <= aEnd, "Overflow!");
-  this->Rebind(aStart, uint32_t(aEnd - aStart));
+  Rebind(aStart, uint32_t(aEnd - aStart));
 }
--- a/xpcom/string/nsTDependentString.h
+++ b/xpcom/string/nsTDependentString.h
@@ -1,140 +1,99 @@
 /* -*- 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/. */
 
-#ifndef nsTDependentString_h
-#define nsTDependentString_h
-
-#include "nsTString.h"
 
 /**
- * nsTDependentString
+ * nsTDependentString_CharT
  *
  * Stores a null-terminated, immutable sequence of characters.
  *
  * Subclass of nsTString that restricts string value to an immutable
  * character sequence.  This class does not own its data, so the creator
  * of objects of this type must take care to ensure that a
  * nsTDependentString continues to reference valid memory for the
  * duration of its use.
  */
-template <typename T>
-class nsTDependentString : public nsTString<T>
+class nsTDependentString_CharT : public nsTString_CharT
 {
 public:
 
-  typedef nsTDependentString<T> self_type;
-  typedef nsTString<T> base_string_type;
-  typedef typename base_string_type::string_type string_type;
-
-  typedef typename base_string_type::fallible_t fallible_t;
-
-  typedef typename base_string_type::char_type char_type;
-  typedef typename base_string_type::char_traits char_traits;
-  typedef typename base_string_type::incompatible_char_type incompatible_char_type;
-
-  typedef typename base_string_type::substring_tuple_type substring_tuple_type;
-
-  typedef typename base_string_type::const_iterator const_iterator;
-  typedef typename base_string_type::iterator iterator;
-
-  typedef typename base_string_type::comparator_type comparator_type;
-
-  typedef typename base_string_type::char_iterator char_iterator;
-  typedef typename base_string_type::const_char_iterator const_char_iterator;
-
-  typedef typename base_string_type::index_type index_type;
-  typedef typename base_string_type::size_type size_type;
-
-  // These are only for internal use within the string classes:
-  typedef typename base_string_type::DataFlags DataFlags;
-  typedef typename base_string_type::ClassFlags ClassFlags;
-
-  using typename base_string_type::IsChar;
-  using typename base_string_type::IsChar16;
-
+  typedef nsTDependentString_CharT self_type;
 
 public:
 
   /**
    * constructors
    */
 
-  nsTDependentString(const char_type* aStart, const char_type* aEnd);
+  nsTDependentString_CharT(const char_type* aStart, const char_type* aEnd);
 
-  nsTDependentString(const char_type* aData, uint32_t aLength)
+  nsTDependentString_CharT(const char_type* aData, uint32_t aLength)
     : string_type(const_cast<char_type*>(aData), aLength,
                   DataFlags::TERMINATED, ClassFlags(0))
   {
-    this->AssertValidDependentString();
+    AssertValidDependentString();
   }
 
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
-  nsTDependentString(char16ptr_t aData, uint32_t aLength)
-    : nsTDependentString(static_cast<const char16_t*>(aData), aLength)
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+  nsTDependentString_CharT(char16ptr_t aData, uint32_t aLength)
+    : nsTDependentString_CharT(static_cast<const char16_t*>(aData), aLength)
   {
   }
 #endif
 
   explicit
-  nsTDependentString(const char_type* aData)
+  nsTDependentString_CharT(const char_type* aData)
     : string_type(const_cast<char_type*>(aData),
                   uint32_t(char_traits::length(aData)),
                   DataFlags::TERMINATED, ClassFlags(0))
   {
-    string_type::AssertValidDependentString();
+    AssertValidDependentString();
   }
 
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
   explicit
-  nsTDependentString(char16ptr_t aData)
-    : nsTDependentString(static_cast<const char16_t*>(aData))
+  nsTDependentString_CharT(char16ptr_t aData)
+    : nsTDependentString_CharT(static_cast<const char16_t*>(aData))
   {
   }
 #endif
 
-  nsTDependentString(const string_type& aStr, uint32_t aStartPos)
+  nsTDependentString_CharT(const string_type& aStr, uint32_t aStartPos)
     : string_type()
   {
     Rebind(aStr, aStartPos);
   }
 
   // Create a nsTDependentSubstring to be bound later
-  nsTDependentString()
+  nsTDependentString_CharT()
     : string_type()
   {
   }
 
   // XXX are you sure??
   // auto-generated copy-constructor OK
   // auto-generated copy-assignment operator OK
   // auto-generated destructor OK
 
 
   /**
    * allow this class to be bound to a different string...
    */
 
-  using nsTString<T>::Rebind;
+  using nsTString_CharT::Rebind;
   void Rebind(const char_type* aData)
   {
     Rebind(aData, uint32_t(char_traits::length(aData)));
   }
 
   void Rebind(const char_type* aStart, const char_type* aEnd);
   void Rebind(const string_type&, uint32_t aStartPos);
 
 private:
 
   // NOT USED
-  nsTDependentString(const substring_tuple_type&) = delete;
+  nsTDependentString_CharT(const substring_tuple_type&) = delete;
 };
-
-extern template class nsTDependentString<char>;
-extern template class nsTDependentString<char16_t>;
-
-#endif
--- a/xpcom/string/nsTDependentSubstring.cpp
+++ b/xpcom/string/nsTDependentSubstring.cpp
@@ -1,106 +1,83 @@
 /* -*- 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/. */
 
-template <typename T>
 void
-nsTDependentSubstring<T>::Rebind(const substring_type& str,
+nsTDependentSubstring_CharT::Rebind(const substring_type& str,
                                     uint32_t startPos, uint32_t length)
 {
   // If we currently own a buffer, release it.
-  this->Finalize();
+  Finalize();
 
   size_type strLength = str.Length();
 
   if (startPos > strLength) {
     startPos = strLength;
   }
 
   char_type* newData =
     const_cast<char_type*>(static_cast<const char_type*>(str.Data())) + startPos;
   size_type newLength = XPCOM_MIN(length, strLength - startPos);
   DataFlags newDataFlags = DataFlags(0);
-  this->SetData(newData, newLength, newDataFlags);
+  SetData(newData, newLength, newDataFlags);
 }
 
-template <typename T>
 void
-nsTDependentSubstring<T>::Rebind(const char_type* data, size_type length)
+nsTDependentSubstring_CharT::Rebind(const char_type* data, size_type length)
 {
   NS_ASSERTION(data, "nsTDependentSubstring must wrap a non-NULL buffer");
 
   // If we currently own a buffer, release it.
-  this->Finalize();
+  Finalize();
 
   char_type* newData =
     const_cast<char_type*>(static_cast<const char_type*>(data));
   size_type newLength = length;
   DataFlags newDataFlags = DataFlags(0);
-  this->SetData(newData, newLength, newDataFlags);
+  SetData(newData, newLength, newDataFlags);
 }
 
-template <typename T>
 void
-nsTDependentSubstring<T>::Rebind(const char_type* aStart, const char_type* aEnd)
+nsTDependentSubstring_CharT::Rebind(const char_type* aStart, const char_type* aEnd)
 {
   MOZ_RELEASE_ASSERT(aStart <= aEnd, "Overflow!");
-  this->Rebind(aStart, size_type(aEnd - aStart));
+  Rebind(aStart, size_type(aEnd - aStart));
 }
 
-template <typename T>
-nsTDependentSubstring<T>::nsTDependentSubstring(const char_type* aStart,
-                                                const char_type* aEnd)
+nsTDependentSubstring_CharT::nsTDependentSubstring_CharT(const char_type* aStart,
+                                                         const char_type* aEnd)
   : substring_type(const_cast<char_type*>(aStart), uint32_t(aEnd - aStart),
                    DataFlags(0), ClassFlags(0))
 {
   MOZ_RELEASE_ASSERT(aStart <= aEnd, "Overflow!");
 }
 
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-template <typename T>
-template <typename EnableIfChar16>
-nsTDependentSubstring<T>::nsTDependentSubstring(char16ptr_t aStart,
-                                                char16ptr_t aEnd)
-  : substring_type(static_cast<const char16_t*>(aStart),
-                   static_cast<const char16_t*>(aEnd))
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+nsTDependentSubstring_CharT::nsTDependentSubstring_CharT(char16ptr_t aStart,
+                                                         char16ptr_t aEnd)
+  : nsTDependentSubstring_CharT(static_cast<const char16_t*>(aStart),
+                                static_cast<const char16_t*>(aEnd))
 {
   MOZ_RELEASE_ASSERT(static_cast<const char16_t*>(aStart) <=
                      static_cast<const char16_t*>(aEnd),
                      "Overflow!");
 }
 #endif
 
-template <typename T>
-nsTDependentSubstring<T>::nsTDependentSubstring(const const_iterator& aStart,
-                                                const const_iterator& aEnd)
+nsTDependentSubstring_CharT::nsTDependentSubstring_CharT(const const_iterator& aStart,
+                                                         const const_iterator& aEnd)
   : substring_type(const_cast<char_type*>(aStart.get()),
                    uint32_t(aEnd.get() - aStart.get()),
                    DataFlags(0), ClassFlags(0))
 {
   MOZ_RELEASE_ASSERT(aStart.get() <= aEnd.get(), "Overflow!");
 }
 
-template <typename T>
-const nsTDependentSubstring<T>
-Substring(const T* aStart, const T* aEnd)
+const nsTDependentSubstring_CharT
+Substring(const CharT* aStart, const CharT* aEnd)
 {
   MOZ_RELEASE_ASSERT(aStart <= aEnd, "Overflow!");
-  return nsTDependentSubstring<T>(aStart, aEnd);
-}
-
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-const nsTDependentSubstring<char16_t>
-Substring(char16ptr_t aData, uint32_t aLength)
-{
-  return nsTDependentSubstring<char16_t>(aData, aLength);
+  return nsTDependentSubstring_CharT(aStart, aEnd);
 }
-
-const nsTDependentSubstring<char16_t>
-Substring(char16ptr_t aStart, char16ptr_t aEnd)
-{
-  return Substring(static_cast<const char16_t*>(aStart),
-                   static_cast<const char16_t*>(aEnd));
-}
-#endif
--- a/xpcom/string/nsTDependentSubstring.h
+++ b/xpcom/string/nsTDependentSubstring.h
@@ -1,169 +1,107 @@
 /* -*- 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/. */
 // IWYU pragma: private, include "nsString.h"
 
-#ifndef nsTDependentSubstring_h
-#define nsTDependentSubstring_h
-
-#include "nsTSubstring.h"
-#include "nsTLiteralString.h"
-
 /**
  * nsTDependentSubstring_CharT
  *
  * A string class which wraps an external array of string characters. It
  * is the client code's responsibility to ensure that the external buffer
  * remains valid for a long as the string is alive.
  *
  * NAMES:
  *   nsDependentSubstring for wide characters
  *   nsDependentCSubstring for narrow characters
  */
-template <typename T>
-class nsTDependentSubstring : public nsTSubstring<T>
+class nsTDependentSubstring_CharT : public nsTSubstring_CharT
 {
 public:
 
-  typedef nsTDependentSubstring<T> self_type;
-  typedef nsTSubstring<T> substring_type;
-  typedef typename substring_type::fallible_t fallible_t;
-
-  typedef typename substring_type::char_type char_type;
-  typedef typename substring_type::char_traits char_traits;
-  typedef typename substring_type::incompatible_char_type incompatible_char_type;
-
-  typedef typename substring_type::substring_tuple_type substring_tuple_type;
-
-  typedef typename substring_type::const_iterator const_iterator;
-  typedef typename substring_type::iterator iterator;
-
-  typedef typename substring_type::comparator_type comparator_type;
-
-  typedef typename substring_type::char_iterator char_iterator;
-  typedef typename substring_type::const_char_iterator const_char_iterator;
-
-  typedef typename substring_type::index_type index_type;
-  typedef typename substring_type::size_type size_type;
-
-  // These are only for internal use within the string classes:
-  typedef typename substring_type::DataFlags DataFlags;
-  typedef typename substring_type::ClassFlags ClassFlags;
-
-  using typename substring_type::IsChar;
-  using typename substring_type::IsChar16;
+  typedef nsTDependentSubstring_CharT self_type;
 
 public:
 
   void Rebind(const substring_type&, uint32_t aStartPos,
               uint32_t aLength = size_type(-1));
 
   void Rebind(const char_type* aData, size_type aLength);
 
   void Rebind(const char_type* aStart, const char_type* aEnd);
 
-  nsTDependentSubstring(const substring_type& aStr, uint32_t aStartPos,
-                        uint32_t aLength = size_type(-1))
+  nsTDependentSubstring_CharT(const substring_type& aStr, uint32_t aStartPos,
+                              uint32_t aLength = size_type(-1))
     : substring_type()
   {
     Rebind(aStr, aStartPos, aLength);
   }
 
-  nsTDependentSubstring(const char_type* aData, size_type aLength)
+  nsTDependentSubstring_CharT(const char_type* aData, size_type aLength)
     : substring_type(const_cast<char_type*>(aData), aLength,
                      DataFlags(0), ClassFlags(0))
   {
   }
 
-  nsTDependentSubstring(const char_type* aStart, const char_type* aEnd);
+  nsTDependentSubstring_CharT(const char_type* aStart, const char_type* aEnd);
 
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
-  nsTDependentSubstring(char16ptr_t aData, size_type aLength)
-    : nsTDependentSubstring(static_cast<const char16_t*>(aData), aLength)
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+  nsTDependentSubstring_CharT(char16ptr_t aData, size_type aLength)
+    : nsTDependentSubstring_CharT(static_cast<const char16_t*>(aData), aLength)
   {
   }
 
-  template <typename EnableIfChar16 = IsChar16>
-  nsTDependentSubstring(char16ptr_t aStart, char16ptr_t aEnd);
+  nsTDependentSubstring_CharT(char16ptr_t aStart, char16ptr_t aEnd);
 #endif
 
-  nsTDependentSubstring(const const_iterator& aStart,
-                        const const_iterator& aEnd);
+  nsTDependentSubstring_CharT(const const_iterator& aStart,
+                              const const_iterator& aEnd);
 
   // Create a nsTDependentSubstring to be bound later
-  nsTDependentSubstring()
+  nsTDependentSubstring_CharT()
     : substring_type()
   {
   }
 
   // auto-generated copy-constructor OK (XXX really?? what about base class copy-ctor?)
 
 private:
   // NOT USED
   void operator=(const self_type&);  // we're immutable, you can't assign into a substring
 };
 
-extern template class nsTDependentSubstring<char>;
-extern template class nsTDependentSubstring<char16_t>;
-
-template <typename T>
-inline const nsTDependentSubstring<T>
-Substring(const nsTSubstring<T>& aStr, uint32_t aStartPos,
+inline const nsTDependentSubstring_CharT
+Substring(const nsTSubstring_CharT& aStr, uint32_t aStartPos,
           uint32_t aLength = uint32_t(-1))
 {
-  return nsTDependentSubstring<T>(aStr, aStartPos, aLength);
+  return nsTDependentSubstring_CharT(aStr, aStartPos, aLength);
 }
 
-template <typename T>
-inline const nsTDependentSubstring<T>
-Substring(const nsTLiteralString<T>& aStr, uint32_t aStartPos,
-          uint32_t aLength = uint32_t(-1))
+inline const nsTDependentSubstring_CharT
+Substring(const nsReadingIterator<CharT>& aStart,
+          const nsReadingIterator<CharT>& aEnd)
 {
-  return nsTDependentSubstring<T>(aStr, aStartPos, aLength);
-}
-
-template <typename T>
-inline const nsTDependentSubstring<T>
-Substring(const nsReadingIterator<T>& aStart,
-          const nsReadingIterator<T>& aEnd)
-{
-  return nsTDependentSubstring<T>(aStart.get(), aEnd.get());
+  return nsTDependentSubstring_CharT(aStart.get(), aEnd.get());
 }
 
-template <typename T>
-inline const nsTDependentSubstring<T>
-Substring(const T* aData, uint32_t aLength)
+inline const nsTDependentSubstring_CharT
+Substring(const CharT* aData, uint32_t aLength)
 {
-  return nsTDependentSubstring<T>(aData, aLength);
+  return nsTDependentSubstring_CharT(aData, aLength);
 }
 
-template <typename T>
-const nsTDependentSubstring<T>
-Substring(const T* aStart, const T* aEnd);
-
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-inline const nsTDependentSubstring<char16_t>
-Substring(char16ptr_t aData, uint32_t aLength);
+const nsTDependentSubstring_CharT
+Substring(const CharT* aStart, const CharT* aEnd);
 
-const nsTDependentSubstring<char16_t>
-Substring(char16ptr_t aStart, char16ptr_t aEnd);
-#endif
-
-template <typename T>
-inline const nsTDependentSubstring<T>
-StringHead(const nsTSubstring<T>& aStr, uint32_t aCount)
+inline const nsTDependentSubstring_CharT
+StringHead(const nsTSubstring_CharT& aStr, uint32_t aCount)
 {
-  return nsTDependentSubstring<T>(aStr, 0, aCount);
+  return nsTDependentSubstring_CharT(aStr, 0, aCount);
 }
 
-template <typename T>
-inline const nsTDependentSubstring<T>
-StringTail(const nsTSubstring<T>& aStr, uint32_t aCount)
+inline const nsTDependentSubstring_CharT
+StringTail(const nsTSubstring_CharT& aStr, uint32_t aCount)
 {
-  return nsTDependentSubstring<T>(aStr, aStr.Length() - aCount, aCount);
+  return nsTDependentSubstring_CharT(aStr, aStr.Length() - aCount, aCount);
 }
-
-#endif
--- a/xpcom/string/nsTLiteralString.h
+++ b/xpcom/string/nsTLiteralString.h
@@ -1,92 +1,79 @@
 /* -*- 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/. */
 
-#ifndef nsTLiteralString_h
-#define nsTLiteralString_h
-
-#include "nsTStringRepr.h"
-
 /**
  * nsTLiteralString_CharT
  *
  * Stores a null-terminated, immutable sequence of characters.
  *
  * nsTString-lookalike that restricts its string value to a literal character
  * sequence. Can be implicitly cast to const nsTString& (the const is
  * essential, since this class's data are not writable). The data are assumed
  * to be static (permanent) and therefore, as an optimization, this class
  * does not have a destructor.
  */
-template<typename T>
-class nsTLiteralString : public mozilla::detail::nsTStringRepr<T>
+class nsTLiteralString_CharT : public mozilla::detail::nsTStringRepr_CharT
 {
 public:
 
-  typedef nsTLiteralString<T> self_type;
-  typedef typename mozilla::detail::nsTStringRepr<T>::base_string_type base_string_type;
-  typedef typename base_string_type::char_type char_type;
-  typedef typename base_string_type::size_type size_type;
-  typedef typename base_string_type::DataFlags DataFlags;
-  typedef typename base_string_type::ClassFlags ClassFlags;
+  typedef nsTLiteralString_CharT self_type;
 
 public:
 
   /**
    * constructor
    */
 
   template<size_type N>
-  explicit constexpr nsTLiteralString(const char_type (&aStr)[N])
+  explicit constexpr nsTLiteralString_CharT(const char_type (&aStr)[N])
     : base_string_type(const_cast<char_type*>(aStr), N - 1,
                        DataFlags::TERMINATED | DataFlags::LITERAL,
                        ClassFlags::NULL_TERMINATED)
   {
   }
 
   /**
    * For compatibility with existing code that requires const ns[C]String*.
    * Use sparingly. If possible, rewrite code to use const ns[C]String&
    * and the implicit cast will just work.
    */
-  const nsTString<T>& AsString() const
+  const nsTString_CharT& AsString() const
   {
-    return *reinterpret_cast<const nsTString<T>*>(this);
+    return *reinterpret_cast<const nsTString_CharT*>(this);
   }
 
-  operator const nsTString<T>&() const
+  operator const nsTString_CharT&() const
   {
     return AsString();
   }
 
-  template<typename N> struct raw_type { typedef N* type; };
-
-#ifdef MOZ_USE_CHAR16_WRAPPER
-  template<> struct raw_type<char16_t> { typedef char16ptr_t type; };
-#endif
-
   /**
    * Prohibit get() on temporaries as in nsLiteralCString("x").get().
    * These should be written as just "x", using a string literal directly.
    */
-  const typename raw_type<T>::type get() const && = delete;
-  const typename raw_type<T>::type get() const &
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+  char16ptr_t get() const && = delete;
+  char16ptr_t get() const &
+#else
+  const char_type* get() const && = delete;
+  const char_type* get() const &
+#endif
   {
-    return this->mData;
+    return mData;
   }
 
 private:
 
   // NOT TO BE IMPLEMENTED
   template<size_type N>
-  nsTLiteralString(char_type (&aStr)[N]) = delete;
+  nsTLiteralString_CharT(char_type (&aStr)[N]) = delete;
 
   self_type& operator=(const self_type&) = delete;
 };
 
-extern template class nsTLiteralString<char>;
-extern template class nsTLiteralString<char16_t>;
-
-#endif
+static_assert(sizeof(nsTLiteralString_CharT) == sizeof(nsTString_CharT),
+              "nsTLiteralString_CharT can masquerade as nsTString_CharT, "
+              "so they must have identical layout");
--- a/xpcom/string/nsTPromiseFlatString.cpp
+++ b/xpcom/string/nsTPromiseFlatString.cpp
@@ -1,23 +1,22 @@
 /* -*- 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/. */
 
-template <typename T>
 void
-nsTPromiseFlatString<T>::Init(const substring_type& str)
+nsTPromiseFlatString_CharT::Init(const substring_type& str)
 {
   if (str.IsTerminated()) {
     char_type* newData =
       const_cast<char_type*>(static_cast<const char_type*>(str.Data()));
     size_type newLength = str.Length();
     DataFlags newDataFlags =
       str.GetDataFlags() & (DataFlags::TERMINATED | DataFlags::LITERAL);
     // does not promote DataFlags::VOIDED
 
-    this->SetData(newData, newLength, newDataFlags);
+    SetData(newData, newLength, newDataFlags);
   } else {
-    this->Assign(str);
+    Assign(str);
   }
 }
--- a/xpcom/string/nsTPromiseFlatString.h
+++ b/xpcom/string/nsTPromiseFlatString.h
@@ -1,18 +1,14 @@
 /* -*- 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/. */
 
-#ifndef nsTPromiseFlatString_h
-#define nsTPromiseFlatString_h
-
-#include "nsTString.h"
 
 /**
  * NOTE:
  *
  * Try to avoid flat strings.  |PromiseFlat[C]String| will help you as a last
  * resort, and this may be necessary when dealing with legacy or OS calls,
  * but in general, requiring a null-terminated array of characters kills many
  * of the performance wins the string classes offer.  Write your own code to
@@ -63,85 +59,54 @@
  * A |nsPromiseFlat[C]String| is just a wrapper for another string.  If you
  * apply it to a string that happens to be flat, your promise is just a
  * dependent reference to the string's data.  If you apply it to a non-flat
  * string, then a temporary flat string is created for you, by allocating and
  * copying.  In the event that you end up assigning the result into a sharing
  * string (e.g., |nsTString|), the right thing happens.
  */
 
-template <typename T>
-class nsTPromiseFlatString : public nsTString<T>
+class nsTPromiseFlatString_CharT : public nsTString_CharT
 {
 public:
 
-  typedef nsTPromiseFlatString<T> self_type;
-  typedef nsTString<T> base_string_type;
-  typedef typename base_string_type::substring_type substring_type;
-  typedef typename base_string_type::string_type string_type;
-  typedef typename base_string_type::substring_tuple_type substring_tuple_type;
-  typedef typename base_string_type::char_type char_type;
-  typedef typename base_string_type::size_type size_type;
-
-  // These are only for internal use within the string classes:
-  typedef typename base_string_type::DataFlags DataFlags;
-  typedef typename base_string_type::ClassFlags ClassFlags;
+  typedef nsTPromiseFlatString_CharT self_type;
 
 private:
 
   void Init(const substring_type&);
 
   // NOT TO BE IMPLEMENTED
   void operator=(const self_type&) = delete;
 
   // NOT TO BE IMPLEMENTED
-  nsTPromiseFlatString() = delete;
+  nsTPromiseFlatString_CharT() = delete;
 
   // NOT TO BE IMPLEMENTED
-  nsTPromiseFlatString(const string_type& aStr) = delete;
+  nsTPromiseFlatString_CharT(const string_type& aStr) = delete;
 
 public:
 
   explicit
-  nsTPromiseFlatString(const substring_type& aStr)
+  nsTPromiseFlatString_CharT(const substring_type& aStr)
     : string_type()
   {
     Init(aStr);
   }
 
   explicit
-  nsTPromiseFlatString(const substring_tuple_type& aTuple)
+  nsTPromiseFlatString_CharT(const substring_tuple_type& aTuple)
     : string_type()
   {
     // nothing else to do here except assign the value of the tuple
     // into ourselves.
-    this->Assign(aTuple);
+    Assign(aTuple);
   }
 };
 
-extern template class nsTPromiseFlatString<char>;
-extern template class nsTPromiseFlatString<char16_t>;
-
 // We template this so that the constructor is chosen based on the type of the
 // parameter. This allows us to reject attempts to promise a flat flat string.
 template<class T>
-const nsTPromiseFlatString<T>
-TPromiseFlatString(const typename nsTPromiseFlatString<T>::substring_type& aString)
-{
-  return nsTPromiseFlatString<T>(aString);
-}
-
-template<class T>
-const nsTPromiseFlatString<T>
-TPromiseFlatString(const typename nsTPromiseFlatString<T>::substring_tuple_type& aString)
+const nsTPromiseFlatString_CharT
+TPromiseFlatString_CharT(const T& aString)
 {
-  return nsTPromiseFlatString<T>(aString);
+  return nsTPromiseFlatString_CharT(aString);
 }
-
-#ifndef PromiseFlatCString
-#define PromiseFlatCString TPromiseFlatString<char>
-#endif
-
-#ifndef PromiseFlatString
-#define PromiseFlatString TPromiseFlatString<char16_t>
-#endif
-
-#endif
--- a/xpcom/string/nsTString.cpp
+++ b/xpcom/string/nsTString.cpp
@@ -1,17 +1,16 @@
 /* -*- 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/. */
 
-template <typename T>
 void
-nsTString<T>::Rebind(const char_type* data, size_type length)
+nsTString_CharT::Rebind(const char_type* data, size_type length)
 {
   // If we currently own a buffer, release it.
-  this->Finalize();
+  Finalize();
 
-  this->SetData(const_cast<char_type*>(data), length, DataFlags::TERMINATED);
-  this->AssertValidDependentString();
+  SetData(const_cast<char_type*>(data), length, DataFlags::TERMINATED);
+  AssertValidDependentString();
 }
 
--- a/xpcom/string/nsTString.h
+++ b/xpcom/string/nsTString.h
@@ -1,183 +1,139 @@
 /* -*- 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/. */
 // IWYU pragma: private, include "nsString.h"
 
-#ifndef nsTString_h
-#define nsTString_h
-
-#include "nsTSubstring.h"
-
 /**
  * This is the canonical null-terminated string class.  All subclasses
  * promise null-terminated storage.  Instances of this class allocate
  * strings on the heap.
  *
  * NAMES:
  *   nsString for wide characters
  *   nsCString for narrow characters
  *
  * This class is also known as nsAFlat[C]String, where "flat" is used
  * to denote a null-terminated string.
  */
-template <typename T>
-class nsTString : public nsTSubstring<T>
+class nsTString_CharT : public nsTSubstring_CharT
 {
 public:
 
-  typedef nsTString<T> self_type;
-
-#ifdef __clang__
-  // bindgen w/ clang 3.9 at least chokes on a typedef, but using is okay.
-  using typename nsTSubstring<T>::substring_type;
-#else
-  // On the other hand msvc chokes on the using statement. It seems others
-  // don't care either way so we lump them in here.
-  typedef typename nsTSubstring<T>::substring_type substring_type;
-#endif
-
-  typedef typename substring_type::fallible_t fallible_t;
-
-  typedef typename substring_type::char_type char_type;
-  typedef typename substring_type::char_traits char_traits;
-  typedef typename substring_type::incompatible_char_type incompatible_char_type;
-
-  typedef typename substring_type::substring_tuple_type substring_tuple_type;
-
-  typedef typename substring_type::const_iterator const_iterator;
-  typedef typename substring_type::iterator iterator;
-
-  typedef typename substring_type::comparator_type comparator_type;
-
-  typedef typename substring_type::char_iterator char_iterator;
-  typedef typename substring_type::const_char_iterator const_char_iterator;
-
-  typedef typename substring_type::index_type index_type;
-  typedef typename substring_type::size_type size_type;
-
-  // These are only for internal use within the string classes:
-  typedef typename substring_type::DataFlags DataFlags;
-  typedef typename substring_type::ClassFlags ClassFlags;
-
-  using typename substring_type::IsChar;
-  using typename substring_type::IsChar16;
+  typedef nsTString_CharT self_type;
 
 public:
 
   /**
    * constructors
    */
 
-  nsTString()
+  nsTString_CharT()
     : substring_type(ClassFlags::NULL_TERMINATED)
   {
   }
 
   explicit
-  nsTString(const char_type* aData, size_type aLength = size_type(-1))
+  nsTString_CharT(const char_type* aData, size_type aLength = size_type(-1))
     : substring_type(ClassFlags::NULL_TERMINATED)
   {
-    this->Assign(aData, aLength);
+    Assign(aData, aLength);
   }
 
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
   explicit
-  nsTString(char16ptr_t aStr, size_type aLength = size_type(-1))
+  nsTString_CharT(char16ptr_t aStr, size_type aLength = size_type(-1))
     : substring_type(ClassFlags::NULL_TERMINATED)
   {
-    this->Assign(static_cast<const char16_t*>(aStr), aLength);
+    Assign(static_cast<const char16_t*>(aStr), aLength);
   }
 #endif
 
-  nsTString(const self_type& aStr)
+  nsTString_CharT(const self_type& aStr)
     : substring_type(ClassFlags::NULL_TERMINATED)
   {
-    this->Assign(aStr);
+    Assign(aStr);
   }
 
-  MOZ_IMPLICIT nsTString(const substring_tuple_type& aTuple)
+  MOZ_IMPLICIT nsTString_CharT(const substring_tuple_type& aTuple)
     : substring_type(ClassFlags::NULL_TERMINATED)
   {
-    this->Assign(aTuple);
+    Assign(aTuple);
   }
 
   explicit
-  nsTString(const substring_type& aReadable)
+  nsTString_CharT(const substring_type& aReadable)
     : substring_type(ClassFlags::NULL_TERMINATED)
   {
-    this->Assign(aReadable);
+    Assign(aReadable);
   }
 
 
   // |operator=| does not inherit, so we must define our own
   self_type& operator=(char_type aChar)
   {
-    this->Assign(aChar);
+    Assign(aChar);
     return *this;
   }
   self_type& operator=(const char_type* aData)
   {
-    this->Assign(aData);
+    Assign(aData);
     return *this;
   }
   self_type& operator=(const self_type& aStr)
   {
-    this->Assign(aStr);
+    Assign(aStr);
     return *this;
   }
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
   self_type& operator=(const char16ptr_t aStr)
   {
-    this->Assign(static_cast<const char16_t*>(aStr));
+    Assign(static_cast<const char16_t*>(aStr));
     return *this;
   }
 #endif
   self_type& operator=(const substring_type& aStr)
   {
-    this->Assign(aStr);
+    Assign(aStr);
     return *this;
   }
   self_type& operator=(const substring_tuple_type& aTuple)
   {
-    this->Assign(aTuple);
+    Assign(aTuple);
     return *this;
   }
 
   /**
    * returns the null-terminated string
    */
 
-  template <typename U> struct raw_type { typedef const U* type; };
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <> struct raw_type<char16_t> { typedef char16ptr_t type; };
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+  MOZ_NO_DANGLING_ON_TEMPORARIES char16ptr_t get() const
+#else
+  MOZ_NO_DANGLING_ON_TEMPORARIES const char_type* get() const
 #endif
-
-  MOZ_NO_DANGLING_ON_TEMPORARIES typename raw_type<T>::type get() const
   {
-    return this->mData;
+    return mData;
   }
 
 
   /**
    * returns character at specified index.
    *
    * NOTE: unlike nsTSubstring::CharAt, this function allows you to index
    *       the null terminator character.
    */
 
   char_type CharAt(index_type aIndex) const
   {
-    NS_ASSERTION(aIndex <= this->mLength, "index exceeds allowable range");
-    return this->mData[aIndex];
+    NS_ASSERTION(aIndex <= mLength, "index exceeds allowable range");
+    return mData[aIndex];
   }
 
   char_type operator[](index_type aIndex) const
   {
     return CharAt(aIndex);
   }
 
 
@@ -190,61 +146,59 @@ public:
    *  @param   aString is substring to be sought in this
    *  @param   aIgnoreCase selects case sensitivity
    *  @param   aOffset tells us where in this string to start searching
    *  @param   aCount tells us how far from the offset we are to search. Use
    *           -1 to search the whole string.
    *  @return  offset in string, or kNotFound
    */
 
-  int32_t Find(const nsTString<char>& aString, bool aIgnoreCase = false,
+  int32_t Find(const nsCString& aString, bool aIgnoreCase = false,
                int32_t aOffset = 0, int32_t aCount = -1) const;
   int32_t Find(const char* aString, bool aIgnoreCase = false,
                int32_t aOffset = 0, int32_t aCount = -1) const;
 
-  template <typename EnableIfChar16 = IsChar16>
-  int32_t Find(const self_type& aString, int32_t aOffset = 0,
+#ifdef CharT_is_PRUnichar
+  int32_t Find(const nsString& aString, int32_t aOffset = 0,
                int32_t aCount = -1) const;
-  template <typename EnableIfChar16 = IsChar16>
-  int32_t Find(const char_type* aString, int32_t aOffset = 0,
+  int32_t Find(const char16_t* aString, int32_t aOffset = 0,
                int32_t aCount = -1) const;
 #ifdef MOZ_USE_CHAR16_WRAPPER
-  template <typename EnableIfChar16 = IsChar16>
   int32_t Find(char16ptr_t aString, int32_t aOffset = 0,
                int32_t aCount = -1) const
   {
     return Find(static_cast<const char16_t*>(aString), aOffset, aCount);
   }
 #endif
+#endif
 
 
   /**
    * This methods scans the string backwards, looking for the given string
    *
    * @param   aString is substring to be sought in this
    * @param   aIgnoreCase tells us whether or not to do caseless compare
    * @param   aOffset tells us where in this string to start searching.
    *          Use -1 to search from the end of the string.
    * @param   aCount tells us how many iterations to make starting at the
    *          given offset.
    * @return  offset in string, or kNotFound
    */
 
-  // Case aIgnoreCase option only with char versions
-  int32_t RFind(const nsTString<char>& aString, bool aIgnoreCase = false,
+  int32_t RFind(const nsCString& aString, bool aIgnoreCase = false,
                 int32_t aOffset = -1, int32_t aCount = -1) const;
   int32_t RFind(const char* aCString, bool aIgnoreCase = false,
                 int32_t aOffset = -1, int32_t aCount = -1) const;
 
-  template <typename EnableIfChar16 = IsChar16>
-  int32_t RFind(const self_type& aString, int32_t aOffset = -1,
+#ifdef CharT_is_PRUnichar
+  int32_t RFind(const nsString& aString, int32_t aOffset = -1,
                 int32_t aCount = -1) const;
-  template <typename EnableIfChar16 = IsChar16>
-  int32_t RFind(const char_type* aString, int32_t aOffset = -1,
+  int32_t RFind(const char16_t* aString, int32_t aOffset = -1,
                 int32_t aCount = -1) const;
+#endif
 
 
   /**
    *  Search for given char within this string
    *
    *  @param   aChar is the character to search for
    *  @param   aOffset tells us where in this string to start searching
    *  @param   aCount tells us how far from the offset we are to search.
@@ -262,24 +216,25 @@ public:
    * the given string.
    *
    * @param aString contains set of chars to be found
    * @param aOffset tells us where in this string to start searching
    *        (counting from left)
    * @return offset in string, or kNotFound
    */
 
-  int32_t FindCharInSet(const char_type* aString, int32_t aOffset = 0) const;
+  int32_t FindCharInSet(const char* aString, int32_t aOffset = 0) const;
   int32_t FindCharInSet(const self_type& aString, int32_t aOffset = 0) const
   {
     return FindCharInSet(aString.get(), aOffset);
   }
 
-  template <typename EnableIfChar16 = IsChar16>
-  int32_t FindCharInSet(const char* aSet, int32_t aOffset = 0) const;
+#ifdef CharT_is_PRUnichar
+  int32_t FindCharInSet(const char16_t* aString, int32_t aOffset = 0) const;
+#endif
 
 
   /**
    * This method searches this string for the last character found in
    * the given string.
    *
    * @param aString contains set of chars to be found
    * @param aOffset tells us where in this string to start searching
@@ -297,53 +252,61 @@ public:
   /**
    * Compares a given string to this string.
    *
    * @param   aString is the string to be compared
    * @param   aIgnoreCase tells us how to treat case
    * @param   aCount tells us how many chars to compare
    * @return  -1,0,1
    */
-  template <typename EnableIfChar = IsChar>
-  int32_t Compare(const char_type* aString, bool aIgnoreCase = false,
+
+#ifdef CharT_is_char
+  int32_t Compare(const char* aString, bool aIgnoreCase = false,
                   int32_t aCount = -1) const;
+#endif
 
 
   /**
    * Equality check between given string and this string.
    *
    * @param   aString is the string to check
    * @param   aIgnoreCase tells us how to treat case
    * @param   aCount tells us how many chars to compare
    * @return  boolean
    */
-  template <typename EnableIfChar = IsChar>
-  bool EqualsIgnoreCase(const char_type* aString, int32_t aCount = -1) const
+#ifdef CharT_is_char
+  bool EqualsIgnoreCase(const char* aString, int32_t aCount = -1) const
   {
     return Compare(aString, true, aCount) == 0;
   }
+#else
+  bool EqualsIgnoreCase(const char* aString, int32_t aCount = -1) const;
 
-  template <typename EnableIfChar16 = IsChar16>
-  bool EqualsIgnoreCase(const incompatible_char_type* aString, int32_t aCount = -1) const;
+
+#endif // !CharT_is_PRUnichar
 
   /**
    * Perform string to double-precision float conversion.
    *
    * @param   aErrorCode will contain error if one occurs
    * @return  double-precision float rep of string value
    */
   double ToDouble(nsresult* aErrorCode) const;
 
   /**
    * Perform string to single-precision float conversion.
    *
    * @param   aErrorCode will contain error if one occurs
    * @return  single-precision float rep of string value
    */
-  float ToFloat(nsresult* aErrorCode) const;
+  float ToFloat(nsresult* aErrorCode) const
+  {
+    return (float)ToDouble(aErrorCode);
+  }
+
 
   /**
    * Perform string to int conversion.
    * @param   aErrorCode will contain error if one occurs
    * @param   aRadix tells us which radix to assume; kAutoDetect tells us to determine the radix for you.
    * @return  int rep of string value, and possible (out) error code
    */
   int32_t ToInteger(nsresult* aErrorCode, uint32_t aRadix = kRadix10) const;
@@ -369,27 +332,27 @@ public:
    *
    * aReadable.Left(aWritable, 17);    // ...a member function that does the assignment
    *
    * or maybe just stamping them out in favor of |Substring|, they are just duplicate functionality
    *
    * aWritable = Substring(aReadable, 0, 17);
    */
 
-  size_type Mid(self_type& aResult, index_type aStartPos, size_type aCount) const;
+  size_type Mid(self_type& aResult, uint32_t aStartPos, uint32_t aCount) const;
 
   size_type Left(self_type& aResult, size_type aCount) const
   {
     return Mid(aResult, 0, aCount);
   }
 
   size_type Right(self_type& aResult, size_type aCount) const
   {
-    aCount = XPCOM_MIN(this->mLength, aCount);
-    return Mid(aResult, this->mLength - aCount, aCount);
+    aCount = XPCOM_MIN(mLength, aCount);
+    return Mid(aResult, mLength - aCount, aCount);
   }
 
 
   /**
    * Set a char inside this string at given index
    *
    * @param aChar is the char you want to write into this string
    * @param anIndex is the ofs where you want to write the given char
@@ -400,41 +363,39 @@ public:
 
 
   /**
    *  These methods are used to remove all occurrences of the
    *  characters found in aSet from this string.
    *
    *  @param  aSet -- characters to be cut from this
    */
-  void StripChars(const char_type* aSet);
+#ifdef CharT_is_PRUnichar
+  using nsTSubstring_CharT::StripChars;
+#endif
+  void StripChars(const char* aSet);
+  bool StripChars(const char* aSet, const fallible_t&);
 
-  template<typename EnableIfChar16 = IsChar16>
-  bool StripChars(const incompatible_char_type* aSet, const fallible_t&);
-
-  template<typename EnableIfChar16 = IsChar16>
-  void StripChars(const incompatible_char_type* aSet);
 
   /**
    *  This method strips whitespace throughout the string.
    */
   void StripWhitespace();
   bool StripWhitespace(const fallible_t&);
 
 
   /**
    *  swaps occurence of 1 string for another
    */
 
   void ReplaceChar(char_type aOldChar, char_type aNewChar);
-  void ReplaceChar(const char_type* aSet, char_type aNewChar);
-
-  template<typename EnableIfChar16 = IsChar16>
-  void ReplaceChar(const char* aSet, char16_t aNewChar);
-
+  void ReplaceChar(const char* aSet, char_type aNewChar);
+#ifdef CharT_is_PRUnichar
+  void ReplaceChar(const char16_t* aSet, char16_t aNewChar);
+#endif
   /**
    * Replace all occurrences of aTarget with aNewValue.
    * The complexity of this function is O(n+m), n being the length of the string
    * and m being the length of aNewValue.
    */
   void ReplaceSubstring(const self_type& aTarget, const self_type& aNewValue);
   void ReplaceSubstring(const char_type* aTarget, const char_type* aNewValue);
   MOZ_MUST_USE bool ReplaceSubstring(const self_type& aTarget,
@@ -478,308 +439,266 @@ public:
    */
   void Rebind(const char_type* aData, size_type aLength);
 
   /**
    * verify restrictions for dependent strings
    */
   void AssertValidDependentString()
   {
-    NS_ASSERTION(this->mData, "nsTDependentString must wrap a non-NULL buffer");
-    NS_ASSERTION(this->mLength != size_type(-1), "nsTDependentString has bogus length");
-    NS_ASSERTION(this->mData[substring_type::mLength] == 0,
+    NS_ASSERTION(mData, "nsTDependentString must wrap a non-NULL buffer");
+    NS_ASSERTION(mLength != size_type(-1), "nsTDependentString has bogus length");
+    NS_ASSERTION(mData[mLength] == 0,
                  "nsTDependentString must wrap only null-terminated strings. "
                  "You are probably looking for nsTDependentSubstring.");
   }
 
 
 protected:
 
   // allow subclasses to initialize fields directly
-  nsTString(char_type* aData, size_type aLength, DataFlags aDataFlags,
-            ClassFlags aClassFlags)
+  nsTString_CharT(char_type* aData, size_type aLength, DataFlags aDataFlags,
+                  ClassFlags aClassFlags)
     : substring_type(aData, aLength, aDataFlags,
                      aClassFlags | ClassFlags::NULL_TERMINATED)
   {
   }
 
-  friend const nsTString<char>& NullCString();
-  friend const nsTString<char16_t>& NullString();
+  friend const nsTString_CharT& TNullString_CharT();
 
   // Used by Null[C]String.
-  explicit nsTString(DataFlags aDataFlags)
+  explicit nsTString_CharT(DataFlags aDataFlags)
     : substring_type(char_traits::sEmptyBuffer, 0,
                      aDataFlags | DataFlags::TERMINATED,
                      ClassFlags::NULL_TERMINATED)
   {}
 
   struct Segment {
     uint32_t mBegin, mLength;
     Segment(uint32_t aBegin, uint32_t aLength)
       : mBegin(aBegin)
       , mLength(aLength)
     {}
   };
 };
 
-// TODO(erahm): Do something with ToDouble so that we can extern the
-// nsTString templates.
-//extern template class nsTString<char>;
-//extern template class nsTString<char16_t>;
 
-template <typename T>
-class nsTFixedString : public nsTString<T>
+class nsTFixedString_CharT : public nsTString_CharT
 {
 public:
 
-  typedef nsTFixedString<T> self_type;
-  typedef nsTFixedString<T> fixed_string_type;
-
-  typedef nsTString<T> base_string_type;
-  typedef typename base_string_type::string_type string_type;
-  typedef typename base_string_type::char_type char_type;
-  typedef typename base_string_type::char_traits char_traits;
-  typedef typename base_string_type::substring_type substring_type;
-  typedef typename base_string_type::size_type size_type;
-  typedef typename base_string_type::substring_tuple_type substring_tuple_type;
-
-  // These are only for internal use within the string classes:
-  typedef typename base_string_type::DataFlags DataFlags;
-  typedef typename base_string_type::ClassFlags ClassFlags;
-
-  using typename base_string_type::IsChar;
-  using typename base_string_type::IsChar16;
+  typedef nsTFixedString_CharT self_type;
+  typedef nsTFixedString_CharT fixed_string_type;
 
 public:
 
   /**
    * @param aData
    *        fixed-size buffer to be used by the string (the contents of
    *        this buffer may be modified by the string)
    * @param aStorageSize
    *        the size of the fixed buffer
    * @param aLength (optional)
    *        the length of the string already contained in the buffer
    */
 
-  nsTFixedString(char_type* aData, size_type aStorageSize)
+  nsTFixedString_CharT(char_type* aData, size_type aStorageSize)
     : string_type(aData, uint32_t(char_traits::length(aData)),
                   DataFlags::TERMINATED | DataFlags::FIXED,
                   ClassFlags::FIXED)
     , mFixedCapacity(aStorageSize - 1)
     , mFixedBuf(aData)
   {
   }
 
-  nsTFixedString(char_type* aData, size_type aStorageSize,
-                 size_type aLength)
+  nsTFixedString_CharT(char_type* aData, size_type aStorageSize,
+                       size_type aLength)
     : string_type(aData, aLength, DataFlags::TERMINATED | DataFlags::FIXED,
                   ClassFlags::FIXED)
     , mFixedCapacity(aStorageSize - 1)
     , mFixedBuf(aData)
   {
     // null-terminate
     mFixedBuf[aLength] = char_type(0);
   }
 
   // |operator=| does not inherit, so we must define our own
   self_type& operator=(char_type aChar)
   {
-    this->Assign(aChar);
+    Assign(aChar);
     return *this;
   }
   self_type& operator=(const char_type* aData)
   {
-    this->Assign(aData);
+    Assign(aData);
     return *this;
   }
   self_type& operator=(const substring_type& aStr)
   {
-    this->Assign(aStr);
+    Assign(aStr);
     return *this;
   }
   self_type& operator=(const substring_tuple_type& aTuple)
   {
-    this->Assign(aTuple);
+    Assign(aTuple);
     return *this;
   }
 
 protected:
 
-  friend class nsTSubstring<T>;
+  friend class nsTSubstring_CharT;
 
   size_type  mFixedCapacity;
   char_type* mFixedBuf;
 };
 
-extern template class nsTFixedString<char>;
-extern template class nsTFixedString<char>;
 
 /**
- * nsTAutoStringN
+ * nsTAutoString_CharT
  *
- * Subclass of nsTString that adds support for stack-based string
+ * Subclass of nsTString_CharT that adds support for stack-based string
  * allocation.  It is normally not a good idea to use this class on the
  * heap, because it will allocate space which may be wasted if the string
  * it contains is significantly smaller or any larger than 64 characters.
  *
  * NAMES:
- *   nsAutoStringN / nsTAutoString for wide characters
- *   nsAutoCStringN / nsTAutoCString for narrow characters
+ *   nsAutoStringN / nsAutoString for wide characters
+ *   nsAutoCStringN / nsAutoCString for narrow characters
  */
-template<typename T, size_t N>
-class MOZ_NON_MEMMOVABLE nsTAutoStringN : public nsTFixedString<T>
+template<size_t N>
+class MOZ_NON_MEMMOVABLE nsTAutoStringN_CharT : public nsTFixedString_CharT
 {
 public:
 
-  typedef nsTAutoStringN<T, N> self_type;
-
-#ifdef __clang__
-  // bindgen w/ clang 3.9 at least chokes on a typedef, but using is okay.
-  using typename nsTFixedString<T>::fixed_string_type;
-#else
-  // On the other hand msvc chokes on the using statement. It seems others
-  // don't care either way so we lump them in here.
-  typedef typename nsTFixedString<T>::fixed_string_type fixed_string_type;
-#endif
-
-  typedef typename fixed_string_type::char_type char_type;
-  typedef typename fixed_string_type::char_traits char_traits;
-  typedef typename fixed_string_type::substring_type substring_type;
-  typedef typename fixed_string_type::size_type size_type;
-  typedef typename fixed_string_type::substring_tuple_type substring_tuple_type;
-
-  using typename fixed_string_type::IsChar;
-  using typename fixed_string_type::IsChar16;
+  typedef nsTAutoStringN_CharT<N> self_type;
 
 public:
 
   /**
    * constructors
    */
 
-  nsTAutoStringN()
+  nsTAutoStringN_CharT()
     : fixed_string_type(mStorage, N, 0)
   {
   }
 
   explicit
-  nsTAutoStringN(char_type aChar)
+  nsTAutoStringN_CharT(char_type aChar)
     : fixed_string_type(mStorage, N, 0)
   {
-    this->Assign(aChar);
+    Assign(aChar);
   }
 
   explicit
-  nsTAutoStringN(const char_type* aData, size_type aLength = size_type(-1))
+  nsTAutoStringN_CharT(const char_type* aData,
+                       size_type aLength = size_type(-1))
     : fixed_string_type(mStorage, N, 0)
   {
-    this->Assign(aData, aLength);
+    Assign(aData, aLength);
   }
 
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
   explicit
-  nsTAutoStringN(char16ptr_t aData, size_type aLength = size_type(-1))
-    : nsTAutoStringN(static_cast<const char16_t*>(aData), aLength)
+  nsTAutoStringN_CharT(char16ptr_t aData, size_type aLength = size_type(-1))
+    : nsTAutoStringN_CharT(static_cast<const char16_t*>(aData), aLength)
   {
   }
 #endif
 
-  nsTAutoStringN(const self_type& aStr)
+  nsTAutoStringN_CharT(const self_type& aStr)
     : fixed_string_type(mStorage, N, 0)
   {
-    this->Assign(aStr);
+    Assign(aStr);
   }
 
   explicit
-  nsTAutoStringN(const substring_type& aStr)
+  nsTAutoStringN_CharT(const substring_type& aStr)
     : fixed_string_type(mStorage, N, 0)
   {
-    this->Assign(aStr);
+    Assign(aStr);
   }
 
-  MOZ_IMPLICIT nsTAutoStringN(const substring_tuple_type& aTuple)
+  MOZ_IMPLICIT nsTAutoStringN_CharT(const substring_tuple_type& aTuple)
     : fixed_string_type(mStorage, N, 0)
   {
-    this->Assign(aTuple);
+    Assign(aTuple);
   }
 
   // |operator=| does not inherit, so we must define our own
   self_type& operator=(char_type aChar)
   {
-    this->Assign(aChar);
+    Assign(aChar);
     return *this;
   }
   self_type& operator=(const char_type* aData)
   {
-    this->Assign(aData);
+    Assign(aData);
     return *this;
   }
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
   self_type& operator=(char16ptr_t aStr)
   {
-    this->Assign(aStr);
+    Assign(aStr);
     return *this;
   }
 #endif
   self_type& operator=(const self_type& aStr)
   {
-    this->Assign(aStr);
+    Assign(aStr);
     return *this;
   }
   self_type& operator=(const substring_type& aStr)
   {
-    this->Assign(aStr);
+    Assign(aStr);
     return *this;
   }
   self_type& operator=(const substring_tuple_type& aTuple)
   {
-    this->Assign(aTuple);
+    Assign(aTuple);
     return *this;
   }
 
   static const size_t kStorageSize = N;
 
 private:
 
   char_type mStorage[N];
 };
 
-// Externs for the most common nsTAutoStringN variations.
-extern template class nsTAutoStringN<char, 64>;
-extern template class nsTAutoStringN<char16_t, 64>;
+// We define this typedef instead of providing a default value for N so that so
+// there is a default typename that doesn't require angle brackets.
+using nsTAutoString_CharT = nsTAutoStringN_CharT<AutoStringDefaultStorageSize>;
 
 //
 // nsAutoString stores pointers into itself which are invalidated when an
 // nsTArray is resized, so nsTArray must not be instantiated with nsAutoString
 // elements!
 //
 template<class E> class nsTArrayElementTraits;
-template<typename T>
-class nsTArrayElementTraits<nsTAutoString<T>>
+template<>
+class nsTArrayElementTraits<nsTAutoString_CharT>
 {
 public:
   template<class A> struct Dont_Instantiate_nsTArray_of;
   template<class A> struct Instead_Use_nsTArray_of;
 
-  static Dont_Instantiate_nsTArray_of<nsTAutoString<T>>*
-  Construct(Instead_Use_nsTArray_of<nsTString<T>>* aE)
+  static Dont_Instantiate_nsTArray_of<nsTAutoString_CharT>*
+  Construct(Instead_Use_nsTArray_of<nsTString_CharT>* aE)
   {
     return 0;
   }
   template<class A>
-  static Dont_Instantiate_nsTArray_of<nsTAutoString<T>>*
-  Construct(Instead_Use_nsTArray_of<nsTString<T>>* aE, const A& aArg)
+  static Dont_Instantiate_nsTArray_of<nsTAutoString_CharT>*
+  Construct(Instead_Use_nsTArray_of<nsTString_CharT>* aE, const A& aArg)
   {
     return 0;
   }
-  static Dont_Instantiate_nsTArray_of<nsTAutoString<T>>*
-  Destruct(Instead_Use_nsTArray_of<nsTString<T>>* aE)
+  static Dont_Instantiate_nsTArray_of<nsTAutoString_CharT>*
+  Destruct(Instead_Use_nsTArray_of<nsTString_CharT>* aE)
   {
     return 0;
   }
 };
 
 /**
  * getter_Copies support for adopting raw string out params that are
  * heap-allocated, e.g.:
@@ -803,44 +722,41 @@ public:
  *    // This is nicer.
  *    void Elegant()
  *    {
  *      nsCString str;
  *      GetBlah(getter_Copies(str));
  *      // ...
  *    }
  */
-template <typename T>
-class MOZ_STACK_CLASS nsTGetterCopies
+class MOZ_STACK_CLASS nsTGetterCopies_CharT
 {
 public:
-  typedef T char_type;
+  typedef CharT char_type;
 
-  explicit nsTGetterCopies(nsTSubstring<T>& aStr)
+  explicit nsTGetterCopies_CharT(nsTSubstring_CharT& aStr)
     : mString(aStr)
     , mData(nullptr)
   {
   }
 
-  ~nsTGetterCopies()
+  ~nsTGetterCopies_CharT()
   {
     mString.Adopt(mData); // OK if mData is null
   }
 
   operator char_type**()
   {
     return &mData;
   }
 
 private:
-  nsTSubstring<T>& mString;
+  nsTSubstring_CharT& mString;
   char_type* mData;
 };
 
 // See the comment above nsTGetterCopies_CharT for how to use this.
-template <typename T>
-inline nsTGetterCopies<T>
-getter_Copies(nsTSubstring<T>& aString)
+inline nsTGetterCopies_CharT
+getter_Copies(nsTSubstring_CharT& aString)
 {
-  return nsTGetterCopies<T>(aString);
+  return nsTGetterCopies_CharT(aString);
 }
 
-#endif
--- a/xpcom/string/nsTStringComparator.cpp
+++ b/xpcom/string/nsTStringComparator.cpp
@@ -1,28 +1,26 @@
 /* -*- 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/. */
 
-template <typename T>
 int NS_FASTCALL
-Compare(const mozilla::detail::nsTStringRepr<T>& aLhs,
-        const mozilla::detail::nsTStringRepr<T>& aRhs,
-        const nsTStringComparator<T>& comp)
+Compare(const nsTSubstring_CharT::base_string_type& aLhs,
+        const nsTSubstring_CharT::base_string_type& aRhs,
+        const nsTStringComparator_CharT& comp)
 {
-  typedef typename nsTSubstring<T>::size_type size_type;
-  typedef typename nsTSubstring<T>::const_iterator const_iterator;
+  typedef nsTSubstring_CharT::size_type size_type;
 
   if (&aLhs == &aRhs) {
     return 0;
   }
 
-  const_iterator leftIter, rightIter;
+  nsTSubstring_CharT::const_iterator leftIter, rightIter;
   aLhs.BeginReading(leftIter);
   aRhs.BeginReading(rightIter);
 
   size_type lLength = aLhs.Length();
   size_type rLength = aRhs.Length();
   size_type lengthToCompare = XPCOM_MIN(lLength, rLength);
 
   int result;
@@ -35,19 +33,18 @@ Compare(const mozilla::detail::nsTString
     } else {
       result = 0;
     }
   }
 
   return result;
 }
 
-template <typename T>
 int
-nsTDefaultStringComparator<T>::operator()(const char_type* aLhs,
-                                          const char_type* aRhs,
-                                          uint32_t aLLength,
-                                          uint32_t aRLength) const
+nsTDefaultStringComparator_CharT::operator()(const char_type* aLhs,
+                                             const char_type* aRhs,
+                                             uint32_t aLLength,
+                                             uint32_t aRLength) const
 {
   return
-    aLLength == aRLength ? nsCharTraits<T>::compare(aLhs, aRhs, aLLength) :
+    aLLength == aRLength ? nsCharTraits<CharT>::compare(aLhs, aRhs, aLLength) :
                             (aLLength > aRLength) ? 1 : -1;
 }
--- a/xpcom/string/nsTStringObsolete.cpp
+++ b/xpcom/string/nsTStringObsolete.cpp
@@ -9,135 +9,130 @@
 #include "mozilla/CheckedInt.h"
 
 /**
  * nsTString::Find
  *
  * aOffset specifies starting index
  * aCount specifies number of string compares (iterations)
  */
-template <typename T>
+
 int32_t
-nsTString<T>::Find(const nsTString<char>& aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
+nsTString_CharT::Find( const nsCString& aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
 {
   // this method changes the meaning of aOffset and aCount:
-  Find_ComputeSearchRange(this->mLength, aString.Length(), aOffset, aCount);
+  Find_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
 
-  int32_t result = FindSubstring(this->mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase);
+  int32_t result = FindSubstring(mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase);
   if (result != kNotFound)
     result += aOffset;
   return result;
 }
 
-template <typename T>
 int32_t
-nsTString<T>::Find(const char* aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
+nsTString_CharT::Find( const char* aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
 {
-  return Find(nsTDependentString<char>(aString), aIgnoreCase, aOffset, aCount);
+  return Find(nsDependentCString(aString), aIgnoreCase, aOffset, aCount);
 }
 
 
 /**
  * nsTString::RFind
  *
  * aOffset specifies starting index
  * aCount specifies number of string compares (iterations)
  */
-template <typename T>
+
 int32_t
-nsTString<T>::RFind(const nsTString<char>& aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
+nsTString_CharT::RFind( const nsCString& aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
 {
   // this method changes the meaning of aOffset and aCount:
-  RFind_ComputeSearchRange(this->mLength, aString.Length(), aOffset, aCount);
+  RFind_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);
 
-  int32_t result = RFindSubstring(this->mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase);
+  int32_t result = RFindSubstring(mData + aOffset, aCount, aString.get(), aString.Length(), aIgnoreCase);
   if (result != kNotFound)
     result += aOffset;
   return result;
 }
 
-template <typename T>
 int32_t
-nsTString<T>::RFind(const char* aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
+nsTString_CharT::RFind( const char* aString, bool aIgnoreCase, int32_t aOffset, int32_t aCount) const
 {
-  return RFind(nsTDependentString<char>(aString), aIgnoreCase, aOffset, aCount);
+  return RFind(nsDependentCString(aString), aIgnoreCase, aOffset, aCount);
 }
 
 
 /**
  * nsTString::RFindChar
  */
-template <typename T>
+
 int32_t
-nsTString<T>::RFindChar(char16_t aChar, int32_t aOffset, int32_t aCount) const
+nsTString_CharT::RFindChar( char16_t aChar, int32_t aOffset, int32_t aCount) const
 {
-  return nsBufferRoutines<T>::rfind_char(this->mData, this->mLength, aOffset, aChar, aCount);
+  return nsBufferRoutines<CharT>::rfind_char(mData, mLength, aOffset, aChar, aCount);
 }
 
 
 /**
  * nsTString::FindCharInSet
  */
 
-template <typename T>
 int32_t
-nsTString<T>::FindCharInSet(const char_type* aSet, int32_t aOffset) const
+nsTString_CharT::FindCharInSet( const char* aSet, int32_t aOffset ) const
 {
   if (aOffset < 0)
     aOffset = 0;
-  else if (aOffset >= int32_t(this->mLength))
+  else if (aOffset >= int32_t(mLength))
     return kNotFound;
 
-  int32_t result = ::FindCharInSet(this->mData + aOffset, this->mLength - aOffset, aSet);
+  int32_t result = ::FindCharInSet(mData + aOffset, mLength - aOffset, aSet);
   if (result != kNotFound)
     result += aOffset;
   return result;
 }
 
 
 /**
  * nsTString::RFindCharInSet
  */
 
-template <typename T>
 int32_t
-nsTString<T>::RFindCharInSet(const char_type* aSet, int32_t aOffset) const
+nsTString_CharT::RFindCharInSet( const CharT* aSet, int32_t aOffset ) const
 {
   // We want to pass a "data length" to ::RFindCharInSet
-  if (aOffset < 0 || aOffset > int32_t(this->mLength))
-    aOffset = this->mLength;
+  if (aOffset < 0 || aOffset > int32_t(mLength))
+    aOffset = mLength;
   else
     ++aOffset;
 
-  return ::RFindCharInSet(this->mData, aOffset, aSet);
+  return ::RFindCharInSet(mData, aOffset, aSet);
 }
 
 
 // it's a shame to replicate this code.  it was done this way in the past
 // to help performance.  this function also gets to keep the rickg style
 // indentation :-/
-template <typename T>
 int32_t
-nsTString<T>::ToInteger(nsresult* aErrorCode, uint32_t aRadix) const
+nsTString_CharT::ToInteger( nsresult* aErrorCode, uint32_t aRadix ) const
 {
-  char_type* cp = this->mData;
-  int32_t theRadix = 10; // base 10 unless base 16 detected, or overriden (aRadix != kAutoDetect)
-  int32_t result = 0;
-  bool negate = false;
-  char_type theChar = 0;
+  CharT*  cp=mData;
+  int32_t theRadix=10; // base 10 unless base 16 detected, or overriden (aRadix != kAutoDetect)
+  int32_t result=0;
+  bool    negate=false;
+  CharT   theChar=0;
 
   //initial value, override if we find an integer
   *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
 
   if(cp) {
 
     //begin by skipping over leading chars that shouldn't be part of the number...
 
-    char_type* endcp=cp+this->mLength;
-    bool done=false;
+    CharT*  endcp=cp+mLength;
+    bool    done=false;
 
     while((cp<endcp) && (!done)){
       switch(*cp++) {
         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
           theRadix=16;
           done=true;
           break;
@@ -159,17 +154,17 @@ nsTString<T>::ToInteger(nsresult* aError
     if (done) {
 
       //integer found
       *aErrorCode = NS_OK;
 
       if (aRadix!=kAutoDetect) theRadix = aRadix; // override
 
       //now iterate the numeric chars and build our result
-      char_type* first=--cp;  //in case we have to back up.
+      CharT* first=--cp;  //in case we have to back up.
       bool haveValue = false;
 
       while(cp<endcp){
         int32_t oldresult = result;
 
         theChar=*cp++;
         if(('0'<=theChar) && (theChar<='9')){
           result = (theRadix * result) + (theChar-'0');
@@ -237,35 +232,34 @@ nsTString<T>::ToInteger(nsresult* aError
   }
   return result;
 }
 
 
 /**
  * nsTString::ToInteger64
  */
-template <typename T>
 int64_t
-nsTString<T>::ToInteger64(nsresult* aErrorCode, uint32_t aRadix) const
+nsTString_CharT::ToInteger64( nsresult* aErrorCode, uint32_t aRadix ) const
 {
-  char_type* cp=this->mData;
+  CharT*  cp=mData;
   int32_t theRadix=10; // base 10 unless base 16 detected, or overriden (aRadix != kAutoDetect)
   int64_t result=0;
-  bool negate=false;
-  char_type theChar=0;
+  bool    negate=false;
+  CharT   theChar=0;
 
   //initial value, override if we find an integer
   *aErrorCode=NS_ERROR_ILLEGAL_VALUE;
 
   if(cp) {
 
     //begin by skipping over leading chars that shouldn't be part of the number...
 
-    char_type* endcp=cp+this->mLength;
-    bool done=false;
+    CharT*  endcp=cp+mLength;
+    bool    done=false;
 
     while((cp<endcp) && (!done)){
       switch(*cp++) {
         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
           theRadix=16;
           done=true;
           break;
@@ -287,17 +281,17 @@ nsTString<T>::ToInteger64(nsresult* aErr
     if (done) {
 
       //integer found
       *aErrorCode = NS_OK;
 
       if (aRadix!=kAutoDetect) theRadix = aRadix; // override
 
       //now iterate the numeric chars and build our result
-      char_type* first=--cp;  //in case we have to back up.
+      CharT* first=--cp;  //in case we have to back up.
       bool haveValue = false;
 
       while(cp<endcp){
         int64_t oldresult = result;
 
         theChar=*cp++;
         if(('0'<=theChar) && (theChar<='9')){
           result = (theRadix * result) + (theChar-'0');
@@ -366,366 +360,343 @@ nsTString<T>::ToInteger64(nsresult* aErr
   return result;
 }
 
 
 /**
    * nsTString::Mid
    */
 
-template <typename T>
-typename nsTString<T>::size_type
-nsTString<T>::Mid(self_type& aResult, index_type aStartPos, size_type aLengthToCopy) const
+uint32_t
+nsTString_CharT::Mid( self_type& aResult, index_type aStartPos, size_type aLengthToCopy ) const
 {
-  if (aStartPos == 0 && aLengthToCopy >= this->mLength)
+  if (aStartPos == 0 && aLengthToCopy >= mLength)
     aResult = *this;
   else
     aResult = Substring(*this, aStartPos, aLengthToCopy);
 
   return aResult.mLength;
 }
 
 
 /**
  * nsTString::SetCharAt
  */
 
-template <typename T>
 bool
-nsTString<T>::SetCharAt(char16_t aChar, uint32_t aIndex)
+nsTString_CharT::SetCharAt( char16_t aChar, uint32_t aIndex )
 {
-  if (aIndex >= this->mLength)
+  if (aIndex >= mLength)
     return false;
 
-  if (!this->EnsureMutable())
-    this->AllocFailed(this->mLength);
+  if (!EnsureMutable())
+    AllocFailed(mLength);
 
-  this->mData[aIndex] = char_type(aChar);
+  mData[aIndex] = CharT(aChar);
   return true;
 }
 
 
 /**
  * nsTString::StripChars,StripChar,StripWhitespace
  */
 
-template<typename T>
-template<typename EnableIfChar16>
 void
-nsTString<T>::StripChars(const incompatible_char_type* aSet)
+nsTString_CharT::StripChars( const char* aSet )
 {
   if (!StripChars(aSet, mozilla::fallible)) {
-    this->AllocFailed(this->mLength);
+    AllocFailed(mLength);
   }
 }
 
-template<typename T>
-template<typename EnableIfChar16>
 bool
-nsTString<T>::StripChars(const incompatible_char_type* aSet, const fallible_t&)
+nsTString_CharT::StripChars( const char* aSet, const fallible_t& )
 {
-  if (!this->EnsureMutable()) {
+  if (!EnsureMutable()) {
     return false;
   }
 
-  this->mLength = nsBufferRoutines<T>::strip_chars(this->mData, this->mLength, aSet);
+  mLength = nsBufferRoutines<CharT>::strip_chars(mData, mLength, aSet);
   return true;
 }
 
-template<typename T>
 void
-nsTString<T>::StripChars(const char_type* aSet)
-{
-  nsTSubstring<T>::StripChars(aSet);
-}
-
-template <typename T>
-void
-nsTString<T>::StripWhitespace()
+nsTString_CharT::StripWhitespace()
 {
   if (!StripWhitespace(mozilla::fallible)) {
-    this->AllocFailed(this->mLength);
+    AllocFailed(mLength);
   }
 }
 
-template <typename T>
 bool
-nsTString<T>::StripWhitespace(const fallible_t&)
+nsTString_CharT::StripWhitespace( const fallible_t& )
 {
-  if (!this->EnsureMutable()) {
+  if (!EnsureMutable()) {
     return false;
   }
 
-  this->StripTaggedASCII(mozilla::ASCIIMask::MaskWhitespace());
+  StripTaggedASCII(mozilla::ASCIIMask::MaskWhitespace());
   return true;
 }
 
 /**
  * nsTString::ReplaceChar,ReplaceSubstring
  */
 
-template <typename T>
 void
-nsTString<T>::ReplaceChar(char_type aOldChar, char_type aNewChar)
+nsTString_CharT::ReplaceChar( char_type aOldChar, char_type aNewChar )
 {
-  if (!this->EnsureMutable()) // XXX do this lazily?
-    this->AllocFailed(this->mLength);
+  if (!EnsureMutable()) // XXX do this lazily?
+    AllocFailed(mLength);
 
-  for (uint32_t i=0; i<this->mLength; ++i)
+  for (uint32_t i=0; i<mLength; ++i)
   {
-    if (this->mData[i] == aOldChar)
-      this->mData[i] = aNewChar;
+    if (mData[i] == aOldChar)
+      mData[i] = aNewChar;
   }
 }
 
-template <typename T>
 void
-nsTString<T>::ReplaceChar(const char_type* aSet, char_type aNewChar)
+nsTString_CharT::ReplaceChar( const char* aSet, char_type aNewChar )
 {
-  if (!this->EnsureMutable()) // XXX do this lazily?
-    this->AllocFailed(this->mLength);
+  if (!EnsureMutable()) // XXX do this lazily?
+    AllocFailed(mLength);
 
-  char_type* data = this->mData;
-  uint32_t lenRemaining = this->mLength;
+  char_type* data = mData;
+  uint32_t lenRemaining = mLength;
 
   while (lenRemaining)
   {
     int32_t i = ::FindCharInSet(data, lenRemaining, aSet);
     if (i == kNotFound)
       break;
 
     data[i++] = aNewChar;
     data += i;
     lenRemaining -= i;
   }
 }
 
 void ReleaseData(void* aData, nsAString::DataFlags aFlags);
 
-template <typename T>
 void
-nsTString<T>::ReplaceSubstring(const char_type* aTarget,
-                               const char_type* aNewValue)
+nsTString_CharT::ReplaceSubstring(const char_type* aTarget,
+                                  const char_type* aNewValue)
 {
-  ReplaceSubstring(nsTDependentString<T>(aTarget),
-                   nsTDependentString<T>(aNewValue));
+  ReplaceSubstring(nsTDependentString_CharT(aTarget),
+                   nsTDependentString_CharT(aNewValue));
 }
 
-template <typename T>
 bool
-nsTString<T>::ReplaceSubstring(const char_type* aTarget,
-                               const char_type* aNewValue,
-                               const fallible_t& aFallible)
+nsTString_CharT::ReplaceSubstring(const char_type* aTarget,
+                                  const char_type* aNewValue,
+                                  const fallible_t& aFallible)
 {
-  return ReplaceSubstring(nsTDependentString<T>(aTarget),
-                          nsTDependentString<T>(aNewValue),
+  return ReplaceSubstring(nsTDependentString_CharT(aTarget),
+                          nsTDependentString_CharT(aNewValue),
                           aFallible);
 }
 
-template <typename T>
 void
-nsTString<T>::ReplaceSubstring(const self_type& aTarget,
-                               const self_type& aNewValue)
+nsTString_CharT::ReplaceSubstring(const self_type& aTarget,
+                                  const self_type& aNewValue)
 {
   if (!ReplaceSubstring(aTarget, aNewValue, mozilla::fallible)) {
     // Note that this may wildly underestimate the allocation that failed, as
     // we could have been replacing multiple copies of aTarget.
-    this->AllocFailed(this->mLength + (aNewValue.Length() - aTarget.Length()));
+    AllocFailed(mLength + (aNewValue.Length() - aTarget.Length()));
   }
 }
 
-template <typename T>
 bool
-nsTString<T>::ReplaceSubstring(const self_type& aTarget,
-                               const self_type& aNewValue,
-                               const fallible_t&)
+nsTString_CharT::ReplaceSubstring(const self_type& aTarget,
+                                  const self_type& aNewValue,
+                                  const fallible_t&)
 {
   if (aTarget.Length() == 0)
     return true;
 
   // Remember all of the non-matching parts.
   AutoTArray<Segment, 16> nonMatching;
   uint32_t i = 0;
   mozilla::CheckedUint32 newLength;
   while (true)
   {
-    int32_t r = FindSubstring(this->mData + i, this->mLength - i, static_cast<const char_type*>(aTarget.Data()), aTarget.Length(), false);
-    int32_t until = (r == kNotFound) ? this->mLength - i : r;
+    int32_t r = FindSubstring(mData + i, mLength - i, static_cast<const char_type*>(aTarget.Data()), aTarget.Length(), false);
+    int32_t until = (r == kNotFound) ? mLength - i : r;
     nonMatching.AppendElement(Segment(i, until));
     newLength += until;
     if (r == kNotFound) {
       break;
     }
 
     newLength += aNewValue.Length();
     i += r + aTarget.Length();
-    if (i >= this->mLength) {
+    if (i >= mLength) {
       // Add an auxiliary entry at the end of the list to help as an edge case
       // for the algorithms below.
-      nonMatching.AppendElement(Segment(this->mLength, 0));
+      nonMatching.AppendElement(Segment(mLength, 0));
       break;
     }
   }
 
   if (!newLength.isValid()) {
     return false;
   }
 
   // If there's only one non-matching segment, then the target string was not
   // found, and there's nothing to do.
   if (nonMatching.Length() == 1) {
-    MOZ_ASSERT(nonMatching[0].mBegin == 0 && nonMatching[0].mLength == this->mLength,
+    MOZ_ASSERT(nonMatching[0].mBegin == 0 && nonMatching[0].mLength == mLength,
                "We should have the correct non-matching segment.");
     return true;
   }
 
   // Make sure that we can mutate our buffer.
-  // Note that we always allocate at least an this->mLength sized buffer, because the
+  // Note that we always allocate at least an mLength sized buffer, because the
   // rest of the algorithm relies on having access to all of the original
   // string.  In other words, we over-allocate in the shrinking case.
   char_type* oldData;
   DataFlags oldFlags;
-  if (!this->MutatePrep(XPCOM_MAX(this->mLength, newLength.value()), &oldData, &oldFlags))
+  if (!MutatePrep(XPCOM_MAX(mLength, newLength.value()), &oldData, &oldFlags))
     return false;
   if (oldData) {
     // Copy all of the old data to the new buffer.
-    char_traits::copy(this->mData, oldData, this->mLength);
+    char_traits::copy(mData, oldData, mLength);
     ::ReleaseData(oldData, oldFlags);
   }
 
   if (aTarget.Length() >= aNewValue.Length()) {
     // In the shrinking case, start filling the buffer from the beginning.
     const uint32_t delta = (aTarget.Length() - aNewValue.Length());
     for (i = 1; i < nonMatching.Length(); ++i) {
       // When we move the i'th non-matching segment into position, we need to
       // account for the characters deleted by the previous |i| replacements by
       // subtracting |i * delta|.
-      const char_type* sourceSegmentPtr = this->mData + nonMatching[i].mBegin;
-      char_type* destinationSegmentPtr = this->mData + nonMatching[i].mBegin - i * delta;
+      const char_type* sourceSegmentPtr = mData + nonMatching[i].mBegin;
+      char_type* destinationSegmentPtr = mData + nonMatching[i].mBegin - i * delta;
       // Write the i'th replacement immediately before the new i'th non-matching
       // segment.
       char_traits::copy(destinationSegmentPtr - aNewValue.Length(),
                         aNewValue.Data(), aNewValue.Length());
       char_traits::move(destinationSegmentPtr, sourceSegmentPtr,
                         nonMatching[i].mLength);
     }
   } else {
     // In the growing case, start filling the buffer from the end.
     const uint32_t delta = (aNewValue.Length() - aTarget.Length());
     for (i = nonMatching.Length() - 1; i > 0; --i) {
       // When we move the i'th non-matching segment into position, we need to
       // account for the characters added by the previous |i| replacements by
       // adding |i * delta|.
-      const char_type* sourceSegmentPtr = this->mData + nonMatching[i].mBegin;
-      char_type* destinationSegmentPtr = this->mData + nonMatching[i].mBegin + i * delta;
+      const char_type* sourceSegmentPtr = mData + nonMatching[i].mBegin;
+      char_type* destinationSegmentPtr = mData + nonMatching[i].mBegin + i * delta;
       char_traits::move(destinationSegmentPtr, sourceSegmentPtr,
                         nonMatching[i].mLength);
       // Write the i'th replacement immediately before the new i'th non-matching
       // segment.
       char_traits::copy(destinationSegmentPtr - aNewValue.Length(),
                         aNewValue.Data(), aNewValue.Length());
     }
   }
 
   // Adjust the length and make sure the string is null terminated.
-  this->mLength = newLength.value();
-  this->mData[this->mLength] = char_type(0);
+  mLength = newLength.value();
+  mData[mLength] = char_type(0);
 
   return true;
 }
 
 /**
  * nsTString::Trim
  */
 
-template <typename T>
 void
-nsTString<T>::Trim(const char* aSet, bool aTrimLeading, bool aTrimTrailing, bool aIgnoreQuotes)
+nsTString_CharT::Trim( const char* aSet, bool aTrimLeading, bool aTrimTrailing, bool aIgnoreQuotes )
 {
   // the old implementation worried about aSet being null :-/
   if (!aSet)
     return;
 
-  char_type* start = this->mData;
-  char_type* end   = this->mData + this->mLength;
+  char_type* start = mData;
+  char_type* end   = mData + mLength;
 
   // skip over quotes if requested
-  if (aIgnoreQuotes && this->mLength > 2 && this->mData[0] == this->mData[this->mLength - 1] &&
-      (this->mData[0] == '\'' || this->mData[0] == '"'))
+  if (aIgnoreQuotes && mLength > 2 && mData[0] == mData[mLength - 1] &&
+      (mData[0] == '\'' || mData[0] == '"'))
   {
     ++start;
     --end;
   }
 
   uint32_t setLen = nsCharTraits<char>::length(aSet);
 
   if (aTrimLeading)
   {
-    uint32_t cutStart = start - this->mData;
+    uint32_t cutStart = start - mData;
     uint32_t cutLength = 0;
 
     // walk forward from start to end
     for (; start != end; ++start, ++cutLength)
     {
       int32_t pos = FindChar1(aSet, setLen, 0, *start, setLen);
       if (pos == kNotFound)
         break;
     }
 
     if (cutLength)
     {
-      this->Cut(cutStart, cutLength);
+      Cut(cutStart, cutLength);
 
       // reset iterators
-      start = this->mData + cutStart;
-      end   = this->mData + this->mLength - cutStart;
+      start = mData + cutStart;
+      end   = mData + mLength - cutStart;
     }
   }
 
   if (aTrimTrailing)
   {
-    uint32_t cutEnd = end - this->mData;
+    uint32_t cutEnd = end - mData;
     uint32_t cutLength = 0;
 
     // walk backward from end to start
     --end;
     for (; end >= start; --end, ++cutLength)
     {
       int32_t pos = FindChar1(aSet, setLen, 0, *end, setLen);
       if (pos == kNotFound)
         break;
     }
 
     if (cutLength)
-      this->Cut(cutEnd - cutLength, cutLength);
+      Cut(cutEnd - cutLength, cutLength);
   }
 }
 
 
 /**
  * nsTString::CompressWhitespace.
  */
 
-template <typename T>
 void
-nsTString<T>::CompressWhitespace(bool aTrimLeading, bool aTrimTrailing)
+nsTString_CharT::CompressWhitespace( bool aTrimLeading, bool aTrimTrailing )
 {
   // Quick exit
-  if (this->mLength == 0) {
+  if (mLength == 0) {
     return;
   }
 
-  if (!this->EnsureMutable())
-    this->AllocFailed(this->mLength);
+  if (!EnsureMutable())
+    AllocFailed(mLength);
 
   const ASCIIMaskArray& mask = mozilla::ASCIIMask::MaskWhitespace();
 
-  char_type* to   = this->mData;
-  char_type* from = this->mData;
-  char_type* end  = this->mData + this->mLength;
+  char_type* to   = mData;
+  char_type* from = mData;
+  char_type* end  = mData + mLength;
 
   // Compresses runs of whitespace down to a normal space ' ' and convert
   // any whitespace to a normal space.  This assumes that whitespace is
   // all standard 7-bit ASCII.
   bool skipWS = aTrimLeading;
   while (from < end) {
     uint32_t theChar = *from++;
     if (mozilla::ASCIIMask::IsMasked(mask, theChar)) {
@@ -735,15 +706,15 @@ nsTString<T>::CompressWhitespace(bool aT
       }
     } else {
       *to++ = theChar;
       skipWS = false;
     }
   }
 
   // If we need to trim the trailing whitespace, back up one character.
-  if (aTrimTrailing && skipWS && to > this->mData) {
+  if (aTrimTrailing && skipWS && to > mData) {
     to--;
   }
 
   *to = char_type(0); // add the null
-  this->mLength = to - this->mData;
+  mLength = to - mData;
 }
deleted file mode 100644
--- a/xpcom/string/nsTStringRepr.h
+++ /dev/null
@@ -1,374 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsTStringRepr_h
-#define nsTStringRepr_h
-
-#include <type_traits> // std::enable_if
-
-#include "nsStringFlags.h"
-#include "nsCharTraits.h"
-
-template <typename T> class nsTSubstringTuple;
-
-// The base for string comparators
-template <typename T> class nsTStringComparator
-{
-public:
-  typedef T char_type;
-
-  nsTStringComparator() {}
-
-  virtual int operator()(const char_type*, const char_type*,
-                         uint32_t, uint32_t) const = 0;
-};
-
-// The default string comparator (case-sensitive comparision)
-template <typename T> class nsTDefaultStringComparator
-  : public nsTStringComparator<T>
-{
-public:
-  typedef T char_type;
-
-  nsTDefaultStringComparator() {}
-
-  virtual int operator()(const char_type*, const char_type*,
-                         uint32_t, uint32_t) const override;
-};
-
-extern template class nsTDefaultStringComparator<char>;
-extern template class nsTDefaultStringComparator<char16_t>;
-
-namespace mozilla {
-namespace detail {
-
-// nsTStringRepr defines a string's memory layout and some accessor methods.
-// This class exists so that nsTLiteralString can avoid inheriting
-// nsTSubstring's destructor. All methods on this class must be const because
-// literal strings are not writable.
-//
-// This class is an implementation detail and should not be instantiated
-// directly, nor used in any way outside of the string code itself. It is
-// buried in a namespace to discourage its use in function parameters.
-// If you need to take a parameter, use [const] ns[C]Substring&.
-// If you need to instantiate a string, use ns[C]String or descendents.
-//
-// NAMES:
-//   nsStringRepr for wide characters
-//   nsCStringRepr for narrow characters
-template <typename T> class nsTStringRepr
-{
-public:
-  typedef mozilla::fallible_t fallible_t;
-
-  typedef T char_type;
-
-  typedef nsCharTraits<char_type> char_traits;
-  typedef typename char_traits::incompatible_char_type incompatible_char_type;
-
-  typedef nsTStringRepr<T> self_type;
-  typedef self_type base_string_type;
-
-  typedef nsTSubstring<T> substring_type;
-  typedef nsTSubstringTuple<T> substring_tuple_type;
-
-  typedef nsReadingIterator<char_type> const_iterator;
-  typedef nsWritingIterator<char_type> iterator;
-
-  typedef nsTStringComparator<char_type> comparator_type;
-
-  typedef char_type* char_iterator;
-  typedef const char_type* const_char_iterator;
-
-  typedef uint32_t index_type;
-  typedef uint32_t size_type;
-
-  // These are only for internal use within the string classes:
-  typedef StringDataFlags DataFlags;
-  typedef StringClassFlags ClassFlags;
-
-  // These are used to conditionally enable functions for specific character
-  // types.
-  using IsChar   = std::enable_if<std::is_same<char, T>::value>;
-  using IsChar16 = std::enable_if<std::is_same<char16_t, T>::value>;
-
-  // Reading iterators.
-  const_char_iterator BeginReading() const
-  {
-    return mData;
-  }
-  const_char_iterator EndReading() const
-  {
-    return mData + mLength;
-  }
-
-  // Deprecated reading iterators.
-  const_iterator& BeginReading(const_iterator& aIter) const
-  {
-    aIter.mStart = mData;
-    aIter.mEnd = mData + mLength;
-    aIter.mPosition = aIter.mStart;
-    return aIter;
-  }
-
-  const_iterator& EndReading(const_iterator& aIter) const
-  {
-    aIter.mStart = mData;
-    aIter.mEnd = mData + mLength;
-    aIter.mPosition = aIter.mEnd;
-    return aIter;
-  }
-
-  const_char_iterator& BeginReading(const_char_iterator& aIter) const
-  {
-    return aIter = mData;
-  }
-
-  const_char_iterator& EndReading(const_char_iterator& aIter) const
-  {
-    return aIter = mData + mLength;
-  }
-
-  // Accessors.
-  template <typename U> struct raw_type { typedef const U* type; };
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <> struct raw_type<char16_t> { typedef char16ptr_t type; };
-#endif
-
-  // Returns pointer to string data (not necessarily null-terminated)
-  const typename raw_type<T>::type Data() const
-  {
-    return mData;
-  }
-
-  size_type Length() const
-  {
-    return mLength;
-  }
-
-  DataFlags GetDataFlags() const
-  {
-    return mDataFlags;
-  }
-
-  bool IsEmpty() const
-  {
-    return mLength == 0;
-  }
-
-  bool IsLiteral() const
-  {
-    return !!(mDataFlags & DataFlags::LITERAL);
-  }
-
-  bool IsVoid() const
-  {
-    return !!(mDataFlags & DataFlags::VOIDED);
-  }
-
-  bool IsTerminated() const
-  {
-    return !!(mDataFlags & DataFlags::TERMINATED);
-  }
-
-  char_type CharAt(index_type aIndex) const
-  {
-    NS_ASSERTION(aIndex < mLength, "index exceeds allowable range");
-    return mData[aIndex];
-  }
-
-  char_type operator[](index_type aIndex) const
-  {
-    return CharAt(aIndex);
-  }
-
-  char_type First() const;
-
-  char_type Last() const;
-
-  size_type NS_FASTCALL CountChar(char_type) const;
-  int32_t NS_FASTCALL FindChar(char_type, index_type aOffset = 0) const;
-
-  inline bool Contains(char_type aChar) const
-  {
-    return FindChar(aChar) != kNotFound;
-  }
-
-  // Equality.
-  bool NS_FASTCALL Equals(const self_type&) const;
-  bool NS_FASTCALL Equals(const self_type&, const comparator_type&) const;
-
-  bool NS_FASTCALL Equals(const substring_tuple_type& aTuple) const;
-  bool NS_FASTCALL Equals(const substring_tuple_type& aTuple,
-                          const comparator_type& aComp) const;
-
-  bool NS_FASTCALL Equals(const char_type* aData) const;
-  bool NS_FASTCALL Equals(const char_type* aData,
-                          const comparator_type& aComp) const;
-
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
-  bool NS_FASTCALL Equals(char16ptr_t aData) const
-  {
-    return Equals(static_cast<const char16_t*>(aData));
-  }
-  template <typename EnableIfChar16 = IsChar16>
-  bool NS_FASTCALL Equals(char16ptr_t aData, const comparator_type& aComp) const
-  {
-    return Equals(static_cast<const char16_t*>(aData), aComp);
-  }
-#endif
-
-  // An efficient comparison with ASCII that can be used even
-  // for wide strings. Call this version when you know the
-  // length of 'data'.
-  bool NS_FASTCALL EqualsASCII(const char* aData, size_type aLen) const;
-  // An efficient comparison with ASCII that can be used even
-  // for wide strings. Call this version when 'data' is
-  // null-terminated.
-  bool NS_FASTCALL EqualsASCII(const char* aData) const;
-
-  // EqualsLiteral must ONLY be applied to an actual literal string, or
-  // a char array *constant* declared without an explicit size.
-  // Do not attempt to use it with a regular char* pointer, or with a
-  // non-constant char array variable. Use EqualsASCII for them.
-  // The template trick to acquire the array length at compile time without
-  // using a macro is due to Corey Kosak, with much thanks.
-  template<int N>
-  inline bool EqualsLiteral(const char (&aStr)[N]) const
-  {
-    return EqualsASCII(aStr, N - 1);
-  }
-
-  // The LowerCaseEquals methods compare the ASCII-lowercase version of
-  // this string (lowercasing only ASCII uppercase characters) to some
-  // ASCII/Literal string. The ASCII string is *not* lowercased for
-  // you. If you compare to an ASCII or literal string that contains an
-  // uppercase character, it is guaranteed to return false. We will
-  // throw assertions too.
-  bool NS_FASTCALL LowerCaseEqualsASCII(const char* aData,
-                                        size_type aLen) const;
-  bool NS_FASTCALL LowerCaseEqualsASCII(const char* aData) const;
-
-  // LowerCaseEqualsLiteral must ONLY be applied to an actual
-  // literal string, or a char array *constant* declared without an
-  // explicit size.  Do not attempt to use it with a regular char*
-  // pointer, or with a non-constant char array variable. Use
-  // LowerCaseEqualsASCII for them.
-  template<int N>
-  bool LowerCaseEqualsLiteral(const char (&aStr)[N]) const
-  {
-    return LowerCaseEqualsASCII(aStr, N - 1);
-  }
-
-  // Returns true if this string overlaps with the given string fragment.
-  bool IsDependentOn(const char_type* aStart, const char_type* aEnd) const
-  {
-     // If it _isn't_ the case that one fragment starts after the other ends,
-     // or ends before the other starts, then, they conflict:
-     //
-     //   !(f2.begin >= f1.aEnd || f2.aEnd <= f1.begin)
-     //
-     // Simplified, that gives us:
-    return (aStart < (mData + mLength) && aEnd > mData);
-  }
-
-protected:
-  nsTStringRepr() = delete; // Never instantiate directly
-
-  constexpr
-  nsTStringRepr(char_type* aData, size_type aLength,
-               DataFlags aDataFlags, ClassFlags aClassFlags)
-    : mData(aData)
-    , mLength(aLength)
-    , mDataFlags(aDataFlags)
-    , mClassFlags(aClassFlags)
-  {
-  }
-
-  char_type* mData;
-  size_type mLength;
-  DataFlags mDataFlags;
-  ClassFlags const mClassFlags;
-};
-
-extern template class nsTStringRepr<char>;
-extern template class nsTStringRepr<char16_t>;
-
-} // namespace detail
-} // namespace mozilla
-
-template <typename T>
-int NS_FASTCALL
-Compare(const mozilla::detail::nsTStringRepr<T>& aLhs,
-        const mozilla::detail::nsTStringRepr<T>& aRhs,
-        const nsTStringComparator<T>& = nsTDefaultStringComparator<T>());
-
-template <typename T>
-inline bool
-operator!=(const mozilla::detail::nsTStringRepr<T>& aLhs,
-           const mozilla::detail::nsTStringRepr<T>& aRhs)
-{
-  return !aLhs.Equals(aRhs);
-}
-
-template <typename T>
-inline bool
-operator!=(const mozilla::detail::nsTStringRepr<T>& aLhs,
-           const T* aRhs)
-{
-  return !aLhs.Equals(aRhs);
-}
-
-template <typename T>
-inline bool
-operator<(const mozilla::detail::nsTStringRepr<T>& aLhs,
-          const mozilla::detail::nsTStringRepr<T>& aRhs)
-{
-  return Compare(aLhs, aRhs) < 0;
-}
-
-template <typename T>
-inline bool
-operator<=(const mozilla::detail::nsTStringRepr<T>& aLhs,
-           const mozilla::detail::nsTStringRepr<T>& aRhs)
-{
-  return Compare(aLhs, aRhs) <= 0;
-}
-
-template <typename T>
-inline bool
-operator==(const mozilla::detail::nsTStringRepr<T>& aLhs,
-           const mozilla::detail::nsTStringRepr<T>& aRhs)
-{
-  return aLhs.Equals(aRhs);
-}
-
-template <typename T>
-inline bool
-operator==(const mozilla::detail::nsTStringRepr<T>& aLhs,
-           const T* aRhs)
-{
-  return aLhs.Equals(aRhs);
-}
-
-template <typename T>
-inline bool
-operator>=(const mozilla::detail::nsTStringRepr<T>& aLhs,
-           const mozilla::detail::nsTStringRepr<T>& aRhs)
-{
-  return Compare(aLhs, aRhs) >= 0;
-}
-
-template <typename T>
-inline bool
-operator>(const mozilla::detail::nsTStringRepr<T>& aLhs,
-          const mozilla::detail::nsTStringRepr<T>& aRhs)
-{
-  return Compare(aLhs, aRhs) > 0;
-}
-
-#endif
--- a/xpcom/string/nsTSubstring.cpp
+++ b/xpcom/string/nsTSubstring.cpp
@@ -7,60 +7,56 @@
 #include "nsASCIIMask.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/double-conversion.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Printf.h"
 
 using double_conversion::DoubleToStringConverter;
 
-template <typename T>
-const typename nsTSubstring<T>::size_type nsTSubstring<T>::kMaxCapacity =
-    (nsTSubstring<T>::size_type(-1) /
+const nsTSubstring_CharT::size_type nsTSubstring_CharT::kMaxCapacity =
+    (nsTSubstring_CharT::size_type(-1) /
         2 - sizeof(nsStringBuffer)) /
-    sizeof(nsTSubstring<T>::char_type) - 2;
+    sizeof(nsTSubstring_CharT::char_type) - 2;
 
 #ifdef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
-template <typename T>
-nsTSubstring<T>::nsTSubstring(char_type* aData, size_type aLength,
-                              DataFlags aDataFlags,
-                              ClassFlags aClassFlags)
-  : ::mozilla::detail::nsTStringRepr<T>(aData, aLength, aDataFlags, aClassFlags)
+nsTSubstring_CharT::nsTSubstring_CharT(char_type* aData, size_type aLength,
+                                       DataFlags aDataFlags,
+                                       ClassFlags aClassFlags)
+  : nsTStringRepr_CharT(aData, aLength, aDataFlags, aClassFlags)
 {
   AssertValid();
   MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "String is too large.");
 
   if (aDataFlags & DataFlags::OWNED) {
     STRING_STAT_INCREMENT(Adopt);
-    MOZ_LOG_CTOR(this->mData, "StringAdopt", 1);
+    MOZ_LOG_CTOR(mData, "StringAdopt", 1);
   }
 }
 #endif /* XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE */
 
 /**
  * helper function for down-casting a nsTSubstring to a nsTFixedString.
  */
-template <typename T>
-inline const nsTFixedString<T>*
-AsFixedString(const nsTSubstring<T>* aStr)
+inline const nsTFixedString_CharT*
+AsFixedString(const nsTSubstring_CharT* aStr)
 {
-  return static_cast<const nsTFixedString<T>*>(aStr);
+  return static_cast<const nsTFixedString_CharT*>(aStr);
 }
 
 /**
  * this function is called to prepare mData for writing.  the given capacity
  * indicates the required minimum storage size for mData, in sizeof(char_type)
  * increments.  this function returns true if the operation succeeds.  it also
  * returns the old data and old flags members if mData is newly allocated.
  * the old data must be released by the caller.
  */
-template <typename T>
 bool
-nsTSubstring<T>::MutatePrep(size_type aCapacity, char_type** aOldData,
-                            DataFlags* aOldDataFlags)
+nsTSubstring_CharT::MutatePrep(size_type aCapacity, char_type** aOldData,
+                               DataFlags* aOldDataFlags)
 {
   // initialize to no old data
   *aOldData = nullptr;
   *aOldDataFlags = DataFlags(0);
 
   size_type curCapacity = Capacity();
 
   // If |aCapacity > kMaxCapacity|, then our doubling algorithm may not be
@@ -73,17 +69,17 @@ nsTSubstring<T>::MutatePrep(size_type aC
   }
 
   // |curCapacity == 0| means that the buffer is immutable or 0-sized, so we
   // need to allocate a new buffer. We cannot use the existing buffer even
   // though it might be large enough.
 
   if (curCapacity != 0) {
     if (aCapacity <= curCapacity) {
-      this->mDataFlags &= ~DataFlags::VOIDED;  // mutation clears voided flag
+      mDataFlags &= ~DataFlags::VOIDED;  // mutation clears voided flag
       return true;
     }
   }
 
   if (curCapacity < aCapacity) {
     // We increase our capacity so that the allocated buffer grows
     // exponentially, which gives us amortized O(1) appending. Below the
     // threshold, we use powers-of-two. Above the threshold, we grow by at
@@ -114,49 +110,49 @@ nsTSubstring<T>::MutatePrep(size_type aC
     MOZ_ASSERT(XPCOM_MIN(temp, kMaxCapacity) >= aCapacity,
                "should have hit the early return at the top");
     aCapacity = XPCOM_MIN(temp, kMaxCapacity);
   }
 
   //
   // several cases:
   //
-  //  (1) we have a shared buffer (this->mDataFlags & DataFlags::SHARED)
-  //  (2) we have an owned buffer (this->mDataFlags & DataFlags::OWNED)
-  //  (3) we have a fixed buffer (this->mDataFlags & DataFlags::FIXED)
+  //  (1) we have a shared buffer (mDataFlags & DataFlags::SHARED)
+  //  (2) we have an owned buffer (mDataFlags & DataFlags::OWNED)
+  //  (3) we have a fixed buffer (mDataFlags & DataFlags::FIXED)
   //  (4) we have a readonly buffer
   //
   // requiring that we in some cases preserve the data before creating
   // a new buffer complicates things just a bit ;-)
   //
 
   size_type storageSize = (aCapacity + 1) * sizeof(char_type);
 
   // case #1
-  if (this->mDataFlags & DataFlags::SHARED) {
-    nsStringBuffer* hdr = nsStringBuffer::FromData(this->mData);
+  if (mDataFlags & DataFlags::SHARED) {
+    nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
     if (!hdr->IsReadonly()) {
       nsStringBuffer* newHdr = nsStringBuffer::Realloc(hdr, storageSize);
       if (!newHdr) {
         return false;  // out-of-memory (original header left intact)
       }
 
       hdr = newHdr;
-      this->mData = (char_type*)hdr->Data();
-      this->mDataFlags &= ~DataFlags::VOIDED;  // mutation clears voided flag
+      mData = (char_type*)hdr->Data();
+      mDataFlags &= ~DataFlags::VOIDED;  // mutation clears voided flag
       return true;
     }
   }
 
   char_type* newData;
   DataFlags newDataFlags;
 
   // if we have a fixed buffer of sufficient size, then use it.  this helps
   // avoid heap allocations.
-  if ((this->mClassFlags & ClassFlags::FIXED) &&
+  if ((mClassFlags & ClassFlags::FIXED) &&
       (aCapacity < AsFixedString(this)->mFixedCapacity)) {
     newData = AsFixedString(this)->mFixedBuf;
     newDataFlags = DataFlags::TERMINATED | DataFlags::FIXED;
   } else {
     // if we reach here then, we must allocate a new buffer.  we cannot
     // make use of our DataFlags::OWNED or DataFlags::FIXED buffers because they are not
     // large enough.
 
@@ -166,343 +162,325 @@ nsTSubstring<T>::MutatePrep(size_type aC
       return false;  // we are still in a consistent state
     }
 
     newData = (char_type*)newHdr->Data();
     newDataFlags = DataFlags::TERMINATED | DataFlags::SHARED;
   }
 
   // save old data and flags
-  *aOldData = this->mData;
-  *aOldDataFlags = this->mDataFlags;
+  *aOldData = mData;
+  *aOldDataFlags = mDataFlags;
 
-  // this->mLength does not change
-  SetData(newData, this->mLength, newDataFlags);
+  // mLength does not change
+  SetData(newData, mLength, newDataFlags);
 
   // though we are not necessarily terminated at the moment, now is probably
   // still the best time to set DataFlags::TERMINATED.
 
   return true;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::Finalize()
+nsTSubstring_CharT::Finalize()
 {
-  ::ReleaseData(this->mData, this->mDataFlags);
-  // this->mData, this->mLength, and this->mDataFlags are purposefully left dangling
+  ::ReleaseData(mData, mDataFlags);
+  // mData, mLength, and mDataFlags are purposefully left dangling
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::ReplacePrep(index_type aCutStart,
-                             size_type aCutLength,
-                             size_type aNewLength)
+nsTSubstring_CharT::ReplacePrep(index_type aCutStart,
+                                size_type aCutLength,
+                                size_type aNewLength)
 {
-  aCutLength = XPCOM_MIN(aCutLength, this->mLength - aCutStart);
+  aCutLength = XPCOM_MIN(aCutLength, mLength - aCutStart);
 
-  mozilla::CheckedInt<size_type> newTotalLen = this->mLength;
+  mozilla::CheckedInt<size_type> newTotalLen = mLength;
   newTotalLen += aNewLength;
   newTotalLen -= aCutLength;
   if (!newTotalLen.isValid()) {
     return false;
   }
 
-  if (aCutStart == this->mLength && Capacity() > newTotalLen.value()) {
-    this->mDataFlags &= ~DataFlags::VOIDED;
-    this->mData[newTotalLen.value()] = char_type(0);
-    this->mLength = newTotalLen.value();
+  if (aCutStart == mLength && Capacity() > newTotalLen.value()) {
+    mDataFlags &= ~DataFlags::VOIDED;
+    mData[newTotalLen.value()] = char_type(0);
+    mLength = newTotalLen.value();
     return true;
   }
 
   return ReplacePrepInternal(aCutStart, aCutLength, aNewLength,
                              newTotalLen.value());
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::ReplacePrepInternal(index_type aCutStart, size_type aCutLen,
-                                     size_type aFragLen, size_type aNewLen)
+nsTSubstring_CharT::ReplacePrepInternal(index_type aCutStart, size_type aCutLen,
+                                        size_type aFragLen, size_type aNewLen)
 {
   char_type* oldData;
   DataFlags oldFlags;
   if (!MutatePrep(aNewLen, &oldData, &oldFlags)) {
     return false;  // out-of-memory
   }
 
   if (oldData) {
     // determine whether or not we need to copy part of the old string
     // over to the new string.
 
     if (aCutStart > 0) {
       // copy prefix from old string
-      char_traits::copy(this->mData, oldData, aCutStart);
+      char_traits::copy(mData, oldData, aCutStart);
     }
 
-    if (aCutStart + aCutLen < this->mLength) {
+    if (aCutStart + aCutLen < mLength) {
       // copy suffix from old string to new offset
       size_type from = aCutStart + aCutLen;
-      size_type fromLen = this->mLength - from;
+      size_type fromLen = mLength - from;
       uint32_t to = aCutStart + aFragLen;
-      char_traits::copy(this->mData + to, oldData + from, fromLen);
+      char_traits::copy(mData + to, oldData + from, fromLen);
     }
 
     ::ReleaseData(oldData, oldFlags);
   } else {
     // original data remains intact
 
     // determine whether or not we need to move part of the existing string
     // to make room for the requested hole.
-    if (aFragLen != aCutLen && aCutStart + aCutLen < this->mLength) {
+    if (aFragLen != aCutLen && aCutStart + aCutLen < mLength) {
       uint32_t from = aCutStart + aCutLen;
-      uint32_t fromLen = this->mLength - from;
+      uint32_t fromLen = mLength - from;
       uint32_t to = aCutStart + aFragLen;
-      char_traits::move(this->mData + to, this->mData + from, fromLen);
+      char_traits::move(mData + to, mData + from, fromLen);
     }
   }
 
-  // add null terminator (mutable this->mData always has room for the null-
+  // add null terminator (mutable mData always has room for the null-
   // terminator).
-  this->mData[aNewLen] = char_type(0);
-  this->mLength = aNewLen;
+  mData[aNewLen] = char_type(0);
+  mLength = aNewLen;
 
   return true;
 }
 
-template <typename T>
-typename nsTSubstring<T>::size_type
-nsTSubstring<T>::Capacity() const
+nsTSubstring_CharT::size_type
+nsTSubstring_CharT::Capacity() const
 {
   // return 0 to indicate an immutable or 0-sized buffer
 
   size_type capacity;
-  if (this->mDataFlags & DataFlags::SHARED) {
+  if (mDataFlags & DataFlags::SHARED) {
     // if the string is readonly, then we pretend that it has no capacity.
-    nsStringBuffer* hdr = nsStringBuffer::FromData(this->mData);
+    nsStringBuffer* hdr = nsStringBuffer::FromData(mData);
     if (hdr->IsReadonly()) {
       capacity = 0;
     } else {
       capacity = (hdr->StorageSize() / sizeof(char_type)) - 1;
     }
-  } else if (this->mDataFlags & DataFlags::FIXED) {
+  } else if (mDataFlags & DataFlags::FIXED) {
     capacity = AsFixedString(this)->mFixedCapacity;
-  } else if (this->mDataFlags & DataFlags::OWNED) {
+  } else if (mDataFlags & DataFlags::OWNED) {
     // we don't store the capacity of an adopted buffer because that would
     // require an additional member field.  the best we can do is base the
     // capacity on our length.  remains to be seen if this is the right
     // trade-off.
-    capacity = this->mLength;
+    capacity = mLength;
   } else {
     capacity = 0;
   }
 
   return capacity;
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::EnsureMutable(size_type aNewLen)
+nsTSubstring_CharT::EnsureMutable(size_type aNewLen)
 {
-  if (aNewLen == size_type(-1) || aNewLen == this->mLength) {
-    if (this->mDataFlags & (DataFlags::FIXED | DataFlags::OWNED)) {
+  if (aNewLen == size_type(-1) || aNewLen == mLength) {
+    if (mDataFlags & (DataFlags::FIXED | DataFlags::OWNED)) {
       return true;
     }
-    if ((this->mDataFlags & DataFlags::SHARED) &&
-        !nsStringBuffer::FromData(this->mData)->IsReadonly()) {
+    if ((mDataFlags & DataFlags::SHARED) &&
+        !nsStringBuffer::FromData(mData)->IsReadonly()) {
       return true;
     }
 
-    aNewLen = this->mLength;
+    aNewLen = mLength;
   }
   return SetLength(aNewLen, mozilla::fallible);
 }
 
 // ---------------------------------------------------------------------------
 
 // This version of Assign is optimized for single-character assignment.
-template <typename T>
 void
-nsTSubstring<T>::Assign(char_type aChar)
+nsTSubstring_CharT::Assign(char_type aChar)
 {
-  if (!ReplacePrep(0, this->mLength, 1)) {
-    AllocFailed(this->mLength);
+  if (!ReplacePrep(0, mLength, 1)) {
+    AllocFailed(mLength);
   }
 
-  *this->mData = aChar;
+  *mData = aChar;
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::Assign(char_type aChar, const fallible_t&)
+nsTSubstring_CharT::Assign(char_type aChar, const fallible_t&)
 {
-  if (!ReplacePrep(0, this->mLength, 1)) {
+  if (!ReplacePrep(0, mLength, 1)) {
     return false;
   }
 
-  *this->mData = aChar;
+  *mData = aChar;
   return true;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::Assign(const char_type* aData)
+nsTSubstring_CharT::Assign(const char_type* aData)
 {
   if (!Assign(aData, mozilla::fallible)) {
     AllocFailed(char_traits::length(aData));
   }
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::Assign(const char_type* aData, const fallible_t&)
+nsTSubstring_CharT::Assign(const char_type* aData, const fallible_t&)
 {
   return Assign(aData, size_type(-1), mozilla::fallible);
 }
 
-template <typename T>
 void
-nsTSubstring<T>::Assign(const char_type* aData, size_type aLength)
+nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength)
 {
   if (!Assign(aData, aLength, mozilla::fallible)) {
     AllocFailed(aLength == size_type(-1) ? char_traits::length(aData)
                                          : aLength);
   }
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::Assign(const char_type* aData, size_type aLength,
-                        const fallible_t& aFallible)
+nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength,
+                           const fallible_t& aFallible)
 {
   if (!aData || aLength == 0) {
     Truncate();
     return true;
   }
 
   if (aLength == size_type(-1)) {
     aLength = char_traits::length(aData);
   }
 
-  if (this->IsDependentOn(aData, aData + aLength)) {
+  if (IsDependentOn(aData, aData + aLength)) {
     return Assign(string_type(aData, aLength), aFallible);
   }
 
-  if (!ReplacePrep(0, this->mLength, aLength)) {
+  if (!ReplacePrep(0, mLength, aLength)) {
     return false;
   }
 
-  char_traits::copy(this->mData, aData, aLength);
+  char_traits::copy(mData, aData, aLength);
   return true;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::AssignASCII(const char* aData, size_type aLength)
+nsTSubstring_CharT::AssignASCII(const char* aData, size_type aLength)
 {
   if (!AssignASCII(aData, aLength, mozilla::fallible)) {
     AllocFailed(aLength);
   }
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::AssignASCII(const char* aData, size_type aLength,
+nsTSubstring_CharT::AssignASCII(const char* aData, size_type aLength,
                                 const fallible_t& aFallible)
 {
   // A Unicode string can't depend on an ASCII string buffer,
   // so this dependence check only applies to CStrings.
 #ifdef CharT_is_char
-  if (this->IsDependentOn(aData, aData + aLength)) {
+  if (IsDependentOn(aData, aData + aLength)) {
     return Assign(string_type(aData, aLength), aFallible);
   }
 #endif
 
-  if (!ReplacePrep(0, this->mLength, aLength)) {
+  if (!ReplacePrep(0, mLength, aLength)) {
     return false;
   }
 
-  char_traits::copyASCII(this->mData, aData, aLength);
+  char_traits::copyASCII(mData, aData, aLength);
   return true;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::AssignLiteral(const char_type* aData, size_type aLength)
+nsTSubstring_CharT::AssignLiteral(const char_type* aData, size_type aLength)
 {
-  ::ReleaseData(this->mData, this->mDataFlags);
+  ::ReleaseData(mData, mDataFlags);
   SetData(const_cast<char_type*>(aData), aLength,
           DataFlags::TERMINATED | DataFlags::LITERAL);
 }
 
-template <typename T>
 void
-nsTSubstring<T>::Assign(const self_type& aStr)
+nsTSubstring_CharT::Assign(const self_type& aStr)
 {
   if (!Assign(aStr, mozilla::fallible)) {
     AllocFailed(aStr.Length());
   }
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::Assign(const self_type& aStr, const fallible_t& aFallible)
+nsTSubstring_CharT::Assign(const self_type& aStr, const fallible_t& aFallible)
 {
   // |aStr| could be sharable. We need to check its flags to know how to
   // deal with it.
 
   if (&aStr == this) {
     return true;
   }
 
   if (!aStr.mLength) {
     Truncate();
-    this->mDataFlags |= aStr.mDataFlags & DataFlags::VOIDED;
+    mDataFlags |= aStr.mDataFlags & DataFlags::VOIDED;
     return true;
   }
 
   if (aStr.mDataFlags & DataFlags::SHARED) {
     // nice! we can avoid a string copy :-)
 
     // |aStr| should be null-terminated
     NS_ASSERTION(aStr.mDataFlags & DataFlags::TERMINATED, "shared, but not terminated");
 
-    ::ReleaseData(this->mData, this->mDataFlags);
+    ::ReleaseData(mData, mDataFlags);
 
     SetData(aStr.mData, aStr.mLength,
             DataFlags::TERMINATED | DataFlags::SHARED);
 
-    // get an owning reference to the this->mData
-    nsStringBuffer::FromData(this->mData)->AddRef();
+    // get an owning reference to the mData
+    nsStringBuffer::FromData(mData)->AddRef();
     return true;
   } else if (aStr.mDataFlags & DataFlags::LITERAL) {
     MOZ_ASSERT(aStr.mDataFlags & DataFlags::TERMINATED, "Unterminated literal");
 
     AssignLiteral(aStr.mData, aStr.mLength);
     return true;
   }
 
   // else, treat this like an ordinary assignment.
   return Assign(aStr.Data(), aStr.Length(), aFallible);
 }
 
-template <typename T>
 void
-nsTSubstring<T>::Assign(const substring_tuple_type& aTuple)
+nsTSubstring_CharT::Assign(const substring_tuple_type& aTuple)
 {
   if (!Assign(aTuple, mozilla::fallible)) {
     AllocFailed(aTuple.Length());
   }
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::Assign(const substring_tuple_type& aTuple,
-                        const fallible_t& aFallible)
+nsTSubstring_CharT::Assign(const substring_tuple_type& aTuple,
+                           const fallible_t& aFallible)
 {
-  if (aTuple.IsDependentOn(this->mData, this->mData + this->mLength)) {
+  if (aTuple.IsDependentOn(mData, mData + mLength)) {
     // take advantage of sharing here...
     return Assign(string_type(aTuple), aFallible);
   }
 
   size_type length = aTuple.Length();
 
   // don't use ReplacePrep here because it changes the length
   char_type* oldData;
@@ -510,573 +488,540 @@ nsTSubstring<T>::Assign(const substring_
   if (!MutatePrep(length, &oldData, &oldFlags)) {
     return false;
   }
 
   if (oldData) {
     ::ReleaseData(oldData, oldFlags);
   }
 
-  aTuple.WriteTo(this->mData, length);
-  this->mData[length] = 0;
-  this->mLength = length;
+  aTuple.WriteTo(mData, length);
+  mData[length] = 0;
+  mLength = length;
   return true;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::Adopt(char_type* aData, size_type aLength)
+nsTSubstring_CharT::Adopt(char_type* aData, size_type aLength)
 {
   if (aData) {
-    ::ReleaseData(this->mData, this->mDataFlags);
+    ::ReleaseData(mData, mDataFlags);
 
     if (aLength == size_type(-1)) {
       aLength = char_traits::length(aData);
     }
 
     MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "adopting a too-long string");
 
     SetData(aData, aLength, DataFlags::TERMINATED | DataFlags::OWNED);
 
     STRING_STAT_INCREMENT(Adopt);
     // Treat this as construction of a "StringAdopt" object for leak
     // tracking purposes.
-    MOZ_LOG_CTOR(this->mData, "StringAdopt", 1);
+    MOZ_LOG_CTOR(mData, "StringAdopt", 1);
   } else {
     SetIsVoid(true);
   }
 }
 
 
 // This version of Replace is optimized for single-character replacement.
-template <typename T>
 void
-nsTSubstring<T>::Replace(index_type aCutStart, size_type aCutLength,
-                         char_type aChar)
+nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
+                            char_type aChar)
 {
-  aCutStart = XPCOM_MIN(aCutStart, this->Length());
+  aCutStart = XPCOM_MIN(aCutStart, Length());
 
   if (ReplacePrep(aCutStart, aCutLength, 1)) {
-    this->mData[aCutStart] = aChar;
+    mData[aCutStart] = aChar;
   }
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::Replace(index_type aCutStart, size_type aCutLength,
-                         char_type aChar,
-                         const fallible_t&)
+nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
+                            char_type aChar,
+                            const fallible_t&)
 {
-  aCutStart = XPCOM_MIN(aCutStart, this->Length());
+  aCutStart = XPCOM_MIN(aCutStart, Length());
 
   if (!ReplacePrep(aCutStart, aCutLength, 1)) {
     return false;
   }
 
-  this->mData[aCutStart] = aChar;
+  mData[aCutStart] = aChar;
 
   return true;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::Replace(index_type aCutStart, size_type aCutLength,
-                         const char_type* aData, size_type aLength)
+nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
+                            const char_type* aData, size_type aLength)
 {
   if (!Replace(aCutStart, aCutLength, aData, aLength,
                mozilla::fallible)) {
-    AllocFailed(this->Length() - aCutLength + 1);
+    AllocFailed(Length() - aCutLength + 1);
   }
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::Replace(index_type aCutStart, size_type aCutLength,
-                         const char_type* aData, size_type aLength,
-                         const fallible_t& aFallible)
+nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
+                            const char_type* aData, size_type aLength,
+                            const fallible_t& aFallible)
 {
   // unfortunately, some callers pass null :-(
   if (!aData) {
     aLength = 0;
   } else {
     if (aLength == size_type(-1)) {
       aLength = char_traits::length(aData);
     }
 
-    if (this->IsDependentOn(aData, aData + aLength)) {
-      nsTAutoString<T> temp(aData, aLength);
+    if (IsDependentOn(aData, aData + aLength)) {
+      nsTAutoString_CharT temp(aData, aLength);
       return Replace(aCutStart, aCutLength, temp, aFallible);
     }
   }
 
-  aCutStart = XPCOM_MIN(aCutStart, this->Length());
+  aCutStart = XPCOM_MIN(aCutStart, Length());
 
   bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
   if (!ok) {
     return false;
   }
 
   if (aLength > 0) {
-    char_traits::copy(this->mData + aCutStart, aData, aLength);
+    char_traits::copy(mData + aCutStart, aData, aLength);
   }
 
   return true;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::ReplaceASCII(index_type aCutStart, size_type aCutLength,
-                              const char* aData, size_type aLength)
+nsTSubstring_CharT::ReplaceASCII(index_type aCutStart, size_type aCutLength,
+                                 const char* aData, size_type aLength)
 {
   if (!ReplaceASCII(aCutStart, aCutLength, aData, aLength, mozilla::fallible)) {
-    AllocFailed(this->Length() - aCutLength + 1);
+    AllocFailed(Length() - aCutLength + 1);
   }
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::ReplaceASCII(index_type aCutStart, size_type aCutLength,
-                              const char* aData, size_type aLength,
-                              const fallible_t& aFallible)
+nsTSubstring_CharT::ReplaceASCII(index_type aCutStart, size_type aCutLength,
+                                 const char* aData, size_type aLength,
+                                 const fallible_t& aFallible)
 {
   if (aLength == size_type(-1)) {
     aLength = strlen(aData);
   }
 
   // A Unicode string can't depend on an ASCII string buffer,
   // so this dependence check only applies to CStrings.
 #ifdef CharT_is_char
-  if (this->IsDependentOn(aData, aData + aLength)) {
+  if (IsDependentOn(aData, aData + aLength)) {
     nsTAutoString_CharT temp(aData, aLength);
     return Replace(aCutStart, aCutLength, temp, aFallible);
   }
 #endif
 
-  aCutStart = XPCOM_MIN(aCutStart, this->Length());
+  aCutStart = XPCOM_MIN(aCutStart, Length());
 
   bool ok = ReplacePrep(aCutStart, aCutLength, aLength);
   if (!ok) {
     return false;
   }
 
   if (aLength > 0) {
-    char_traits::copyASCII(this->mData + aCutStart, aData, aLength);
+    char_traits::copyASCII(mData + aCutStart, aData, aLength);
   }
 
   return true;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::Replace(index_type aCutStart, size_type aCutLength,
-                         const substring_tuple_type& aTuple)
+nsTSubstring_CharT::Replace(index_type aCutStart, size_type aCutLength,
+                            const substring_tuple_type& aTuple)
 {
-  if (aTuple.IsDependentOn(this->mData, this->mData + this->mLength)) {
-    nsTAutoString<T> temp(aTuple);
+  if (aTuple.IsDependentOn(mData, mData + mLength)) {
+    nsTAutoString_CharT temp(aTuple);
     Replace(aCutStart, aCutLength, temp);
     return;
   }
 
   size_type length = aTuple.Length();
 
-  aCutStart = XPCOM_MIN(aCutStart, this->Length());
+  aCutStart = XPCOM_MIN(aCutStart, Length());
 
   if (ReplacePrep(aCutStart, aCutLength, length) && length > 0) {
-    aTuple.WriteTo(this->mData + aCutStart, length);
+    aTuple.WriteTo(mData + aCutStart, length);
   }
 }
 
-template <typename T>
 void
-nsTSubstring<T>::ReplaceLiteral(index_type aCutStart, size_type aCutLength,
-                                const char_type* aData, size_type aLength)
+nsTSubstring_CharT::ReplaceLiteral(index_type aCutStart, size_type aCutLength,
+                                   const char_type* aData, size_type aLength)
 {
-  aCutStart = XPCOM_MIN(aCutStart, this->Length());
+  aCutStart = XPCOM_MIN(aCutStart, Length());
 
-  if (!aCutStart && aCutLength == this->Length()) {
+  if (!aCutStart && aCutLength == Length()) {
     AssignLiteral(aData, aLength);
   } else if (ReplacePrep(aCutStart, aCutLength, aLength) && aLength > 0) {
-    char_traits::copy(this->mData + aCutStart, aData, aLength);
+    char_traits::copy(mData + aCutStart, aData, aLength);
   }
 }
 
-template <typename T>
 void
-nsTSubstring<T>::SetCapacity(size_type aCapacity)
+nsTSubstring_CharT::SetCapacity(size_type aCapacity)
 {
   if (!SetCapacity(aCapacity, mozilla::fallible)) {
     AllocFailed(aCapacity);
   }
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::SetCapacity(size_type aCapacity, const fallible_t&)
+nsTSubstring_CharT::SetCapacity(size_type aCapacity, const fallible_t&)
 {
   // capacity does not include room for the terminating null char
 
   // if our capacity is reduced to zero, then free our buffer.
   if (aCapacity == 0) {
-    ::ReleaseData(this->mData, this->mDataFlags);
+    ::ReleaseData(mData, mDataFlags);
     SetToEmptyBuffer();
     return true;
   }
 
   char_type* oldData;
   DataFlags oldFlags;
   if (!MutatePrep(aCapacity, &oldData, &oldFlags)) {
     return false;  // out-of-memory
   }
 
   // compute new string length
-  size_type newLen = XPCOM_MIN(this->mLength, aCapacity);
+  size_type newLen = XPCOM_MIN(mLength, aCapacity);
 
   if (oldData) {
     // preserve old data
-    if (this->mLength > 0) {
-      char_traits::copy(this->mData, oldData, newLen);
+    if (mLength > 0) {
+      char_traits::copy(mData, oldData, newLen);
     }
 
     ::ReleaseData(oldData, oldFlags);
   }
 
-  // adjust this->mLength if our buffer shrunk down in size
-  if (newLen < this->mLength) {
-    this->mLength = newLen;
+  // adjust mLength if our buffer shrunk down in size
+  if (newLen < mLength) {
+    mLength = newLen;
   }
 
   // always null-terminate here, even if the buffer got longer.  this is
   // for backwards compat with the old string implementation.
-  this->mData[aCapacity] = char_type(0);
+  mData[aCapacity] = char_type(0);
 
   return true;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::SetLength(size_type aLength)
+nsTSubstring_CharT::SetLength(size_type aLength)
 {
   SetCapacity(aLength);
-  this->mLength = aLength;
+  mLength = aLength;
 }
 
-template <typename T>
 bool
-nsTSubstring<T>::SetLength(size_type aLength, const fallible_t& aFallible)
+nsTSubstring_CharT::SetLength(size_type aLength, const fallible_t& aFallible)
 {
   if (!SetCapacity(aLength, aFallible)) {
     return false;
   }
 
-  this->mLength = aLength;
+  mLength = aLength;
   return true;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::SetIsVoid(bool aVal)
+nsTSubstring_CharT::SetIsVoid(bool aVal)
 {
   if (aVal) {
     Truncate();
-    this->mDataFlags |= DataFlags::VOIDED;
+    mDataFlags |= DataFlags::VOIDED;
   } else {
-    this->mDataFlags &= ~DataFlags::VOIDED;
+    mDataFlags &= ~DataFlags::VOIDED;
   }
 }
 
 namespace mozilla {
 namespace detail {
 
-template <typename T>
-typename nsTStringRepr<T>::char_type
-nsTStringRepr<T>::First() const
+nsTStringRepr_CharT::char_type
+nsTStringRepr_CharT::First() const
 {
-  MOZ_RELEASE_ASSERT(this->mLength > 0, "|First()| called on an empty string");
-  return this->mData[0];
+  MOZ_RELEASE_ASSERT(mLength > 0, "|First()| called on an empty string");
+  return mData[0];
 }
 
-template <typename T>
-typename nsTStringRepr<T>::char_type
-nsTStringRepr<T>::Last() const
+nsTStringRepr_CharT::char_type
+nsTStringRepr_CharT::Last() const
 {
-  MOZ_RELEASE_ASSERT(this->mLength > 0, "|Last()| called on an empty string");
-  return this->mData[this->mLength - 1];
+  MOZ_RELEASE_ASSERT(mLength > 0, "|Last()| called on an empty string");
+  return mData[mLength - 1];
 }
 
-template <typename T>
 bool
-nsTStringRepr<T>::Equals(const self_type& aStr) const
+nsTStringRepr_CharT::Equals(const self_type& aStr) const
 {
-  return this->mLength == aStr.mLength &&
-         char_traits::compare(this->mData, aStr.mData, this->mLength) == 0;
+  return mLength == aStr.mLength &&
+         char_traits::compare(mData, aStr.mData, mLength) == 0;
 }
 
-template <typename T>
 bool
-nsTStringRepr<T>::Equals(const self_type& aStr,
-                         const comparator_type& aComp) const
+nsTStringRepr_CharT::Equals(const self_type& aStr,
+                            const comparator_type& aComp) const
 {
-  return this->mLength == aStr.mLength &&
-         aComp(this->mData, aStr.mData, this->mLength, aStr.mLength) == 0;
+  return mLength == aStr.mLength &&
+         aComp(mData, aStr.mData, mLength, aStr.mLength) == 0;
 }
 
-template <typename T>
 bool
-nsTStringRepr<T>::Equals(const substring_tuple_type& aTuple) const
+nsTStringRepr_CharT::Equals(const substring_tuple_type& aTuple) const
 {
   return Equals(substring_type(aTuple));
 }
 
-template <typename T>
 bool
-nsTStringRepr<T>::Equals(const substring_tuple_type& aTuple,
-                         const comparator_type& aComp) const
+nsTStringRepr_CharT::Equals(const substring_tuple_type& aTuple,
+                            const comparator_type& aComp) const
 {
   return Equals(substring_type(aTuple), aComp);
 }
 
-template <typename T>
 bool
-nsTStringRepr<T>::Equals(const char_type* aData) const
+nsTStringRepr_CharT::Equals(const char_type* aData) const
 {
   // unfortunately, some callers pass null :-(
   if (!aData) {
     NS_NOTREACHED("null data pointer");
-    return this->mLength == 0;
+    return mLength == 0;
   }
 
   // XXX avoid length calculation?
   size_type length = char_traits::length(aData);
-  return this->mLength == length &&
-         char_traits::compare(this->mData, aData, this->mLength) == 0;
+  return mLength == length &&
+         char_traits::compare(mData, aData, mLength) == 0;
 }
 
-template <typename T>
 bool
-nsTStringRepr<T>::Equals(const char_type* aData,
-                         const comparator_type& aComp) const
+nsTStringRepr_CharT::Equals(const char_type* aData,
+                            const comparator_type& aComp) const
 {
   // unfortunately, some callers pass null :-(
   if (!aData) {
     NS_NOTREACHED("null data pointer");
-    return this->mLength == 0;
+    return mLength == 0;
   }
 
   // XXX avoid length calculation?
   size_type length = char_traits::length(aData);
-  return this->mLength == length && aComp(this->mData, aData, this->mLength, length) == 0;
+  return mLength == length && aComp(mData, aData, mLength, length) == 0;
 }
 
-template <typename T>
 bool
-nsTStringRepr<T>::EqualsASCII(const char* aData, size_type aLen) const
+nsTStringRepr_CharT::EqualsASCII(const char* aData, size_type aLen) const
 {
-  return this->mLength == aLen &&
-         char_traits::compareASCII(this->mData, aData, aLen) == 0;
+  return mLength == aLen &&
+         char_traits::compareASCII(mData, aData, aLen) == 0;
 }
 
-template <typename T>
 bool
-nsTStringRepr<T>::EqualsASCII(const char* aData) const
+nsTStringRepr_CharT::EqualsASCII(const char* aData) const
 {
-  return char_traits::compareASCIINullTerminated(this->mData, this->mLength, aData) == 0;
+  return char_traits::compareASCIINullTerminated(mData, mLength, aData) == 0;
 }
 
-template <typename T>
 bool
-nsTStringRepr<T>::LowerCaseEqualsASCII(const char* aData,
-                                       size_type aLen) const
+nsTStringRepr_CharT::LowerCaseEqualsASCII(const char* aData,
+                                          size_type aLen) const
 {
-  return this->mLength == aLen &&
-         char_traits::compareLowerCaseToASCII(this->mData, aData, aLen) == 0;
+  return mLength == aLen &&
+         char_traits::compareLowerCaseToASCII(mData, aData, aLen) == 0;
 }
 
-template <typename T>
 bool
-nsTStringRepr<T>::LowerCaseEqualsASCII(const char* aData) const
+nsTStringRepr_CharT::LowerCaseEqualsASCII(const char* aData) const
 {
-  return char_traits::compareLowerCaseToASCIINullTerminated(this->mData,
-                                                            this->mLength,
+  return char_traits::compareLowerCaseToASCIINullTerminated(mData,
+                                                            mLength,
                                                             aData) == 0;
 }
 
-template <typename T>
-typename nsTStringRepr<T>::size_type
-nsTStringRepr<T>::CountChar(char_type aChar) const
+nsTStringRepr_CharT::size_type
+nsTStringRepr_CharT::CountChar(char_type aChar) const
 {
-  const char_type* start = this->mData;
-  const char_type* end   = this->mData + this->mLength;
+  const char_type* start = mData;
+  const char_type* end   = mData + mLength;
 
   return NS_COUNT(start, end, aChar);
 }
 
-template <typename T>
 int32_t
-nsTStringRepr<T>::FindChar(char_type aChar, index_type aOffset) const
+nsTStringRepr_CharT::FindChar(char_type aChar, index_type aOffset) const
 {
-  if (aOffset < this->mLength) {
-    const char_type* result = char_traits::find(this->mData + aOffset,
-                                                this->mLength - aOffset, aChar);
+  if (aOffset < mLength) {
+    const char_type* result = char_traits::find(mData + aOffset,
+                                                mLength - aOffset, aChar);
     if (result) {
-      return result - this->mData;
+      return result - mData;
     }
   }
   return -1;
 }
 
 } // namespace detail
 } // namespace mozilla
 
-template <typename T>
 void
-nsTSubstring<T>::StripChar(char_type aChar)
+nsTSubstring_CharT::StripChar(char_type aChar)
 {
-  if (this->mLength == 0) {
+  if (mLength == 0) {
     return;
   }
 
   if (!EnsureMutable()) { // XXX do this lazily?
-    AllocFailed(this->mLength);
+    AllocFailed(mLength);
   }
 
   // XXX(darin): this code should defer writing until necessary.
 
-  char_type* to   = this->mData;
-  char_type* from = this->mData;
-  char_type* end  = this->mData + this->mLength;
+  char_type* to   = mData;
+  char_type* from = mData;
+  char_type* end  = mData + mLength;
 
   while (from < end) {
     char_type theChar = *from++;
     if (aChar != theChar) {
       *to++ = theChar;
     }
   }
   *to = char_type(0); // add the null
-  this->mLength = to - this->mData;
+  mLength = to - mData;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::StripChars(const char_type* aChars)
+nsTSubstring_CharT::StripChars(const char_type* aChars)
 {
-  if (this->mLength == 0) {
+  if (mLength == 0) {
     return;
   }
 
   if (!EnsureMutable()) { // XXX do this lazily?
-    AllocFailed(this->mLength);
+    AllocFailed(mLength);
   }
 
   // XXX(darin): this code should defer writing until necessary.
 
-  char_type* to   = this->mData;
-  char_type* from = this->mData;
-  char_type* end  = this->mData + this->mLength;
+  char_type* to   = mData;
+  char_type* from = mData;
+  char_type* end  = mData + mLength;
 
   while (from < end) {
     char_type theChar = *from++;
     const char_type* test = aChars;
 
     for (; *test && *test != theChar; ++test);
 
     if (!*test) {
       // Not stripped, copy this char.
       *to++ = theChar;
     }
   }
   *to = char_type(0); // add the null
-  this->mLength = to - this->mData;
+  mLength = to - mData;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::StripTaggedASCII(const ASCIIMaskArray& aToStrip)
+nsTSubstring_CharT::StripTaggedASCII(const ASCIIMaskArray& aToStrip)
 {
-  if (this->mLength == 0) {
+  if (mLength == 0) {
     return;
   }
 
   if (!EnsureMutable()) {
-    AllocFailed(this->mLength);
+    AllocFailed(mLength);
   }
 
-  char_type* to   = this->mData;
-  char_type* from = this->mData;
-  char_type* end  = this->mData + this->mLength;
+  char_type* to   = mData;
+  char_type* from = mData;
+  char_type* end  = mData + mLength;
 
   while (from < end) {
     uint32_t theChar = (uint32_t)*from++;
     // Replacing this with a call to ASCIIMask::IsMasked
     // regresses performance somewhat, so leaving it inlined.
     if (!mozilla::ASCIIMask::IsMasked(aToStrip, theChar)) {
       // Not stripped, copy this char.
       *to++ = (char_type)theChar;
     }
   }
   *to = char_type(0); // add the null
-  this->mLength = to - this->mData;
+  mLength = to - mData;
 }
 
-template <typename T>
 void
-nsTSubstring<T>::StripCRLF()
+nsTSubstring_CharT::StripCRLF()
 {
   // Expanding this call to copy the code from StripTaggedASCII
   // instead of just calling it does somewhat help with performance
   // but it is not worth it given the duplicated code.
   StripTaggedASCII(mozilla::ASCIIMask::MaskCRLF());
 }
 
-template <typename T>
-struct MOZ_STACK_CLASS PrintfAppend : public mozilla::PrintfTarget
+struct MOZ_STACK_CLASS PrintfAppend_CharT : public mozilla::PrintfTarget
 {
-  explicit PrintfAppend(nsTSubstring<T>* aString)
+  explicit PrintfAppend_CharT(nsTSubstring_CharT* aString)
     : mString(aString)
   {
   }
 
   bool append(const char* aStr, size_t aLen) override {
     if (aLen == 0) {
       return true;
     }
 
     mString->AppendASCII(aStr, aLen);
     return true;
   }
 
 private:
 
-  nsTSubstring<T>* mString;
+  nsTSubstring_CharT* mString;
 };
 
-template <typename T>
 void
-nsTSubstring<T>::AppendPrintf(const char* aFormat, ...)
+nsTSubstring_CharT::AppendPrintf(const char* aFormat, ...)
 {
-  PrintfAppend<T> appender(this);
+  PrintfAppend_CharT appender(this);
   va_list ap;
   va_start(ap, aFormat);
   bool r = appender.vprint(aFormat, ap);
   if (!r) {
     MOZ_CRASH("Allocation or other failure in PrintfTarget::print");
   }
   va_end(ap);
 }
 
-template <typename T>
 void
-nsTSubstring<T>::AppendPrintf(const char* aFormat, va_list aAp)
+nsTSubstring_CharT::AppendPrintf(const char* aFormat, va_list aAp)
 {
-  PrintfAppend<T> appender(this);
+  PrintfAppend_CharT appender(this);
   bool r = appender.vprint(aFormat, aAp);
   if (!r) {
     MOZ_CRASH("Allocation or other failure in PrintfTarget::print");
   }
 }
 
+/* hack to make sure we define FormatWithoutTrailingZeros only once */
+#ifdef CharT_is_PRUnichar
 // Returns the length of the formatted aDouble in aBuf.
 static int
 FormatWithoutTrailingZeros(char (&aBuf)[40], double aDouble,
                            int aPrecision)
 {
   static const DoubleToStringConverter converter(DoubleToStringConverter::UNIQUE_ZERO |
                                                  DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
                                                  "Infinity",
@@ -1137,108 +1082,102 @@ FormatWithoutTrailingZeros(char (&aBuf)[
     if (trailingZeros == decimalPoint) {
       --trailingZeros;
     }
     length -= end - (trailingZeros + 1);
   }
 
   return length;
 }
+#endif /* CharT_is_PRUnichar */
 
-template <typename T>
 void
-nsTSubstring<T>::AppendFloat(float aFloat)
+nsTSubstring_CharT::AppendFloat(float aFloat)
 {
   char buf[40];
   int length = FormatWithoutTrailingZeros(buf, aFloat, 6);
   AppendASCII(buf, length);
 }
 
-template <typename T>
 void
-nsTSubstring<T>::AppendFloat(double aFloat)
+nsTSubstring_CharT::AppendFloat(double aFloat)
 {
   char buf[40];
   int length = FormatWithoutTrailingZeros(buf, aFloat, 15);
   AppendASCII(buf, length);
 }
 
-template <typename T>
 size_t
-nsTSubstring<T>::SizeOfExcludingThisIfUnshared(
+nsTSubstring_CharT::SizeOfExcludingThisIfUnshared(
     mozilla::MallocSizeOf aMallocSizeOf) const
 {
-  if (this->mDataFlags & DataFlags::SHARED) {
-    return nsStringBuffer::FromData(this->mData)->
+  if (mDataFlags & DataFlags::SHARED) {
+    return nsStringBuffer::FromData(mData)->
       SizeOfIncludingThisIfUnshared(aMallocSizeOf);
   }
-  if (this->mDataFlags & DataFlags::OWNED) {
-    return aMallocSizeOf(this->mData);
+  if (mDataFlags & DataFlags::OWNED) {
+    return aMallocSizeOf(mData);
   }
 
   // If we reach here, exactly one of the following must be true:
-  // - DataFlags::VOIDED is set, and this->mData points to sEmptyBuffer;
-  // - DataFlags::FIXED is set, and this->mData points to a buffer within a string
+  // - DataFlags::VOIDED is set, and mData points to sEmptyBuffer;
+  // - DataFlags::FIXED is set, and mData points to a buffer within a string
   //   object (e.g. nsAutoString);
-  // - None of DataFlags::SHARED, DataFlags::OWNED, DataFlags::FIXED is set, and this->mData points to a buffer
+  // - None of DataFlags::SHARED, DataFlags::OWNED, DataFlags::FIXED is set, and mData points to a buffer
   //   owned by something else.
   //
   // In all three cases, we don't measure it.
   return 0;
 }
 
-template <typename T>
 size_t
-nsTSubstring<T>::SizeOfExcludingThisEvenIfShared(
+nsTSubstring_CharT::SizeOfExcludingThisEvenIfShared(
     mozilla::MallocSizeOf aMallocSizeOf) const
 {
   // This is identical to SizeOfExcludingThisIfUnshared except for the
   // DataFlags::SHARED case.
-  if (this->mDataFlags & DataFlags::SHARED) {
-    return nsStringBuffer::FromData(this->mData)->
+  if (mDataFlags & DataFlags::SHARED) {
+    return nsStringBuffer::FromData(mData)->
       SizeOfIncludingThisEvenIfShared(aMallocSizeOf);
   }
-  if (this->mDataFlags & DataFlags::OWNED) {
-    return aMallocSizeOf(this->mData);
+  if (mDataFlags & DataFlags::OWNED) {
+    return aMallocSizeOf(mData);
   }
   return 0;
 }
 
-template <typename T>
 size_t
-nsTSubstring<T>::SizeOfIncludingThisIfUnshared(
+nsTSubstring_CharT::SizeOfIncludingThisIfUnshared(
     mozilla::MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this) + SizeOfExcludingThisIfUnshared(aMallocSizeOf);
 }
 
-template <typename T>
 size_t
-nsTSubstring<T>::SizeOfIncludingThisEvenIfShared(
+nsTSubstring_CharT::SizeOfIncludingThisEvenIfShared(
     mozilla::MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this) + SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
 }
 
-template <typename T>
 inline
-nsTSubstringSplitter<T>::nsTSubstringSplitter(
-    const nsTSubstring<T>* aStr, char_type aDelim)
+nsTSubstringSplitter_CharT::nsTSubstringSplitter_CharT(
+    const nsTSubstring_CharT* aStr, char_type aDelim)
   : mStr(aStr)
   , mArray(nullptr)
   , mDelim(aDelim)
 {
   if (mStr->IsEmpty()) {
     mArraySize = 0;
     return;
   }
 
   size_type delimCount = mStr->CountChar(aDelim);
   mArraySize = delimCount + 1;
-  mArray.reset(new nsTDependentSubstring<T>[mArraySize]);
+  mArray.reset(new nsTDependentSubstring_CharT[mArraySize]);
 
   size_t seenParts = 0;
   size_type start = 0;
   do {
     MOZ_ASSERT(seenParts < mArraySize);
     int32_t offset = mStr->FindChar(aDelim, start);
     if (offset != -1) {
       size_type length = static_cast<size_type>(offset) - start;
@@ -1247,21 +1186,19 @@ nsTSubstringSplitter<T>::nsTSubstringSpl
     } else {
       // Get the remainder
       mArray[seenParts++].Rebind(mStr->Data() + start, mStr->Length() - start);
       break;
     }
   } while (start < mStr->Length());
 }
 
-template <typename T>
-nsTSubstringSplitter<T>
-nsTSubstring<T>::Split(const char_type aChar) const
+nsTSubstringSplitter_CharT
+nsTSubstring_CharT::Split(const char_type aChar) const
 {
-  return nsTSubstringSplitter<T>(this, aChar);
+  return nsTSubstringSplitter_CharT(this, aChar);
 }
 
-template <typename T>
-const nsTDependentSubstring<T>&
-nsTSubstringSplitter<T>::nsTSubstringSplit_Iter::operator* () const
+const nsTDependentSubstring_CharT&
+nsTSubstringSplitter_CharT::nsTSubstringSplit_Iter::operator* () const
 {
    return mObj.Get(mPos);
 }
--- a/xpcom/string/nsTSubstring.h
+++ b/xpcom/string/nsTSubstring.h
@@ -1,119 +1,387 @@
 /* -*- 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/. */
 // IWYU pragma: private, include "nsString.h"
 
-#ifndef nsTSubstring_h
-#define nsTSubstring_h
-
 #include "mozilla/Casting.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/IntegerTypeTraits.h"
 #include "mozilla/Span.h"
 
-#include "nsTStringRepr.h"
-
 #ifndef MOZILLA_INTERNAL_API
 #error "Using XPCOM strings is limited to code linked into libxul."
 #endif
 
-template <typename T> class nsTSubstringSplitter;
-template <typename T> class nsTString;
+/**
+ * The base for string comparators
+ */
+class nsTStringComparator_CharT
+{
+public:
+  typedef CharT char_type;
+
+  nsTStringComparator_CharT()
+  {
+  }
+
+  virtual int operator()(const char_type*, const char_type*,
+                         uint32_t, uint32_t) const = 0;
+};
+
+
+/**
+ * The default string comparator (case-sensitive comparision)
+ */
+class nsTDefaultStringComparator_CharT
+  : public nsTStringComparator_CharT
+{
+public:
+  typedef CharT char_type;
+
+  nsTDefaultStringComparator_CharT()
+  {
+  }
+
+  virtual int operator()(const char_type*, const char_type*,
+                         uint32_t, uint32_t) const override;
+};
+
+class nsTSubstringSplitter_CharT;
+
+namespace mozilla {
+namespace detail {
+
+/**
+ * nsTStringRepr defines a string's memory layout and some accessor methods.
+ * This class exists so that nsTLiteralString can avoid inheriting
+ * nsTSubstring's destructor. All methods on this class must be const because
+ * literal strings are not writable.
+ *
+ * This class is an implementation detail and should not be instantiated
+ * directly, nor used in any way outside of the string code itself. It is
+ * buried in a namespace to discourage its use in function parameters.
+ * If you need to take a parameter, use [const] ns[C]Substring&.
+ * If you need to instantiate a string, use ns[C]String or descendents.
+ *
+ * NAMES:
+ *   nsStringRepr for wide characters
+ *   nsCStringRepr for narrow characters
+ *
+ */
+class nsTStringRepr_CharT
+{
+public:
+  typedef mozilla::fallible_t                 fallible_t;
+
+  typedef CharT                               char_type;
+
+  typedef nsCharTraits<char_type>             char_traits;
+  typedef char_traits::incompatible_char_type incompatible_char_type;
+
+  typedef nsTStringRepr_CharT                 self_type;
+  typedef self_type                           base_string_type;
+
+  typedef nsTSubstring_CharT                  substring_type;
+  typedef nsTSubstringTuple_CharT             substring_tuple_type;
+  typedef nsTString_CharT                     string_type;
+
+  typedef nsReadingIterator<char_type>        const_iterator;
+  typedef nsWritingIterator<char_type>        iterator;
+
+  typedef nsTStringComparator_CharT           comparator_type;
+
+  typedef char_type*                          char_iterator;
+  typedef const char_type*                    const_char_iterator;
+
+  typedef uint32_t                            index_type;
+  typedef uint32_t                            size_type;
+
+  // These are only for internal use within the string classes:
+  typedef StringDataFlags                     DataFlags;
+  typedef StringClassFlags                    ClassFlags;
+
+  /**
+   * reading iterators
+   */
+
+  const_char_iterator BeginReading() const
+  {
+    return mData;
+  }
+  const_char_iterator EndReading() const
+  {
+    return mData + mLength;
+  }
+
+  /**
+   * deprecated reading iterators
+   */
+
+  const_iterator& BeginReading(const_iterator& aIter) const
+  {
+    aIter.mStart = mData;
+    aIter.mEnd = mData + mLength;
+    aIter.mPosition = aIter.mStart;
+    return aIter;
+  }
+
+  const_iterator& EndReading(const_iterator& aIter) const
+  {
+    aIter.mStart = mData;
+    aIter.mEnd = mData + mLength;
+    aIter.mPosition = aIter.mEnd;
+    return aIter;
+  }
+
+  const_char_iterator& BeginReading(const_char_iterator& aIter) const
+  {
+    return aIter = mData;
+  }
+
+  const_char_iterator& EndReading(const_char_iterator& aIter) const
+  {
+    return aIter = mData + mLength;
+  }
+
+  /**
+   * accessors
+   */
+
+  // returns pointer to string data (not necessarily null-terminated)
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+  char16ptr_t Data() const
+#else
+  const char_type* Data() const
+#endif
+  {
+    return mData;
+  }
+
+  size_type Length() const
+  {
+    return mLength;
+  }
+
+  DataFlags GetDataFlags() const
+  {
+    return mDataFlags;
+  }
+
+  bool IsEmpty() const
+  {
+    return mLength == 0;
+  }
+
+  bool IsLiteral() const
+  {
+    return !!(mDataFlags & DataFlags::LITERAL);
+  }
+
+  bool IsVoid() const
+  {
+    return !!(mDataFlags & DataFlags::VOIDED);
+  }
+
+  bool IsTerminated() const
+  {
+    return !!(mDataFlags & DataFlags::TERMINATED);
+  }
+
+  char_type CharAt(index_type aIndex) const
+  {
+    NS_ASSERTION(aIndex < mLength, "index exceeds allowable range");
+    return mData[aIndex];
+  }
+
+  char_type operator[](index_type aIndex) const
+  {
+    return CharAt(aIndex);
+  }
+
+  char_type First() const;
+
+  char_type Last() const;
+
+  size_type NS_FASTCALL CountChar(char_type) const;
+  int32_t NS_FASTCALL FindChar(char_type, index_type aOffset = 0) const;
+
+  inline bool Contains(char_type aChar) const
+  {
+    return FindChar(aChar) != kNotFound;
+  }
+
+  /**
+   * equality
+   */
+
+  bool NS_FASTCALL Equals(const self_type&) const;
+  bool NS_FASTCALL Equals(const self_type&, const comparator_type&) const;
+
+  bool NS_FASTCALL Equals(const substring_tuple_type& aTuple) const;
+  bool NS_FASTCALL Equals(const substring_tuple_type& aTuple,
+                          const comparator_type& aComp) const;
+
+  bool NS_FASTCALL Equals(const char_type* aData) const;
+  bool NS_FASTCALL Equals(const char_type* aData,
+                          const comparator_type& aComp) const;
+
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
+  bool NS_FASTCALL Equals(char16ptr_t aData) const
+  {
+    return Equals(static_cast<const char16_t*>(aData));
+  }
+  bool NS_FASTCALL Equals(char16ptr_t aData, const comparator_type& aComp) const
+  {
+    return Equals(static_cast<const char16_t*>(aData), aComp);
+  }
+#endif
+
+  /**
+   * An efficient comparison with ASCII that can be used even
+   * for wide strings. Call this version when you know the
+   * length of 'data'.
+   */
+  bool NS_FASTCALL EqualsASCII(const char* aData, size_type aLen) const;
+  /**
+   * An efficient comparison with ASCII that can be used even
+   * for wide strings. Call this version when 'data' is
+   * null-terminated.
+   */
+  bool NS_FASTCALL EqualsASCII(const char* aData) const;
+
+  // EqualsLiteral must ONLY be applied to an actual literal string, or
+  // a char array *constant* declared without an explicit size.
+  // Do not attempt to use it with a regular char* pointer, or with a
+  // non-constant char array variable. Use EqualsASCII for them.
+  // The template trick to acquire the array length at compile time without
+  // using a macro is due to Corey Kosak, with much thanks.
+  template<int N>
+  inline bool EqualsLiteral(const char (&aStr)[N]) const
+  {
+    return EqualsASCII(aStr, N - 1);
+  }
+
+  // The LowerCaseEquals methods compare the ASCII-lowercase version of
+  // this string (lowercasing only ASCII uppercase characters) to some
+  // ASCII/Literal string. The ASCII string is *not* lowercased for
+  // you. If you compare to an ASCII or literal string that contains an
+  // uppercase character, it is guaranteed to return false. We will
+  // throw assertions too.
+  bool NS_FASTCALL LowerCaseEqualsASCII(const char* aData,
+                                        size_type aLen) const;
+  bool NS_FASTCALL LowerCaseEqualsASCII(const char* aData) const;
+
+  // LowerCaseEqualsLiteral must ONLY be applied to an actual
+  // literal string, or a char array *constant* declared without an
+  // explicit size.  Do not attempt to use it with a regular char*
+  // pointer, or with a non-constant char array variable. Use
+  // LowerCaseEqualsASCII for them.
+  template<int N>
+  inline bool LowerCaseEqualsLiteral(const char (&aStr)[N]) const
+  {
+    return LowerCaseEqualsASCII(aStr, N - 1);
+  }
+
+  /**
+   * returns true if this string overlaps with the given string fragment.
+   */
+  bool IsDependentOn(const char_type* aStart, const char_type* aEnd) const
+  {
+    /**
+     * if it _isn't_ the case that one fragment starts after the other ends,
+     * or ends before the other starts, then, they conflict:
+     *
+     *   !(f2.begin >= f1.aEnd || f2.aEnd <= f1.begin)
+     *
+     * Simplified, that gives us:
+     */
+    return (aStart < (mData + mLength) && aEnd > mData);
+  }
+
+protected:
+  nsTStringRepr_CharT() = delete; // Never instantiate directly
+
+  constexpr
+  nsTStringRepr_CharT(char_type* aData, size_type aLength,
+                      DataFlags aDataFlags, ClassFlags aClassFlags)
+    : mData(aData)
+    , mLength(aLength)
+    , mDataFlags(aDataFlags)
+    , mClassFlags(aClassFlags)
+  {
+  }
+
+  char_type* mData;
+  size_type mLength;
+  DataFlags mDataFlags;
+  ClassFlags const mClassFlags;
+};
+
+} // namespace detail
+} // namespace mozilla
 
 /**
  * nsTSubstring is an abstract string class. From an API perspective, this
  * class is the root of the string class hierarchy. It represents a single
  * contiguous array of characters, which may or may not be null-terminated.
  * This type is not instantiated directly. A sub-class is instantiated
  * instead. For example, see nsTString.
  *
  * NAMES:
  *   nsAString for wide characters
  *   nsACString for narrow characters
  *
  */
-template <typename T>
-class nsTSubstring : public mozilla::detail::nsTStringRepr<T>
+class nsTSubstring_CharT : public mozilla::detail::nsTStringRepr_CharT
 {
 public:
-  typedef nsTSubstring<T> self_type;
-
-  typedef nsTString<T> string_type;
-
-  typedef typename mozilla::detail::nsTStringRepr<T> base_string_type;
-  typedef typename base_string_type::substring_type substring_type;
-
-  typedef typename base_string_type::fallible_t fallible_t;
-
-  typedef typename base_string_type::char_type char_type;
-  typedef typename base_string_type::char_traits char_traits;
-  typedef typename base_string_type::incompatible_char_type incompatible_char_type;
-
-  typedef typename base_string_type::substring_tuple_type substring_tuple_type;
-
-  typedef typename base_string_type::const_iterator const_iterator;
-  typedef typename base_string_type::iterator iterator;
-
-  typedef typename base_string_type::comparator_type comparator_type;
-
-  typedef typename base_string_type::char_iterator char_iterator;
-  typedef typename base_string_type::const_char_iterator const_char_iterator;
-
-  typedef typename base_string_type::index_type index_type;
-  typedef typename base_string_type::size_type size_type;
-
-  // These are only for internal use within the string classes:
-  typedef typename base_string_type::DataFlags DataFlags;
-  typedef typename base_string_type::ClassFlags ClassFlags;
-
-  using typename base_string_type::IsChar;
-  using typename base_string_type::IsChar16;
+  typedef nsTSubstring_CharT                  self_type;
 
   // this acts like a virtual destructor
-  ~nsTSubstring()
+  ~nsTSubstring_CharT()
   {
     Finalize();
   }
 
   /**
    * writing iterators
    */
 
   char_iterator BeginWriting()
   {
     if (!EnsureMutable()) {
-      AllocFailed(base_string_type::mLength);
+      AllocFailed(mLength);
     }
 
-    return base_string_type::mData;
+    return mData;
   }
 
   char_iterator BeginWriting(const fallible_t&)
   {
-    return EnsureMutable() ? base_string_type::mData : char_iterator(0);
+    return EnsureMutable() ? mData : char_iterator(0);
   }
 
   char_iterator EndWriting()
   {
     if (!EnsureMutable()) {
-      AllocFailed(base_string_type::mLength);
+      AllocFailed(mLength);
     }
 
-    return base_string_type::mData + base_string_type::mLength;
+    return mData + mLength;
   }
 
   char_iterator EndWriting(const fallible_t&)
   {
-    return EnsureMutable() ? (base_string_type::mData + base_string_type::mLength) : char_iterator(0);
+    return EnsureMutable() ? (mData + mLength) : char_iterator(0);
   }
 
   char_iterator& BeginWriting(char_iterator& aIter)
   {
     return aIter = BeginWriting();
   }
 
   char_iterator& BeginWriting(char_iterator& aIter, const fallible_t& aFallible)
@@ -134,26 +402,26 @@ public:
   /**
    * deprecated writing iterators
    */
 
   iterator& BeginWriting(iterator& aIter)
   {
     char_type* data = BeginWriting();
     aIter.mStart = data;
-    aIter.mEnd = data + base_string_type::mLength;
+    aIter.mEnd = data + mLength;
     aIter.mPosition = aIter.mStart;
     return aIter;
   }
 
   iterator& EndWriting(iterator& aIter)
   {
     char_type* data = BeginWriting();
     aIter.mStart = data;
-    aIter.mEnd = data + base_string_type::mLength;
+    aIter.mEnd = data + mLength;
     aIter.mPosition = aIter.mEnd;
     return aIter;
   }
 
   /**
    * assignment
    */
 
@@ -170,30 +438,27 @@ public:
 
   void NS_FASTCALL Assign(const self_type&);
   MOZ_MUST_USE bool NS_FASTCALL Assign(const self_type&, const fallible_t&);
 
   void NS_FASTCALL Assign(const substring_tuple_type&);
   MOZ_MUST_USE bool NS_FASTCALL Assign(const substring_tuple_type&,
                                        const fallible_t&);
 
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
   void Assign(char16ptr_t aData)
   {
     Assign(static_cast<const char16_t*>(aData));
   }
 
-  template <typename EnableIfChar16 = IsChar16>
   void Assign(char16ptr_t aData, size_type aLength)
   {
     Assign(static_cast<const char16_t*>(aData), aLength);
   }
 
-  template <typename EnableIfChar16 = IsChar16>
   MOZ_MUST_USE bool Assign(char16ptr_t aData, size_type aLength,
                            const fallible_t& aFallible)
   {
     return Assign(static_cast<const char16_t*>(aData), aLength,
                   aFallible);
   }
 #endif
 
@@ -220,35 +485,35 @@ public:
   // non-constant char array variable. Use AssignASCII for those.
   // There are not fallible version of these methods because they only really
   // apply to small allocations that we wouldn't want to check anyway.
   template<int N>
   void AssignLiteral(const char_type (&aStr)[N])
   {
     AssignLiteral(aStr, N - 1);
   }
-
-  template<int N, typename EnableIfChar16 = IsChar16>
-  void AssignLiteral(const incompatible_char_type (&aStr)[N])
+#ifdef CharT_is_PRUnichar
+  template<int N>
+  void AssignLiteral(const char (&aStr)[N])
   {
     AssignASCII(aStr, N - 1);
   }
+#endif
 
   self_type& operator=(char_type aChar)
   {
     Assign(aChar);
     return *this;
   }
   self_type& operator=(const char_type* aData)
   {
     Assign(aData);
     return *this;
   }
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
   self_type& operator=(char16ptr_t aData)
   {
     Assign(aData);
     return *this;
   }
 #endif
   self_type& operator=(const self_type& aStr)
   {
@@ -317,66 +582,65 @@ public:
   void ReplaceLiteral(index_type aCutStart, size_type aCutLength,
                       const char_type (&aStr)[N])
   {
     ReplaceLiteral(aCutStart, aCutLength, aStr, N - 1);
   }
 
   void Append(char_type aChar)
   {
-    Replace(base_string_type::mLength, 0, aChar);
+    Replace(mLength, 0, aChar);
   }
   MOZ_MUST_USE bool Append(char_type aChar, const fallible_t& aFallible)
   {
-    return Replace(base_string_type::mLength, 0, aChar, aFallible);
+    return Replace(mLength, 0, aChar, aFallible);
   }
   void Append(const char_type* aData, size_type aLength = size_type(-1))
   {
-    Replace(base_string_type::mLength, 0, aData, aLength);
+    Replace(mLength, 0, aData, aLength);
   }
   MOZ_MUST_USE bool Append(const char_type* aData, size_type aLength,
                            const fallible_t& aFallible)
   {
-    return Replace(base_string_type::mLength, 0, aData, aLength, aFallible);
+    return Replace(mLength, 0, aData, aLength, aFallible);
   }
 
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
   void Append(char16ptr_t aData, size_type aLength = size_type(-1))
   {
     Append(static_cast<const char16_t*>(aData), aLength);
   }
 #endif
 
   void Append(const self_type& aStr)
   {
-    Replace(base_string_type::mLength, 0, aStr);
+    Replace(mLength, 0, aStr);
   }
   MOZ_MUST_USE bool Append(const self_type& aStr, const fallible_t& aFallible)
   {
-    return Replace(base_string_type::mLength, 0, aStr, aFallible);
+    return Replace(mLength, 0, aStr, aFallible);
   }
   void Append(const substring_tuple_type& aTuple)
   {
-    Replace(base_string_type::mLength, 0, aTuple);
+    Replace(mLength, 0, aTuple);
   }
 
   void AppendASCII(const char* aData, size_type aLength = size_type(-1))
   {
-    ReplaceASCII(base_string_type::mLength, 0, aData, aLength);
+    ReplaceASCII(mLength, 0, aData, aLength);
   }
 
   MOZ_MUST_USE bool AppendASCII(const char* aData, const fallible_t& aFallible)
   {
-    return ReplaceASCII(base_string_type::mLength, 0, aData, size_type(-1), aFallible);
+    return ReplaceASCII(mLength, 0, aData, size_type(-1), aFallible);
   }
 
   MOZ_MUST_USE bool AppendASCII(const char* aData, size_type aLength, const fallible_t& aFallible)
   {
-    return ReplaceASCII(base_string_type::mLength, 0, aData, aLength, aFallible);
+    return ReplaceASCII(mLength, 0, aData, aLength, aFallible);
   }
 
   /**
    * Append a formatted string to the current string. Uses the
    * standard printf format codes.  This uses NSPR formatting, which will be
    * locale-aware for floating-point values.  You probably don't want to use
    * this with floating-point values as a result.
    */
@@ -435,46 +699,43 @@ public:
 public:
 
   // AppendLiteral must ONLY be applied to an actual literal string.
   // Do not attempt to use it with a regular char* pointer, or with a char
   // array variable. Use Append or AppendASCII for those.
   template<int N>
   void AppendLiteral(const char_type (&aStr)[N])
   {
-    ReplaceLiteral(base_string_type::mLength, 0, aStr, N - 1);
+    ReplaceLiteral(mLength, 0, aStr, N - 1);
   }
-
-  // Only enable for T = char16_t
-  template<int N, typename EnableIfChar16 = IsChar16>
-  void AppendLiteral(const incompatible_char_type (&aStr)[N])
+#ifdef CharT_is_PRUnichar
+  template<int N>
+  void AppendLiteral(const char (&aStr)[N])
   {
     AppendASCII(aStr, N - 1);
   }
 
-  // Only enable for T = char16_t
-  template<int N, typename EnableIfChar16 = IsChar16>
-  MOZ_MUST_USE bool
-  AppendLiteral(const incompatible_char_type (&aStr)[N], const fallible_t& aFallible)
+  template<int N>
+  MOZ_MUST_USE bool AppendLiteral(const char (&aStr)[N], const fallible_t& aFallible)
   {
     return AppendASCII(aStr, N - 1, aFallible);
   }
+#endif
 
   self_type& operator+=(char_type aChar)
   {
     Append(aChar);
     return *this;
   }
   self_type& operator+=(const char_type* aData)
   {
     Append(aData);
     return *this;
   }
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
   self_type& operator+=(char16ptr_t aData)
   {
     Append(aData);
     return *this;
   }
 #endif
   self_type& operator+=(const self_type& aStr)
   {
@@ -491,18 +752,17 @@ public:
   {
     Replace(aPos, 0, aChar);
   }
   void Insert(const char_type* aData, index_type aPos,
               size_type aLength = size_type(-1))
   {
     Replace(aPos, 0, aData, aLength);
   }
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
   void Insert(char16ptr_t aData, index_type aPos,
               size_type aLength = size_type(-1))
   {
     Insert(static_cast<const char16_t*>(aData), aPos, aLength);
   }
 #endif
   void Insert(const self_type& aStr, index_type aPos)
   {
@@ -522,17 +782,17 @@ public:
     ReplaceLiteral(aPos, 0, aStr, N - 1);
   }
 
   void Cut(index_type aCutStart, size_type aCutLength)
   {
     Replace(aCutStart, aCutLength, char_traits::sEmptyBuffer, 0);
   }
 
-  nsTSubstringSplitter<T> Split(const char_type aChar) const;
+  nsTSubstringSplitter_CharT Split(const char_type aChar) const;
 
   /**
    * buffer sizing
    */
 
   /**
    * Attempts to set the capacity to the given size in number of
    * characters, without affecting the length of the string.
@@ -545,17 +805,17 @@ public:
                                             const fallible_t&);
 
   void NS_FASTCALL SetLength(size_type aNewLength);
   MOZ_MUST_USE bool NS_FASTCALL SetLength(size_type aNewLength,
                                           const fallible_t&);
 
   void Truncate(size_type aNewLength = 0)
   {
-    NS_ASSERTION(aNewLength <= base_string_type::mLength, "Truncate cannot make string longer");
+    NS_ASSERTION(aNewLength <= mLength, "Truncate cannot make string longer");
     SetLength(aNewLength);
   }
 
 
   /**
    * buffer access
    */
 
@@ -563,79 +823,77 @@ public:
   /**
    * Get a const pointer to the string's internal buffer.  The caller
    * MUST NOT modify the characters at the returned address.
    *
    * @returns The length of the buffer in characters.
    */
   inline size_type GetData(const char_type** aData) const
   {
-    *aData = base_string_type::mData;
-    return base_string_type::mLength;
+    *aData = mData;
+    return mLength;
   }
 
   /**
    * Get a pointer to the string's internal buffer, optionally resizing
    * the buffer first.  If size_type(-1) is passed for newLen, then the
    * current length of the string is used.  The caller MAY modify the
    * characters at the returned address (up to but not exceeding the
    * length of the string).
    *
    * @returns The length of the buffer in characters or 0 if unable to
    * satisfy the request due to low-memory conditions.
    */
   size_type GetMutableData(char_type** aData, size_type aNewLen = size_type(-1))
   {
     if (!EnsureMutable(aNewLen)) {
-      AllocFailed(aNewLen == size_type(-1) ? base_string_type::mLength : aNewLen);
+      AllocFailed(aNewLen == size_type(-1) ? mLength : aNewLen);
     }
 
-    *aData = base_string_type::mData;
-    return base_string_type::mLength;
+    *aData = mData;
+    return mLength;
   }
 
   size_type GetMutableData(char_type** aData, size_type aNewLen, const fallible_t&)
   {
     if (!EnsureMutable(aNewLen)) {
       *aData = nullptr;
       return 0;
     }
 
-    *aData = base_string_type::mData;
-    return base_string_type::mLength;
+    *aData = mData;
+    return mLength;
   }
 
-#if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+#if defined(CharT_is_PRUnichar) && defined(MOZ_USE_CHAR16_WRAPPER)
   size_type GetMutableData(wchar_t** aData, size_type aNewLen = size_type(-1))
   {
     return GetMutableData(reinterpret_cast<char16_t**>(aData), aNewLen);
   }
 
-  template <typename EnableIfChar16 = IsChar16>
   size_type GetMutableData(wchar_t** aData, size_type aNewLen,
                            const fallible_t& aFallible)
   {
     return GetMutableData(reinterpret_cast<char16_t**>(aData), aNewLen,
                           aFallible);
   }
 #endif
 
   /**
    * Span integration
    */
 
   operator mozilla::Span<char_type>()
   {
-    return mozilla::MakeSpan(BeginWriting(), base_string_type::Length());
+    return mozilla::MakeSpan(BeginWriting(), Length());
   }
 
   operator mozilla::Span<const char_type>() const
   {
-    return mozilla::MakeSpan(base_string_type::BeginReading(), base_string_type::Length());
+    return mozilla::MakeSpan(BeginReading(), Length());
   }
 
   void Append(mozilla::Span<const char_type> aSpan)
   {
     auto len = aSpan.Length();
     MOZ_RELEASE_ASSERT(len <= mozilla::MaxValue<size_type>::value);
     Append(aSpan.Elements(), len);
   }
@@ -645,49 +903,47 @@ public:
   {
     auto len = aSpan.Length();
     if (len > mozilla::MaxValue<size_type>::value) {
       return false;
     }
     return Append(aSpan.Elements(), len, aFallible);
   }
 
-  template <typename EnableIfChar = IsChar>
+#if !defined(CharT_is_PRUnichar)
   operator mozilla::Span<uint8_t>()
   {
     return mozilla::MakeSpan(reinterpret_cast<uint8_t*>(BeginWriting()),
-                             base_string_type::Length());
+                             Length());
   }
 
-  template <typename EnableIfChar = IsChar>
   operator mozilla::Span<const uint8_t>() const
   {
-    return mozilla::MakeSpan(reinterpret_cast<const uint8_t*>(base_string_type::BeginReading()),
-                             base_string_type::Length());
+    return mozilla::MakeSpan(reinterpret_cast<const uint8_t*>(BeginReading()),
+                             Length());
   }
 
-  template <typename EnableIfChar = IsChar>
   void Append(mozilla::Span<const uint8_t> aSpan)
   {
     auto len = aSpan.Length();
     MOZ_RELEASE_ASSERT(len <= mozilla::MaxValue<size_type>::value);
     Append(reinterpret_cast<const char*>(aSpan.Elements()), len);
   }
 
-  template <typename EnableIfChar = IsChar>
   MOZ_MUST_USE bool Append(mozilla::Span<const uint8_t> aSpan,
                            const fallible_t& aFallible)
   {
     auto len = aSpan.Length();
     if (len > mozilla::MaxValue<size_type>::value) {
       return false;
     }
     return Append(
       reinterpret_cast<const char*>(aSpan.Elements()), len, aFallible);
   }
+#endif
 
   /**
    * string data is never null, but can be marked void.  if true, the
    * string will be truncated.  @see nsTSubstring::IsVoid
    */
 
   void NS_FASTCALL SetIsVoid(bool);
 
@@ -734,38 +990,38 @@ public:
   void StripCRLF();
 
   /**
    * If the string uses a shared buffer, this method
    * clears the pointer without releasing the buffer.
    */
   void ForgetSharedBuffer()
   {
-    if (base_string_type::mDataFlags & DataFlags::SHARED) {
+    if (mDataFlags & DataFlags::SHARED) {
       SetToEmptyBuffer();
     }
   }
 
 protected:
   void AssertValid()
   {
-    MOZ_ASSERT(!(this->mClassFlags & ClassFlags::NULL_TERMINATED) ||
-               (this->mDataFlags & DataFlags::TERMINATED),
+    MOZ_ASSERT(!(mClassFlags & ClassFlags::NULL_TERMINATED) ||
+               (mDataFlags & DataFlags::TERMINATED),
                "String classes whose static type guarantees a null-terminated "
                "buffer must not be assigned a non-null-terminated buffer.");
   }
 
 public:
 
   /**
    * this is public to support automatic conversion of tuple to string
    * base type, which helps avoid converting to nsTAString.
    */
-  MOZ_IMPLICIT nsTSubstring(const substring_tuple_type& aTuple)
-    : base_string_type(nullptr, 0, DataFlags(0), ClassFlags(0))
+  MOZ_IMPLICIT nsTSubstring_CharT(const substring_tuple_type& aTuple)
+    : nsTStringRepr_CharT(nullptr, 0, DataFlags(0), ClassFlags(0))
   {
     AssertValid();
     Assign(aTuple);
   }
 
   size_t SizeOfExcludingThisIfUnshared(mozilla::MallocSizeOf aMallocSizeOf)
   const;
   size_t SizeOfIncludingThisIfUnshared(mozilla::MallocSizeOf aMallocSizeOf)
@@ -777,89 +1033,89 @@ public:
    * you do use them, please explain clearly in a comment why it's safe
    * and won't lead to double-counting.
    */
   size_t SizeOfExcludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf)
   const;
   size_t SizeOfIncludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf)
   const;
 
-  template<class N>
+  template<class T>
   void NS_ABORT_OOM(T)
   {
     struct never {}; // a compiler-friendly way to do static_assert(false)
-    static_assert(mozilla::IsSame<N, never>::value,
+    static_assert(mozilla::IsSame<T, never>::value,
       "In string classes, use AllocFailed to account for sizeof(char_type). "
       "Use the global ::NS_ABORT_OOM if you really have a count of bytes.");
   }
 
   MOZ_ALWAYS_INLINE void AllocFailed(size_t aLength)
   {
     ::NS_ABORT_OOM(aLength * sizeof(char_type));
   }
 
 protected:
 
   // default initialization
-  nsTSubstring()
-    : base_string_type(char_traits::sEmptyBuffer, 0, DataFlags::TERMINATED,
-                       ClassFlags(0))
+  nsTSubstring_CharT()
+    : nsTStringRepr_CharT(char_traits::sEmptyBuffer, 0, DataFlags::TERMINATED,
+                          ClassFlags(0))
   {
     AssertValid();
   }
 
   // copy-constructor, constructs as dependent on given object
   // (NOTE: this is for internal use only)
-  nsTSubstring(const self_type& aStr)
-    : base_string_type(aStr.base_string_type::mData, aStr.base_string_type::mLength,
-                       aStr.base_string_type::mDataFlags & (DataFlags::TERMINATED | DataFlags::VOIDED),
-                       ClassFlags(0))
+  nsTSubstring_CharT(const self_type& aStr)
+    : nsTStringRepr_CharT(aStr.mData, aStr.mLength,
+                          aStr.mDataFlags & (DataFlags::TERMINATED | DataFlags::VOIDED),
+                          ClassFlags(0))
   {
     AssertValid();
   }
 
   // initialization with ClassFlags
-  explicit nsTSubstring(ClassFlags aClassFlags)
-    : base_string_type(char_traits::sEmptyBuffer, 0, DataFlags::TERMINATED,
-                       aClassFlags)
+  explicit nsTSubstring_CharT(ClassFlags aClassFlags)
+    : nsTStringRepr_CharT(char_traits::sEmptyBuffer, 0, DataFlags::TERMINATED,
+                          aClassFlags)
   {
     AssertValid();
   }
 
  /**
    * allows for direct initialization of a nsTSubstring object.
    */
-  nsTSubstring(char_type* aData, size_type aLength,
-               DataFlags aDataFlags, ClassFlags aClassFlags)
+  nsTSubstring_CharT(char_type* aData, size_type aLength,
+                     DataFlags aDataFlags, ClassFlags aClassFlags)
 // XXXbz or can I just include nscore.h and use NS_BUILD_REFCNT_LOGGING?
 #if defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)
 #define XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
     ;
 #else
 #undef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
-    : base_string_type(aData, aLength, aDataFlags, aClassFlags)
+    : nsTStringRepr_CharT(aData, aLength, aDataFlags, aClassFlags)
   {
     AssertValid();
     MOZ_RELEASE_ASSERT(CheckCapacity(aLength), "String is too large.");
   }
 #endif /* DEBUG || FORCE_BUILD_REFCNT_LOGGING */
 
   void SetToEmptyBuffer()
   {
-    base_string_type::mData = char_traits::sEmptyBuffer;
-    base_string_type::mLength = 0;
-    base_string_type::mDataFlags = DataFlags::TERMINATED;
+    mData = char_traits::sEmptyBuffer;
+    mLength = 0;
+    mDataFlags = DataFlags::TERMINATED;
     AssertValid();
   }
 
   void SetData(char_type* aData, size_type aLength, DataFlags aDataFlags)
   {
-    base_string_type::mData = aData;
-    base_string_type::mLength = aLength;
-    base_string_type::mDataFlags = aDataFlags;
+    mData = aData;
+    mLength = aLength;
+    mDataFlags = aDataFlags;
     AssertValid();
   }
 
   /**
    * this function releases mData and does not change the value of
    * any of its member variables.  in other words, this function acts
    * like a destructor.
    */
@@ -952,115 +1208,155 @@ protected:
   static const size_type kMaxCapacity;
 public:
 
   // NOTE: this method is declared public _only_ for convenience for
   // callers who don't have access to the original nsLiteralString_CharT.
   void NS_FASTCALL AssignLiteral(const char_type* aData, size_type aLength);
 };
 
-extern template class nsTSubstring<char>;
-extern template class nsTSubstring<char16_t>;
+static_assert(sizeof(nsTSubstring_CharT) ==
+              sizeof(mozilla::detail::nsTStringRepr_CharT),
+              "Don't add new data fields to nsTSubstring_CharT. "
+              "Add to nsTStringRepr_CharT instead.");
+
+int NS_FASTCALL
+Compare(const nsTSubstring_CharT::base_string_type& aLhs,
+        const nsTSubstring_CharT::base_string_type& aRhs,
+        const nsTStringComparator_CharT& = nsTDefaultStringComparator_CharT());
+
+
+inline bool
+operator!=(const nsTSubstring_CharT::base_string_type& aLhs,
+           const nsTSubstring_CharT::base_string_type& aRhs)
+{
+  return !aLhs.Equals(aRhs);
+}
+
+inline bool
+operator!=(const nsTSubstring_CharT::base_string_type& aLhs,
+           const nsTSubstring_CharT::char_type* aRhs)
+{
+  return !aLhs.Equals(aRhs);
+}
+
+inline bool
+operator<(const nsTSubstring_CharT::base_string_type& aLhs,
+          const nsTSubstring_CharT::base_string_type& aRhs)
+{
+  return Compare(aLhs, aRhs) < 0;
+}
 
-static_assert(sizeof(nsTSubstring<char>) ==
-              sizeof(mozilla::detail::nsTStringRepr<char>),
-              "Don't add new data fields to nsTSubstring_CharT. "
-              "Add to nsTStringRepr<T> instead.");
+inline bool
+operator<=(const nsTSubstring_CharT::base_string_type& aLhs,
+           const nsTSubstring_CharT::base_string_type& aRhs)
+{
+  return Compare(aLhs, aRhs) <= 0;
+}
+
+inline bool
+operator==(const nsTSubstring_CharT::base_string_type& aLhs,
+           const nsTSubstring_CharT::base_string_type& aRhs)
+{
+  return aLhs.Equals(aRhs);
+}
 
+inline bool
+operator==(const nsTSubstring_CharT::base_string_type& aLhs,
+           const nsTSubstring_CharT::char_type* aRhs)
+{
+  return aLhs.Equals(aRhs);
+}
+
+
+inline bool
+operator>=(const nsTSubstring_CharT::base_string_type& aLhs,
+           const nsTSubstring_CharT::base_string_type& aRhs)
+{
+  return Compare(aLhs, aRhs) >= 0;
+}
+
+inline bool
+operator>(const nsTSubstring_CharT::base_string_type& aLhs,
+          const nsTSubstring_CharT::base_string_type& aRhs)
+{
+  return Compare(aLhs, aRhs) > 0;
+}
 
 // You should not need to instantiate this class directly.
 // Use nsTSubstring::Split instead.
-template <typename T>
-class nsTSubstringSplitter
+class nsTSubstringSplitter_CharT
 {
-  typedef typename nsTSubstring<T>::size_type size_type;
-  typedef typename nsTSubstring<T>::char_type char_type;
+  typedef nsTSubstring_CharT::size_type size_type;
+  typedef nsTSubstring_CharT::char_type char_type;
 
   class nsTSubstringSplit_Iter
   {
   public:
-    nsTSubstringSplit_Iter(const nsTSubstringSplitter<T>& aObj,
+    nsTSubstringSplit_Iter(const nsTSubstringSplitter_CharT& aObj,
                            size_type aPos)
       : mObj(aObj)
       , mPos(aPos)
     {
     }
 
     bool operator!=(const nsTSubstringSplit_Iter& other) const
     {
       return mPos != other.mPos;
     }
 
-    const nsTDependentSubstring<T>& operator*() const;
+    const nsTDependentSubstring_CharT& operator*() const;
 
     const nsTSubstringSplit_Iter& operator++()
     {
       ++mPos;
       return *this;
     }
 
   private:
-    const nsTSubstringSplitter<T>& mObj;
+    const nsTSubstringSplitter_CharT& mObj;
     size_type mPos;
   };
 
 private:
-  const nsTSubstring<T>* const mStr;
-  mozilla::UniquePtr<nsTDependentSubstring<T>[]> mArray;
+  const nsTSubstring_CharT* const mStr;
+  mozilla::UniquePtr<nsTDependentSubstring_CharT[]> mArray;
   size_type mArraySize;
   const char_type mDelim;
 
 public:
-  nsTSubstringSplitter(const nsTSubstring<T>* aStr, char_type aDelim);
+  nsTSubstringSplitter_CharT(const nsTSubstring_CharT* aStr, char_type aDelim);
 
   nsTSubstringSplit_Iter begin() const
   {
     return nsTSubstringSplit_Iter(*this, 0);
   }
 
   nsTSubstringSplit_Iter end() const
   {
     return nsTSubstringSplit_Iter(*this, mArraySize);
   }
 
-  const nsTDependentSubstring<T>& Get(const size_type index) const
+  const nsTDependentSubstring_CharT& Get(const size_type index) const
   {
     MOZ_ASSERT(index < mArraySize);
     return mArray[index];
   }
 };
 
-extern template class nsTSubstringSplitter<char>;
-extern template class nsTSubstringSplitter<char16_t>;
-
 /**
  * Span integration
  */
 namespace mozilla {
 
-inline Span<char>
-MakeSpan(nsTSubstring<char>& aString)
-{
-  return aString;
-}
-
-inline Span<const char>
-MakeSpan(const nsTSubstring<char>& aString)
+inline Span<CharT>
+MakeSpan(nsTSubstring_CharT& aString)
 {
   return aString;
 }
 
-inline Span<char16_t>
-MakeSpan(nsTSubstring<char16_t>& aString)
+inline Span<const CharT>
+MakeSpan(const nsTSubstring_CharT& aString)
 {
   return aString;
 }
 
-inline Span<const char16_t>
-MakeSpan(const nsTSubstring<char16_t>& aString)
-{
-  return aString;
-}
-
-
 } // namespace mozilla
-
-#endif
--- a/xpcom/string/nsTSubstringTuple.cpp
+++ b/xpcom/string/nsTSubstringTuple.cpp
@@ -5,19 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/CheckedInt.h"
 
 /**
  * computes the aggregate string length
  */
 
-template <typename T>
-typename nsTSubstringTuple<T>::size_type
-nsTSubstringTuple<T>::Length() const
+nsTSubstringTuple_CharT::size_type
+nsTSubstringTuple_CharT::Length() const
 {
   mozilla::CheckedInt<size_type> len;
   if (mHead) {
     len = mHead->Length();
   } else {
     len = mFragA->Length();
   }
 
@@ -28,19 +27,18 @@ nsTSubstringTuple<T>::Length() const
 
 
 /**
  * writes the aggregate string to the given buffer. aBufLen is assumed
  * to be equal to or greater than the value returned by the Length()
  * method.  the string written to |aBuf| is not null-terminated.
  */
 
-template <typename T>
 void
-nsTSubstringTuple<T>::WriteTo(char_type* aBuf, uint32_t aBufLen) const
+nsTSubstringTuple_CharT::WriteTo(char_type* aBuf, uint32_t aBufLen) const
 {
   MOZ_RELEASE_ASSERT(aBufLen >= mFragB->Length(), "buffer too small");
   uint32_t headLen = aBufLen - mFragB->Length();
   if (mHead) {
     mHead->WriteTo(aBuf, headLen);
   } else {
     MOZ_RELEASE_ASSERT(mFragA->Length() == headLen, "buffer incorrectly sized");
     char_traits::copy(aBuf, mFragA->Data(), mFragA->Length());
@@ -50,20 +48,19 @@ nsTSubstringTuple<T>::WriteTo(char_type*
 }
 
 
 /**
  * returns true if this tuple is dependent on (i.e., overlapping with)
  * the given char sequence.
  */
 
-template <typename T>
 bool
-nsTSubstringTuple<T>::IsDependentOn(const char_type* aStart,
-                                    const char_type* aEnd) const
+nsTSubstringTuple_CharT::IsDependentOn(const char_type* aStart,
+                                       const char_type* aEnd) const
 {
   // we aStart with the right-most fragment since it is faster to check.
 
   if (mFragB->IsDependentOn(aStart, aEnd)) {
     return true;
   }
 
   if (mHead) {
--- a/xpcom/string/nsTSubstringTuple.h
+++ b/xpcom/string/nsTSubstringTuple.h
@@ -1,53 +1,47 @@
 /* -*- 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/. */
 // IWYU pragma: private, include "nsString.h"
 
-#ifndef nsTSubstringTuple_h
-#define nsTSubstringTuple_h
-
-#include "nsTStringRepr.h"
-
 /**
- * nsTSubstringTuple
+ * nsTSubstringTuple_CharT
  *
  * Represents a tuple of string fragments.  Built as a recursive binary tree.
  * It is used to implement the concatenation of two or more string objects.
  *
  * NOTE: This class is a private implementation detail and should never be
  * referenced outside the string code.
  */
-template <typename T>
-class nsTSubstringTuple
+class nsTSubstringTuple_CharT
 {
 public:
 
-  typedef T char_type;
-  typedef nsCharTraits<char_type> char_traits;
+  typedef CharT                                 char_type;
+  typedef nsCharTraits<char_type>               char_traits;
 
-  typedef nsTSubstringTuple<T> self_type;
-  typedef mozilla::detail::nsTStringRepr<char_type> base_string_type;
-  typedef uint32_t size_type;
+  typedef nsTSubstringTuple_CharT               self_type;
+  typedef mozilla::detail::nsTStringRepr_CharT  base_string_type;
+  typedef uint32_t                              size_type;
 
 public:
 
-  nsTSubstringTuple(const base_string_type* aStrA,
-                    const base_string_type* aStrB)
+  nsTSubstringTuple_CharT(const base_string_type* aStrA,
+                          const base_string_type* aStrB)
     : mHead(nullptr)
     , mFragA(aStrA)
     , mFragB(aStrB)
   {
   }
 
-  nsTSubstringTuple(const self_type& aHead,
-                    const base_string_type* aStrB)
+  nsTSubstringTuple_CharT(const self_type& aHead,
+                          const base_string_type* aStrB)
     : mHead(&aHead)
     , mFragA(nullptr) // this fragment is ignored when aHead != nullptr
     , mFragB(aStrB)
   {
   }
 
   /**
    * computes the aggregate string length
@@ -69,25 +63,21 @@ public:
 
 private:
 
   const self_type*        const mHead;
   const base_string_type* const mFragA;
   const base_string_type* const mFragB;
 };
 
-template <typename T>
-inline const nsTSubstringTuple<T>
-operator+(const mozilla::detail::nsTStringRepr<T>& aStrA,
-          const mozilla::detail::nsTStringRepr<T>& aStrB)
+inline const nsTSubstringTuple_CharT
+operator+(const nsTSubstringTuple_CharT::base_string_type& aStrA,
+          const nsTSubstringTuple_CharT::base_string_type& aStrB)
 {
-  return nsTSubstringTuple<T>(&aStrA, &aStrB);
+  return nsTSubstringTuple_CharT(&aStrA, &aStrB);
 }
 
-template <typename T>
-inline const nsTSubstringTuple<T>
-operator+(const nsTSubstringTuple<T>& aHead,
-          const mozilla::detail::nsTStringRepr<T>& aStrB)
+inline const nsTSubstringTuple_CharT
+operator+(const nsTSubstringTuple_CharT& aHead,
+          const nsTSubstringTuple_CharT::base_string_type& aStrB)
 {
-  return nsTSubstringTuple<T>(aHead, &aStrB);
+  return nsTSubstringTuple_CharT(aHead, &aStrB);
 }
-
-#endif
deleted file mode 100644
--- a/xpcom/string/precompiled_templates.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsString.h"
-
-// This file provides concrete instantiations for externed string template
-// classes and functions.
-
-// ================
-// Template classes
-// ================
-template class mozilla::detail::nsTStringRepr<char>;
-template class mozilla::detail::nsTStringRepr<char16_t>;
-
-template class nsTLiteralString<char>;
-template class nsTLiteralString<char16_t>;
-
-template class nsTSubstring<char>;
-template class nsTSubstring<char16_t>;
-
-template class nsTDependentSubstring<char>;
-template class nsTDependentSubstring<char16_t>;
-
-// Note: nsTString is skipped as it's implicitly instantiated by derived
-// classes.
-
-template class nsTFixedString<char>;
-template class nsTFixedString<char16_t>;
-template class nsTAutoStringN<char, 64>;
-template class nsTAutoStringN<char16_t, 64>;
-
-template class nsTDependentString<char>;
-template class nsTDependentString<char16_t>;
-
-template class nsTPromiseFlatString<char>;
-template class nsTPromiseFlatString<char16_t>;
-
-template class nsTSubstringSplitter<char>;
-template class nsTSubstringSplitter<char16_t>;
-
-template class nsTDefaultStringComparator<char>;
-template class nsTDefaultStringComparator<char16_t>;
-
-// =============================
-// Templated top-level functions
-// =============================
-
-template
-int
-Compare<char>(mozilla::detail::nsTStringRepr<char> const&,
-              mozilla::detail::nsTStringRepr<char> const&,
-              nsTStringComparator<char> const&);
-
-template
-int
-Compare<char16_t>(mozilla::detail::nsTStringRepr<char16_t> const&,
-                  mozilla::detail::nsTStringRepr<char16_t> const&,
-                  nsTStringComparator<char16_t> const&);
-
-template
-nsTDependentSubstring<char> const
-Substring<char>(char const*, char const*);
-
-template
-nsTDependentSubstring<char16_t> const
-Substring<char16_t>(char16_t const*, char16_t const*);
-
-// =========================================================
-// Templated member functions that are conditionally enabled
-// =========================================================
-
-template
-int32_t
-nsTString<char16_t>::Find(const self_type&, int32_t, int32_t) const;
-
-template
-int32_t
-nsTString<char16_t>::Find(const char_type*, int32_t, int32_t) const;
-
-template
-int32_t
-nsTString<char16_t>::RFind(const self_type&, int32_t, int32_t) const;
-
-template
-int32_t
-nsTString<char16_t>::RFind(const char_type*, int32_t, int32_t) const;
-
-template
-int32_t
-nsTString<char16_t>::FindCharInSet(const char*, int32_t) const;
-
-template
-int32_t
-nsTString<char>::Compare(const char_type*, bool, int32_t) const;
-
-template
-bool
-nsTString<char16_t>::EqualsIgnoreCase(const incompatible_char_type*,
-                                      int32_t) const;
-
-template
-bool
-nsTString<char16_t>::StripChars(const incompatible_char_type*,
-                                const fallible_t&);
-
-template
-void
-nsTString<char16_t>::StripChars(const incompatible_char_type*);
-
-template
-void
-nsTString<char16_t>::ReplaceChar(const char*, char16_t);
new file mode 100644
--- /dev/null
+++ b/xpcom/string/string-template-def-char.h
@@ -0,0 +1,28 @@
+/* -*- 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/. */
+// IWYU pragma: private, include "nsString.h"
+
+#define CharT                               char
+#define CharT_is_char                       1
+#define nsTAString_IncompatibleCharT        nsAString
+#define nsTString_CharT                     nsCString
+#define nsTStringRepr_CharT                 nsCStringRepr
+#define nsTFixedString_CharT                nsFixedCString
+#define nsTAutoStringN_CharT                nsAutoCStringN
+#define nsTAutoString_CharT                 nsAutoCString
+#define nsTSubstring_CharT                  nsACString
+#define PrintfAppend_CharT                  PrintfAppend_nsACString
+#define nsTSubstringTuple_CharT             nsCSubstringTuple
+#define nsTStringComparator_CharT           nsCStringComparator
+#define nsTDefaultStringComparator_CharT    nsDefaultCStringComparator
+#define nsTDependentString_CharT            nsDependentCString
+#define nsTDependentSubstring_CharT         nsDependentCSubstring
+#define nsTLiteralString_CharT              nsLiteralCString
+#define nsTGetterCopies_CharT               nsCGetterCopies
+#define nsTPromiseFlatString_CharT          nsPromiseFlatCString
+#define TPromiseFlatString_CharT            PromiseFlatCString
+#define nsTSubstringSplitter_CharT          nsCSubstringSplitter
+#define TNullString_CharT                   NullCString
new file mode 100644
--- /dev/null
+++ b/xpcom/string/string-template-def-unichar.h
@@ -0,0 +1,28 @@
+/* -*- 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/. */
+// IWYU pragma: private, include "nsString.h"
+
+#define CharT                               char16_t
+#define CharT_is_PRUnichar                  1
+#define nsTAString_IncompatibleCharT        nsACString
+#define nsTString_CharT                     nsString
+#define nsTStringRepr_CharT                 nsStringRepr
+#define nsTFixedString_CharT                nsFixedString
+#define nsTAutoStringN_CharT                nsAutoStringN
+#define nsTAutoString_CharT                 nsAutoString
+#define nsTSubstring_CharT                  nsAString
+#define PrintfAppend_CharT                  PrintfAppend_nsAString
+#define nsTSubstringTuple_CharT             nsSubstringTuple
+#define nsTStringComparator_CharT           nsStringComparator
+#define nsTDefaultStringComparator_CharT    nsDefaultStringComparator
+#define nsTDependentString_CharT            nsDependentString
+#define nsTDependentSubstring_CharT         nsDependentSubstring
+#define nsTLiteralString_CharT              nsLiteralString
+#define nsTGetterCopies_CharT               nsGetterCopies
+#define nsTPromiseFlatString_CharT          nsPromiseFlatString
+#define TPromiseFlatString_CharT            PromiseFlatString
+#define nsTSubstringSplitter_CharT          nsSubstringSplitter
+#define TNullString_CharT                   NullString
new file mode 100644
--- /dev/null
+++ b/xpcom/string/string-template-undef.h
@@ -0,0 +1,29 @@
+/* -*- 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/. */
+// IWYU pragma: private, include "nsString.h"
+
+#undef CharT
+#undef CharT_is_PRUnichar
+#undef CharT_is_char
+#undef nsTAString_IncompatibleCharT
+#undef nsTString_CharT
+#undef nsTStringRepr_CharT
+#undef nsTFixedString_CharT
+#undef nsTAutoStringN_CharT
+#undef nsTAutoString_CharT
+#undef nsTSubstring_CharT
+#undef PrintfAppend_CharT
+#undef nsTSubstringTuple_CharT
+#undef nsTStringComparator_CharT
+#undef nsTDefaultStringComparator_CharT
+#undef nsTDependentString_CharT
+#undef nsTDependentSubstring_CharT
+#undef nsTLiteralString_CharT
+#undef nsTGetterCopies_CharT
+#undef nsTPromiseFlatString_CharT
+#undef TPromiseFlatString_CharT
+#undef nsTSubstringSplitter_CharT
+#undef TNullString_CharT