Merge inbound to mozilla-central. a=merge
authorCosmin Sabou <csabou@mozilla.com>
Sun, 15 Apr 2018 02:37:03 +0300
changeset 466914 008f977fc4ac4fc09b98580ff4355b03c14f1e19
parent 466895 ffcb6aa7e8717e58ee14e13eb370e2f902c9d03c (current diff)
parent 466913 821b3597208e2bec078c93f2109f54b8a1464f82 (diff)
child 466915 a0c455e036df741556d695cf46baeacc581c0c58
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
js/src/jsapi-tests/testToIntWidth.cpp
--- a/docshell/base/nsDefaultURIFixup.cpp
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -2,32 +2,32 @@
 /* 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 "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
-#include "nsCRT.h"
 
 #include "nsIFile.h"
 #include <algorithm>
 
 #ifdef MOZ_TOOLKIT_SEARCH
 #include "nsIBrowserSearchService.h"
 #endif
 
 #include "nsIURIFixup.h"
 #include "nsIURIMutator.h"
 #include "nsDefaultURIFixup.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/ipc/URIUtils.h"
+#include "mozilla/TextUtils.h"
 #include "mozilla/Tokenizer.h"
 #include "mozilla/Unused.h"
 #include "nsIObserverService.h"
 #include "nsXULAppAPI.h"
 
 // Used to check if external protocol schemes are usable
 #include "nsCExternalHandlerService.h"
 #include "nsIExternalProtocolService.h"
@@ -741,18 +741,18 @@ nsDefaultURIFixup::PossiblyHostPortUrl(c
   aUrl.EndReading(iterEnd);
   nsACString::const_iterator iter = iterBegin;
 
   while (iter != iterEnd) {
     uint32_t chunkSize = 0;
     // Parse a chunk of the address
     while (iter != iterEnd &&
            (*iter == '-' ||
-            nsCRT::IsAsciiAlpha(*iter) ||
-            nsCRT::IsAsciiDigit(*iter))) {
+            IsAsciiAlpha(*iter) ||
+            IsAsciiDigit(*iter))) {
       ++chunkSize;
       ++iter;
     }
     if (chunkSize == 0 || iter == iterEnd) {
       return false;
     }
     if (*iter == ':') {
       // Go onto checking the for the digits
@@ -770,17 +770,17 @@ nsDefaultURIFixup::PossiblyHostPortUrl(c
   }
   ++iter;
 
   // Count the number of digits after the colon and before the
   // next forward slash (or end of string)
 
   uint32_t digitCount = 0;
   while (iter != iterEnd && digitCount <= 5) {
-    if (nsCRT::IsAsciiDigit(*iter)) {
+    if (IsAsciiDigit(*iter)) {
       digitCount++;
     } else if (*iter == '/') {
       break;
     } else {
       // Whatever it is, it ain't a port!
       return false;
     }
     ++iter;
@@ -849,17 +849,17 @@ nsDefaultURIFixup::KeywordURIFixup(const
   while (iter != iterEnd) {
     if (pos >= 1 && foundRSBrackets == 0) {
       if (!(lastLSBracketLoc == 0 &&
             (*iter == ':' ||
              *iter == '.' ||
              *iter == ']' ||
              (*iter >= 'a' && *iter <= 'f') ||
              (*iter >= 'A' && *iter <= 'F') ||
-             nsCRT::IsAsciiDigit(*iter)))) {
+             IsAsciiDigit(*iter)))) {
         looksLikeIpv6 = false;
       }
     }
 
     // If we're at the end of the string or this is the first slash,
     // check if the thing before the slash looks like ipv4:
     if ((iterEnd - iter == 1 ||
          (lastSlashLoc == uint32_t(kNotFound) && *iter == '/')) &&
@@ -895,19 +895,19 @@ nsDefaultURIFixup::KeywordURIFixup(const
                firstQuoteLoc == uint32_t(kNotFound)) {
       firstQuoteLoc = pos;
     } else if (*iter == '[') {
       lastLSBracketLoc = pos;
     } else if (*iter == ']') {
       foundRSBrackets++;
     } else if (*iter == '/') {
       lastSlashLoc = pos;
-    } else if (nsCRT::IsAsciiAlpha(*iter)) {
+    } else if (IsAsciiAlpha(*iter)) {
       hasAsciiAlpha = true;
-    } else if (nsCRT::IsAsciiDigit(*iter)) {
+    } else if (IsAsciiDigit(*iter)) {
       ++foundDigits;
     }
 
     pos++;
     iter++;
   }
 
   if (lastLSBracketLoc > 0 || foundRSBrackets != 1) {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -6154,18 +6154,23 @@ nsDocShell::GetCurScrollPos(int32_t aScr
 
 nsresult
 nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
                               int32_t aCurVerticalPos)
 {
   nsIScrollableFrame* sf = GetRootScrollFrame();
   NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
 
-  sf->ScrollTo(nsPoint(aCurHorizontalPos, aCurVerticalPos),
-               nsIScrollableFrame::INSTANT);
+  nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
+  if (sf->GetScrollbarStyles().mScrollBehavior ==
+        NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
+    scrollMode = nsIScrollableFrame::SMOOTH_MSD;
+  }
+
+  sf->ScrollTo(nsPoint(aCurHorizontalPos, aCurVerticalPos), scrollMode);
   return NS_OK;
 }
 
 //*****************************************************************************
 // nsDocShell::nsIScrollable
 //*****************************************************************************
 
 NS_IMETHODIMP
--- a/dom/base/ResponsiveImageSelector.cpp
+++ b/dom/base/ResponsiveImageSelector.cpp
@@ -1,16 +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 "mozilla/dom/ResponsiveImageSelector.h"
 #include "mozilla/ServoStyleSetInlines.h"
+#include "mozilla/TextUtils.h"
 #include "nsIURI.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsPresContext.h"
 
 #include "nsCSSParser.h"
 #include "nsCSSProps.h"
 
@@ -50,47 +51,47 @@ ParseFloat(const nsAString& aString, dou
   if (iter == end) {
     return false;
   }
 
   if (*iter == char16_t('-') && ++iter == end) {
     return false;
   }
 
-  if (nsCRT::IsAsciiDigit(*iter)) {
-    for (; iter != end && nsCRT::IsAsciiDigit(*iter) ; ++iter);
+  if (IsAsciiDigit(*iter)) {
+    for (; iter != end && IsAsciiDigit(*iter) ; ++iter);
   } else if (*iter == char16_t('.')) {
     // Do nothing, jumps to fraction part
   } else {
     return false;
   }
 
   // Fraction
   if (*iter == char16_t('.')) {
     ++iter;
-    if (iter == end || !nsCRT::IsAsciiDigit(*iter)) {
+    if (iter == end || !IsAsciiDigit(*iter)) {
       // U+002E FULL STOP character (.) must be followed by one or more ASCII digits
       return false;
     }
 
-    for (; iter != end && nsCRT::IsAsciiDigit(*iter) ; ++iter);
+    for (; iter != end && IsAsciiDigit(*iter) ; ++iter);
   }
 
   if (iter != end && (*iter == char16_t('e') || *iter == char16_t('E'))) {
     ++iter;
     if (*iter == char16_t('-') || *iter == char16_t('+')) {
       ++iter;
     }
 
-    if (iter == end || !nsCRT::IsAsciiDigit(*iter)) {
+    if (iter == end || !IsAsciiDigit(*iter)) {
       // Should have one or more ASCII digits
       return false;
     }
 
-    for (; iter != end && nsCRT::IsAsciiDigit(*iter) ; ++iter);
+    for (; iter != end && IsAsciiDigit(*iter) ; ++iter);
   }
 
   if (iter != end) {
     return false;
   }
 
   nsresult rv;
   aDouble = PromiseFlatString(aString).ToDouble(&rv);
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3838,23 +3838,35 @@ nsDocument::TryChannelCharset(nsIChannel
     }
   }
 }
 
 static inline void
 AssertNoStaleServoDataIn(const nsINode& aSubtreeRoot)
 {
 #ifdef DEBUG
-  for (const nsINode* node = aSubtreeRoot.GetFirstChild();
+  for (const nsINode* node = &aSubtreeRoot;
        node;
-       node = node->GetNextNode()) {
-    if (node->IsElement()) {
-      MOZ_ASSERT(!node->AsElement()->HasServoData());
-      if (auto* shadow = node->AsElement()->GetShadowRoot()) {
-        AssertNoStaleServoDataIn(*shadow);
+       node = node->GetNextNode(&aSubtreeRoot)) {
+    if (!node->IsElement()) {
+      continue;
+    }
+    MOZ_ASSERT(!node->AsElement()->HasServoData());
+    if (auto* shadow = node->AsElement()->GetShadowRoot()) {
+      AssertNoStaleServoDataIn(*shadow);
+    }
+    if (nsXBLBinding* binding = node->AsElement()->GetXBLBinding()) {
+      if (nsXBLBinding* bindingWithContent = binding->GetBindingWithContent()) {
+        nsIContent* content = bindingWithContent->GetAnonymousContent();
+        MOZ_ASSERT(!content->AsElement()->HasServoData());
+        for (nsINode* child = content->GetFirstChild();
+             child;
+             child = child->GetNextSibling()) {
+          AssertNoStaleServoDataIn(*child);
+        }
       }
     }
   }
 #endif
 }
 
 already_AddRefed<nsIPresShell>
 nsIDocument::CreateShell(nsPresContext* aContext,
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -235,25 +235,17 @@ public:
     return HasFlag(NODE_IS_ANONYMOUS_ROOT);
   }
 
   /**
    * Returns true if there is NOT a path through child lists
    * from the top of this node's parent chain back to this node or
    * if the node is in native anonymous subtree without a parent.
    */
-  bool IsInAnonymousSubtree() const
-  {
-    NS_ASSERTION(!IsInNativeAnonymousSubtree() || GetBindingParent() ||
-                 (!IsInUncomposedDoc() &&
-                  static_cast<nsIContent*>(SubtreeRoot())->IsInNativeAnonymousSubtree()),
-                 "Must have binding parent when in native anonymous subtree which is in document.\n"
-                 "Native anonymous subtree which is not in document must have native anonymous root.");
-    return IsInNativeAnonymousSubtree() || (!IsInShadowTree() && GetBindingParent() != nullptr);
-  }
+  inline bool IsInAnonymousSubtree() const;
 
   /**
    * Return true iff this node is in an HTML document (in the HTML5 sense of
    * the term, i.e. not in an XHTML/XML document).
    */
   inline bool IsInHTMLDocument() const;
 
 
--- a/dom/base/nsIContentInlines.h
+++ b/dom/base/nsIContentInlines.h
@@ -171,12 +171,37 @@ nsIContent::IsActiveChildrenElement() co
     return false;
   }
 
   nsIContent* bindingParent = GetBindingParent();
   if (!bindingParent) {
     return false;
   }
 
+  // We reuse the binding parent machinery for Shadow DOM too, so prevent that
+  // from getting us confused in this case.
+  return !bindingParent->GetShadowRoot();
+}
+
+inline bool
+nsIContent::IsInAnonymousSubtree() const
+{
+  NS_ASSERTION(!IsInNativeAnonymousSubtree() || GetBindingParent() ||
+               (!IsInUncomposedDoc() &&
+                static_cast<nsIContent*>(SubtreeRoot())->IsInNativeAnonymousSubtree()),
+               "Must have binding parent when in native anonymous subtree which is in document.\n"
+               "Native anonymous subtree which is not in document must have native anonymous root.");
+
+  if (IsInNativeAnonymousSubtree()) {
+    return true;
+  }
+
+  nsIContent* bindingParent = GetBindingParent();
+  if (!bindingParent) {
+    return false;
+  }
+
+  // We reuse the binding parent machinery for Shadow DOM too, so prevent that
+  // from getting us confused in this case.
   return !bindingParent->GetShadowRoot();
 }
 
 #endif // nsIContentInlines_h
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/TouchEvents.h"
 #include "nsContentUtils.h"
 #include "nsCOMPtr.h"
 #include "nsDeviceContext.h"
 #include "nsError.h"
 #include "nsGlobalWindow.h"
 #include "nsIFrame.h"
 #include "nsIContent.h"
+#include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsIScrollableFrame.h"
 #include "nsJSEnvironment.h"
 #include "nsLayoutUtils.h"
 #include "nsPIWindowRoot.h"
 #include "nsRFPService.h"
 
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsPresContext.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include <new>
 #include "nsIContent.h"
+#include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsINode.h"
 #include "nsPIDOMWindow.h"
 #include "AnimationEvent.h"
 #include "BeforeUnloadEvent.h"
 #include "ClipboardEvent.h"
 #include "CommandEvent.h"
 #include "CompositionEvent.h"
--- a/dom/events/WheelHandlingHelper.cpp
+++ b/dom/events/WheelHandlingHelper.cpp
@@ -11,16 +11,18 @@
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/WheelEventBinding.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
+#include "nsIContentInlines.h"
+#include "nsIDocument.h"
 #include "nsIDocumentInlines.h"         // for nsIDocument and HTMLBodyElement
 #include "nsIPresShell.h"
 #include "nsIScrollableFrame.h"
 #include "nsITextControlElement.h"
 #include "nsITimer.h"
 #include "nsPluginFrame.h"
 #include "nsPresContext.h"
 #include "prtime.h"
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -99,16 +99,17 @@
 #include "nsContentUtils.h"
 #include "mozilla/dom/DirectionalityUtils.h"
 #include "nsRadioVisitor.h"
 #include "nsTextEditorState.h"
 
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/MathAlgorithms.h"
+#include "mozilla/TextUtils.h"
 
 #include "nsIIDNService.h"
 
 #include <limits>
 
 #include "nsIColorPicker.h"
 #include "nsIStringEnumerator.h"
 #include "HTMLSplitOnSpacesTokenizer.h"
@@ -5043,17 +5044,17 @@ HTMLInputElement::SanitizeValue(nsAStrin
 
 bool HTMLInputElement::IsValidSimpleColor(const nsAString& aValue) const
 {
   if (aValue.Length() != 7 || aValue.First() != '#') {
     return false;
   }
 
   for (int i = 1; i < 7; ++i) {
-    if (!nsCRT::IsAsciiDigit(aValue[i]) &&
+    if (!IsAsciiDigit(aValue[i]) &&
         !(aValue[i] >= 'a' && aValue[i] <= 'f') &&
         !(aValue[i] >= 'A' && aValue[i] <= 'F')) {
       return false;
     }
   }
   return true;
 }
 
@@ -5351,17 +5352,17 @@ HTMLInputElement::NumberOfDaysInMonth(ui
 /* static */ bool
 HTMLInputElement::DigitSubStringToNumber(const nsAString& aStr,
                                          uint32_t aStart, uint32_t aLen,
                                          uint32_t* aRetVal)
 {
   MOZ_ASSERT(aStr.Length() > (aStart + aLen - 1));
 
   for (uint32_t offset = 0; offset < aLen; ++offset) {
-    if (!NS_IsAsciiDigit(aStr[aStart + offset])) {
+    if (!IsAsciiDigit(aStr[aStart + offset])) {
       return false;
     }
   }
 
   nsresult ec;
   *aRetVal = static_cast<uint32_t>(PromiseFlatString(Substring(aStr, aStart, aLen)).ToInteger(&ec));
 
   return NS_SUCCEEDED(ec);
--- a/dom/html/input/SingleLineTextInputTypes.cpp
+++ b/dom/html/input/SingleLineTextInputTypes.cpp
@@ -3,24 +3,28 @@
 /* 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 "SingleLineTextInputTypes.h"
 
 #include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/TextUtils.h"
 #include "HTMLSplitOnSpacesTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsCRTGlue.h"
 #include "nsIIDNService.h"
 #include "nsIIOService.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 
+using mozilla::IsAsciiAlpha;
+using mozilla::IsAsciiDigit;
+
 bool
 SingleLineTextInputTypeBase::IsMutable() const
 {
   return !mInputElement->IsDisabled() &&
          !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly);
 }
 
 bool
@@ -218,17 +222,17 @@ EmailInputType::IsValidEmailAddress(cons
   uint32_t length = value.Length();
   uint32_t i = 0;
 
   // Parsing the username.
   for (; i < atPos; ++i) {
     char16_t c = value[i];
 
     // The username characters have to be in this list to be valid.
-    if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) ||
+    if (!(IsAsciiAlpha(c) || IsAsciiDigit(c) ||
           c == '.' || c == '!' || c == '#' || c == '$' || c == '%' ||
           c == '&' || c == '\''|| c == '*' || c == '+' || c == '-' ||
           c == '/' || c == '=' || c == '?' || c == '^' || c == '_' ||
           c == '`' || c == '{' || c == '|' || c == '}' || c == '~' )) {
       return false;
     }
   }
 
@@ -249,17 +253,17 @@ EmailInputType::IsValidEmailAddress(cons
       if (value[i-1] == '.' || value[i-1] == '-') {
         return false;
       }
     } else if (c == '-'){
       // A dash can't follow a dot.
       if (value[i-1] == '.') {
         return false;
       }
-    } else if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) ||
+    } else if (!(IsAsciiAlpha(c) || IsAsciiDigit(c) ||
                  c == '-')) {
       // The domain characters have to be in this list to be valid.
       return false;
     }
   }
 
   return true;
 }
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -168,16 +168,37 @@ nsGenericHTMLElement::CopyInnerTo(Elemen
 static const nsAttrValue::EnumTable kDirTable[] = {
   { "ltr", eDir_LTR },
   { "rtl", eDir_RTL },
   { "auto", eDir_Auto },
   { nullptr, 0 }
 };
 
 void
+nsGenericHTMLElement::AddToNameTable(nsAtom* aName)
+{
+  MOZ_ASSERT(HasName(), "Node doesn't have name?");
+  nsIDocument* doc = GetUncomposedDoc();
+  if (doc && !IsInAnonymousSubtree()) {
+    doc->AddToNameTable(this, aName);
+  }
+}
+
+void
+nsGenericHTMLElement::RemoveFromNameTable()
+{
+  if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
+    if (nsIDocument* doc = GetUncomposedDoc()) {
+      doc->RemoveFromNameTable(this,
+                               GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
+    }
+  }
+}
+
+void
 nsGenericHTMLElement::GetAccessKeyLabel(nsString& aLabel)
 {
   nsAutoString suffix;
   GetAccessKey(suffix);
   if (!suffix.IsEmpty()) {
     EventStateManager::GetAccessKeyLabelPrefix(this, aLabel);
     aLabel.Append(suffix);
   }
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -693,32 +693,18 @@ public:
     // HasName() is true precisely when name is nonempty.
     return aElement->IsHTMLElement(nsGkAtoms::img) && aElement->HasName();
   }
 
 protected:
   /**
    * Add/remove this element to the documents name cache
    */
-  void AddToNameTable(nsAtom* aName) {
-    NS_ASSERTION(HasName(), "Node doesn't have name?");
-    nsIDocument* doc = GetUncomposedDoc();
-    if (doc && !IsInAnonymousSubtree()) {
-      doc->AddToNameTable(this, aName);
-    }
-  }
-  void RemoveFromNameTable() {
-    if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
-      nsIDocument* doc = GetUncomposedDoc();
-      if (doc) {
-        doc->RemoveFromNameTable(this, GetParsedAttr(nsGkAtoms::name)->
-                                         GetAtomValue());
-      }
-    }
-  }
+  void AddToNameTable(nsAtom* aName);
+  void RemoveFromNameTable();
 
   /**
    * Register or unregister an access key to this element based on the
    * accesskey attribute.
    */
   void RegAccessKey()
   {
     if (HasFlag(NODE_HAS_ACCESSKEY)) {
--- a/dom/mathml/nsMathMLElement.cpp
+++ b/dom/mathml/nsMathMLElement.cpp
@@ -4,19 +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/. */
 
 
 #include "nsMathMLElement.h"
 #include "base/compiler_specific.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/FontPropertyTypes.h"
+#include "mozilla/TextUtils.h"
 #include "nsGkAtoms.h"
 #include "nsITableCellLayout.h" // for MAX_COLSPAN / MAX_ROWSPAN
-#include "nsCRT.h"
 #include "nsLayoutStylesheetCache.h"
 #include "nsCSSValue.h"
 #include "nsCSSParser.h"
 #include "nsMappedAttributes.h"
 #include "nsStyleConsts.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
@@ -416,17 +416,17 @@ nsMathMLElement::ParseNumericValue(const
     if (gotDot && c == '.') {
       if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
         ReportLengthParseError(aString, aDocument);
       }
       return false;  // two dots encountered
     }
     else if (c == '.')
       gotDot = true;
-    else if (!nsCRT::IsAsciiDigit(c)) {
+    else if (!IsAsciiDigit(c)) {
       str.Right(unit, stringLength - i);
       // some authors leave blanks before the unit, but that shouldn't
       // be allowed, so don't CompressWhitespace on 'unit'.
       break;
     }
     number.Append(c);
   }
 
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -38,16 +38,17 @@
 #include "mozilla/dom/quota/PQuotaUsageRequestParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/TextUtils.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 #include "mozStorageCID.h"
 #include "mozStorageHelper.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsComponentManagerUtils.h"
 #include "nsAboutProtocolUtils.h"
 #include "nsCharSeparatedTokenizer.h"
@@ -8328,17 +8329,17 @@ OriginParser::HandleToken(const nsDepend
     case eExpectingAppIdOrScheme: {
       if (aToken.IsEmpty()) {
         QM_WARNING("Expected an app id or scheme (not an empty string)!");
 
         mError = true;
         return;
       }
 
-      if (NS_IsAsciiDigit(aToken.First())) {
+      if (IsAsciiDigit(aToken.First())) {
         // nsDependentCSubstring doesn't provice ToInteger()
         nsCString token(aToken);
 
         nsresult rv;
         uint32_t appId = token.ToInteger(&rv);
         if (NS_SUCCEEDED(rv)) {
           mAppId = appId;
           mState = eExpectingInMozBrowser;
@@ -8489,17 +8490,17 @@ OriginParser::HandleToken(const nsDepend
 
         mState =
           mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
                                      : eComplete;
 
         return;
       }
 
-      if (aToken.Length() == 1 && NS_IsAsciiAlpha(aToken.First())) {
+      if (aToken.Length() == 1 && IsAsciiAlpha(aToken.First())) {
         mMaybeDriveLetter = true;
 
         mPathnameComponents.AppendElement(aToken);
 
         mState =
           mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
                                      : eComplete;
 
--- a/dom/svg/SVGAElement.cpp
+++ b/dom/svg/SVGAElement.cpp
@@ -29,26 +29,36 @@ SVGAElement::WrapNode(JSContext *aCx, JS
 
 nsSVGElement::StringInfo SVGAElement::sStringInfo[3] =
 {
   { &nsGkAtoms::href, kNameSpaceID_None, true },
   { &nsGkAtoms::href, kNameSpaceID_XLink, true },
   { &nsGkAtoms::target, kNameSpaceID_None, true }
 };
 
+// static
+const DOMTokenListSupportedToken SVGAElement::sSupportedRelValues[] = {
+  "noreferrer",
+  "noopener",
+  nullptr
+};
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
-NS_INTERFACE_MAP_BEGIN(SVGAElement)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAElement)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNode)
   NS_INTERFACE_MAP_ENTRY(nsIDOMElement)
   NS_INTERFACE_MAP_ENTRY(Link)
 NS_INTERFACE_MAP_END_INHERITING(SVGAElementBase)
 
+NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGAElement,
+                                   SVGAElementBase,
+                                   mRelList)
+
 NS_IMPL_ADDREF_INHERITED(SVGAElement, SVGAElementBase)
 NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase)
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGAElement::SVGAElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : SVGAElementBase(aNodeInfo)
@@ -113,16 +123,96 @@ SVGAElement::GetDownload(nsAString& aDow
 }
 
 void
 SVGAElement::SetDownload(const nsAString& aDownload, ErrorResult& rv)
 {
   SetAttr(nsGkAtoms::download, aDownload, rv);
 }
 
+void
+SVGAElement::GetPing(nsAString& aPing)
+{
+  GetAttr(nsGkAtoms::ping, aPing);
+}
+
+void
+SVGAElement::SetPing(const nsAString& aPing, ErrorResult& rv)
+{
+  SetAttr(nsGkAtoms::ping, aPing, rv);
+}
+
+void
+SVGAElement::GetRel(nsAString& aRel)
+{
+  GetAttr(nsGkAtoms::rel, aRel);
+}
+
+void
+SVGAElement::SetRel(const nsAString& aRel, ErrorResult& rv)
+{
+  SetAttr(nsGkAtoms::rel, aRel, rv);
+}
+
+void
+SVGAElement::GetReferrerPolicy(nsAString& aPolicy)
+{
+  GetEnumAttr(nsGkAtoms::referrerpolicy, EmptyCString().get(), aPolicy);
+}
+
+void
+SVGAElement::SetReferrerPolicy(const nsAString& aPolicy,
+                               mozilla::ErrorResult& rv)
+{
+  SetAttr(nsGkAtoms::referrerpolicy, aPolicy, rv);
+}
+
+nsDOMTokenList*
+SVGAElement:: RelList()
+{
+  if (!mRelList) {
+    mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, sSupportedRelValues);
+  }
+  return mRelList;
+}
+
+void
+SVGAElement::GetHreflang(nsAString& aHreflang)
+{
+  GetAttr(nsGkAtoms::hreflang, aHreflang);
+}
+
+void
+SVGAElement::SetHreflang(const nsAString& aHreflang, mozilla::ErrorResult& rv)
+{
+  SetAttr(nsGkAtoms::hreflang, aHreflang, rv);
+}
+
+void SVGAElement::GetType(nsAString& aType)
+{
+  GetAttr(nsGkAtoms::type, aType);
+}
+
+void SVGAElement::SetType(const nsAString& aType, mozilla::ErrorResult& rv)
+{
+  SetAttr(nsGkAtoms::type, aType, rv);
+}
+
+void SVGAElement::GetText(nsAString& aText, mozilla::ErrorResult& rv)
+{
+  if (NS_WARN_IF(!nsContentUtils::GetNodeTextContent(this, true, aText, fallible))) {
+    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+  }
+}
+
+void SVGAElement::SetText(const nsAString& aText, mozilla::ErrorResult& rv)
+{
+  rv = nsContentUtils::SetNodeTextContent(this, aText, false);
+}
+
 //----------------------------------------------------------------------
 // nsIContent methods
 
 nsresult
 SVGAElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent,
                         nsIContent *aBindingParent,
                         bool aCompileEventHandlers)
 {
--- a/dom/svg/SVGAElement.h
+++ b/dom/svg/SVGAElement.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_SVGAElement_h
 #define mozilla_dom_SVGAElement_h
 
 #include "Link.h"
+#include "nsDOMTokenList.h"
 #include "nsSVGString.h"
 #include "mozilla/dom/SVGGraphicsElement.h"
 
 nsresult NS_NewSVGAElement(nsIContent **aResult,
                            already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 
@@ -22,24 +23,28 @@ class EventChainPreVisitor;
 namespace dom {
 
 typedef SVGGraphicsElement SVGAElementBase;
 
 class SVGAElement final : public SVGAElementBase,
                           public Link
 {
 protected:
+  using Element::GetText;
+
   explicit SVGAElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   friend nsresult (::NS_NewSVGAElement(nsIContent **aResult,
                                        already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
   virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGAElement, SVGAElementBase)
+
   // nsINode interface methods
   void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
   virtual nsresult PostHandleEvent(
                      EventChainPostVisitor& aVisitor) override;
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
   // nsIContent
@@ -62,31 +67,47 @@ public:
                                 bool aNotify) override;
 
   // Link
   virtual bool ElementHasHref() const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> Href();
   already_AddRefed<SVGAnimatedString> Target();
-  void GetDownload(nsAString & aDownload);
-  void SetDownload(const nsAString & aDownload, ErrorResult& rv);
+  void GetDownload(nsAString& aDownload);
+  void SetDownload(const nsAString& aDownload, ErrorResult& rv);
+  void GetPing(nsAString& aPing);
+  void SetPing(const nsAString& aPing, mozilla::ErrorResult& rv);
+  void GetRel(nsAString& aRel);
+  void SetRel(const nsAString& aRel, mozilla::ErrorResult& rv);
+  void SetReferrerPolicy(const nsAString& aReferrerPolicy, mozilla::ErrorResult& rv);
+  void GetReferrerPolicy(nsAString& aReferrerPolicy);
+  nsDOMTokenList* RelList();
+  void GetHreflang(nsAString& aHreflang);
+  void SetHreflang(const nsAString& aHreflang, mozilla::ErrorResult& rv);
+  void GetType(nsAString& aType);
+  void SetType(const nsAString& aType, mozilla::ErrorResult& rv);
+  void GetText(nsAString& aText, mozilla::ErrorResult& rv);
+  void SetText(const nsAString& aText, mozilla::ErrorResult& rv);
 
   void NodeInfoChanged(nsIDocument* aOldDoc) final
   {
     ClearHasPendingLinkUpdate();
     SVGAElementBase::NodeInfoChanged(aOldDoc);
   }
 
 protected:
   virtual ~SVGAElement();
 
   virtual StringAttributesInfo GetStringInfo() override;
 
   enum { HREF, XLINK_HREF, TARGET };
   nsSVGString mStringAttributes[3];
   static StringInfo sStringInfo[3];
+
+  RefPtr<nsDOMTokenList> mRelList;
+  static DOMTokenListSupportedToken sSupportedRelValues[];
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_SVGAElement_h
--- a/dom/svg/nsSVGElement.cpp
+++ b/dom/svg/nsSVGElement.cpp
@@ -621,16 +621,21 @@ nsSVGElement::ParseAttribute(int32_t aNa
       }
     }
 
     if (aAttribute == nsGkAtoms::_class) {
       mClassAttribute.SetBaseValue(aValue, this, false);
       aResult.ParseAtomArray(aValue);
       return true;
     }
+
+    if (aAttribute == nsGkAtoms::rel) {
+      aResult.ParseAtomArray(aValue);
+      return true;
+    }
   }
 
   if (!foundMatch) {
     // Check for nsSVGString attribute
     StringAttributesInfo stringInfo = GetStringInfo();
     for (uint32_t i = 0; i < stringInfo.mStringCount; i++) {
       if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID &&
           aAttribute == *stringInfo.mStringInfo[i].mName) {
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/TextUtils.h"
 
 #include "nspr.h"
 
 #include "nsIFileStreams.h"       // New Necko file streams
 #include <algorithm>
 
 #include "nsAutoPtr.h"
 #include "nsNetCID.h"
@@ -25,17 +26,16 @@
 #include "nsIHttpChannelInternal.h"
 #include "nsIEncodedChannel.h"
 #include "nsIUploadChannel.h"
 #include "nsICacheInfoChannel.h"
 #include "nsIFileChannel.h"
 #include "nsEscape.h"
 #include "nsUnicharUtils.h"
 #include "nsIStringEnumerator.h"
-#include "nsCRT.h"
 #include "nsContentCID.h"
 #include "nsStreamUtils.h"
 
 #include "nsCExternalHandlerService.h"
 
 #include "nsIURL.h"
 #include "nsIFileURL.h"
 #include "nsIURIMutator.h"
@@ -2137,17 +2137,17 @@ nsWebBrowserPersist::MakeFilenameFromURI
         {
             // Unescape the file name (GetFileName escapes it)
             NS_UnescapeURL(nameFromURL);
             uint32_t nameLength = 0;
             const char *p = nameFromURL.get();
             for (;*p && *p != ';' && *p != '?' && *p != '#' && *p != '.'
                  ;p++)
             {
-                if (nsCRT::IsAsciiAlpha(*p) || nsCRT::IsAsciiDigit(*p)
+                if (IsAsciiAlpha(*p) || IsAsciiDigit(*p)
                     || *p == '.' || *p == '-' ||  *p == '_' || (*p == ' '))
                 {
                     fileName.Append(char16_t(*p));
                     if (++nameLength == kDefaultMaxFilenameLength)
                     {
                         // Note:
                         // There is no point going any further since it will be
                         // truncated in CalculateUniqueFilename anyway.
--- a/dom/webidl/SVGAElement.webidl
+++ b/dom/webidl/SVGAElement.webidl
@@ -10,12 +10,27 @@
  * liability, trademark and document use rules apply.
  */
 
 interface SVGAElement : SVGGraphicsElement {
   readonly attribute SVGAnimatedString target;
 
   [SetterThrows]
   attribute DOMString download;
+  [SetterThrows]
+  attribute DOMString ping;
+  [SetterThrows]
+  attribute DOMString rel;
+  [SetterThrows]
+  attribute DOMString referrerPolicy;
+  [PutForwards=value]
+  readonly attribute DOMTokenList relList;
+  [SetterThrows]
+  attribute DOMString hreflang;
+  [SetterThrows]
+  attribute DOMString type;
+
+  [Throws]
+  attribute DOMString text;
 };
 
 SVGAElement implements SVGURIReference;
 
--- a/dom/xbl/XBLChildrenElement.cpp
+++ b/dom/xbl/XBLChildrenElement.cpp
@@ -39,16 +39,43 @@ XBLChildrenElement::BeforeSetAttr(int32_
         }
       }
     }
   }
 
   return nsXMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
 }
 
+void
+XBLChildrenElement::DoRemoveDefaultContent(bool aNotify)
+{
+  // Default content is going away, need to tell layout about it first.
+  MOZ_ASSERT(HasChildren(), "Why bothering?");
+  MOZ_ASSERT(GetParentElement());
+
+  // We don't want to do this from frame construction while setting up the
+  // binding initially.
+  if (aNotify) {
+    Element* parent = GetParentElement();
+    if (nsIDocument* doc = parent->GetComposedDoc()) {
+      if (nsIPresShell* shell = doc->GetShell()) {
+        shell->DestroyFramesForAndRestyle(parent);
+      }
+    }
+  }
+
+  for (nsIContent* child = static_cast<nsINode*>(this)->GetFirstChild();
+       child;
+       child = child->GetNextSibling()) {
+    MOZ_ASSERT(!child->GetPrimaryFrame());
+    MOZ_ASSERT(!child->IsElement() || !child->AsElement()->HasServoData());
+    child->SetXBLInsertionPoint(nullptr);
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsAnonymousContentList, mParent)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnonymousContentList)
--- a/dom/xbl/XBLChildrenElement.h
+++ b/dom/xbl/XBLChildrenElement.h
@@ -33,80 +33,82 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsINode interface methods
   virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult,
                          bool aPreallocateChildren) const override;
 
   virtual nsIDOMNode* AsDOMNode() override { return this; }
 
-  void AppendInsertedChild(nsIContent* aChild)
+  void AppendInsertedChild(nsIContent* aChild, bool aNotify)
   {
+    // Appending an inserted child causes the inserted
+    // children to be projected instead of default content.
+    MaybeRemoveDefaultContent(aNotify);
+
     mInsertedChildren.AppendElement(aChild);
     aChild->SetXBLInsertionPoint(this);
-
-    // Appending an inserted child causes the inserted
-    // children to be projected instead of default content.
-    MaybeRemoveDefaultContent();
   }
 
   void InsertInsertedChildAt(nsIContent* aChild, uint32_t aIndex)
   {
+    // Inserting an inserted child causes the inserted
+    // children to be projected instead of default content.
+    MaybeRemoveDefaultContent(true);
+
     mInsertedChildren.InsertElementAt(aIndex, aChild);
     aChild->SetXBLInsertionPoint(this);
-
-    // Inserting an inserted child causes the inserted
-    // children to be projected instead of default content.
-    MaybeRemoveDefaultContent();
   }
 
   void RemoveInsertedChild(nsIContent* aChild)
   {
     // Can't use this assertion as we cheat for dynamic insertions and
     // only insert in the innermost insertion point.
     //NS_ASSERTION(mInsertedChildren.Contains(aChild),
     //             "Removing child that's not there");
     mInsertedChildren.RemoveElement(aChild);
 
     // After removing the inserted child, default content
     // may be projected into this insertion point.
+    //
+    // FIXME: Layout should be told about this before clearing
+    // mInsertedChildren, this leaves stale styles and frames in the frame tree.
     MaybeSetupDefaultContent();
   }
 
   void ClearInsertedChildren()
   {
     for (auto* child : mInsertedChildren) {
       child->SetXBLInsertionPoint(nullptr);
     }
     mInsertedChildren.Clear();
 
     // After clearing inserted children, default content
     // will be projected into this insertion point.
+    //
+    // FIXME: Layout should be told about this before clearing
+    // mInsertedChildren, this leaves stale styles and frames in the frame tree.
     MaybeSetupDefaultContent();
   }
 
   void MaybeSetupDefaultContent()
   {
     if (!HasInsertedChildren()) {
       for (nsIContent* child = static_cast<nsINode*>(this)->GetFirstChild();
            child;
            child = child->GetNextSibling()) {
         child->SetXBLInsertionPoint(this);
       }
     }
   }
 
-  void MaybeRemoveDefaultContent()
+  void MaybeRemoveDefaultContent(bool aNotify)
   {
-    if (!HasInsertedChildren()) {
-      for (nsIContent* child = static_cast<nsINode*>(this)->GetFirstChild();
-           child;
-           child = child->GetNextSibling()) {
-        child->SetXBLInsertionPoint(nullptr);
-      }
+    if (!HasInsertedChildren() && HasChildren()) {
+      DoRemoveDefaultContent(aNotify);
     }
   }
 
   uint32_t InsertedChildrenLength()
   {
     return mInsertedChildren.Length();
   }
 
@@ -138,16 +140,18 @@ public:
   }
 
 protected:
   ~XBLChildrenElement();
   virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
                                  const nsAttrValueOrString* aValue,
                                  bool aNotify) override;
 
+  void DoRemoveDefaultContent(bool aNotify);
+
 private:
   nsTArray<nsIContent*> mInsertedChildren; // WEAK
   nsTArray<RefPtr<nsAtom> > mIncludes;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/xbl/nsBindingManager.cpp
+++ b/dom/xbl/nsBindingManager.cpp
@@ -782,17 +782,17 @@ nsBindingManager::ContentAppended(nsICon
 
     // Even though we're in ContentAppended, nested insertion points force us
     // to deal with this append as an insertion except in the outermost
     // binding.
     if (first) {
       first = false;
       for (nsIContent* child = aFirstNewContent; child;
            child = child->GetNextSibling()) {
-        point->AppendInsertedChild(child);
+        point->AppendInsertedChild(child, true);
       }
     } else {
       InsertAppendedContent(point, aFirstNewContent);
     }
 
     nsIContent* newParent = point->GetParent();
     if (newParent == parent) {
       break;
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -342,27 +342,32 @@ nsXBLBinding::GenerateAnonymousContent()
     // pointer which would make the GetNextNode call above fail
     BindAnonymousContent(mContent, mBoundElement,
                          mPrototypeBinding->ChromeOnlyContent());
 
     // Insert explicit children into insertion points
     if (mDefaultInsertionPoint && mInsertionPoints.IsEmpty()) {
       ExplicitChildIterator iter(mBoundElement);
       for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
-        mDefaultInsertionPoint->AppendInsertedChild(child);
+        // Pass aNotify = false because we're just setting up the whole thing.
+        // Furthermore we do it from frame construction, so passing true here
+        // would reenter into it which is... not great.
+        mDefaultInsertionPoint->AppendInsertedChild(child, false);
       }
     } else {
       // It is odd to come into this code if mInsertionPoints is not empty, but
       // we need to make sure to do the compatibility hack below if the bound
       // node has any non <xul:template> or <xul:observes> children.
       ExplicitChildIterator iter(mBoundElement);
       for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
         XBLChildrenElement* point = FindInsertionPointForInternal(child);
         if (point) {
-          point->AppendInsertedChild(child);
+          // Pass aNotify = false because we're just setting up the whole thing.
+          // (see the similar call above for more details).
+          point->AppendInsertedChild(child, false);
         } else {
           NodeInfo *ni = child->NodeInfo();
           if (ni->NamespaceID() != kNameSpaceID_XUL ||
               (!ni->Equals(nsGkAtoms::_template) &&
                !ni->Equals(nsGkAtoms::observes))) {
             // Compatibility hack. For some reason the original XBL
             // implementation dropped the content of a binding if any child of
             // the bound element didn't match any of the <children> in the
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -64,16 +64,17 @@
 #include "nsISelectionPrivate.h"
 #include "nsISelectionController.h"
 #include "nsIServiceManager.h"
 #include "nsITextServicesFilter.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsIContent.h"
+#include "nsIContentInlines.h"
 #include "nsRange.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsITextControlElement.h"
 #include "prtime.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -2793,17 +2793,17 @@ FilterNodeArithmeticCombineSoftware::Inp
 }
 
 void
 FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex,
                                                   const Float* aFloat,
                                                   uint32_t aSize)
 {
   MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
-  MOZ_ASSERT(aSize == 4);
+  MOZ_RELEASE_ASSERT(aSize == 4);
 
   mK1 = aFloat[0];
   mK2 = aFloat[1];
   mK3 = aFloat[2];
   mK4 = aFloat[3];
 
   Invalidate();
 }
--- a/js/public/Conversions.h
+++ b/js/public/Conversions.h
@@ -282,37 +282,35 @@ ToObject(JSContext* cx, HandleValue v)
 {
     detail::AssertArgumentsAreSane(cx, v);
 
     if (v.isObject())
         return &v.toObject();
     return js::ToObjectSlow(cx, v, false);
 }
 
-namespace detail {
-
-/*
- * Convert a double value to ResultType (an unsigned integral type) using
+/**
+ * Convert a double value to UnsignedInteger (an unsigned integral type) using
  * ECMAScript-style semantics (that is, in like manner to how ECMAScript's
  * ToInt32 converts to int32_t).
  *
  *   If d is infinite or NaN, return 0.
- *   Otherwise compute d2 = sign(d) * floor(abs(d)), and return the ResultType
- *   value congruent to d2 mod 2**(bit width of ResultType).
+ *   Otherwise compute d2 = sign(d) * floor(abs(d)), and return the
+ *   UnsignedInteger value congruent to d2 % 2**(bit width of UnsignedInteger).
  *
  * The algorithm below is inspired by that found in
- * <http://trac.webkit.org/changeset/67825/trunk/JavaScriptCore/runtime/JSValue.cpp>
+ * <https://trac.webkit.org/changeset/67825/webkit/trunk/JavaScriptCore/runtime/JSValue.cpp>
  * but has been generalized to all integer widths.
  */
-template<typename ResultType>
-inline ResultType
-ToUintWidth(double d)
+template<typename UnsignedInteger>
+inline UnsignedInteger
+ToUnsignedInteger(double d)
 {
-    static_assert(mozilla::IsUnsigned<ResultType>::value,
-                  "ResultType must be an unsigned type");
+    static_assert(mozilla::IsUnsigned<UnsignedInteger>::value,
+                  "UnsignedInteger must be an unsigned type");
 
     uint64_t bits = mozilla::BitwiseCast<uint64_t>(d);
     unsigned DoubleExponentShift = mozilla::FloatingPoint<double>::kExponentShift;
 
     // Extract the exponent component.  (Be careful here!  It's not technically
     // the exponent in NaN, infinities, and subnormals.)
     int_fast16_t exp =
         int_fast16_t((bits & mozilla::FloatingPoint<double>::kExponentBits) >> DoubleExponentShift) -
@@ -321,33 +319,33 @@ ToUintWidth(double d)
     // If the exponent's less than zero, abs(d) < 1, so the result is 0.  (This
     // also handles subnormals.)
     if (exp < 0)
         return 0;
 
     uint_fast16_t exponent = mozilla::AssertedCast<uint_fast16_t>(exp);
 
     // If the exponent is greater than or equal to the bits of precision of a
-    // double plus ResultType's width, the number is either infinite, NaN, or
-    // too large to have lower-order bits in the congruent value.  (Example:
+    // double plus UnsignedInteger's width, the number is either infinite, NaN,
+    // or too large to have lower-order bits in the congruent value.  (Example:
     // 2**84 is exactly representable as a double.  The next exact double is
-    // 2**84 + 2**32.  Thus if ResultType is int32_t, an exponent >= 84 implies
-    // floor(abs(d)) == 0 mod 2**32.)  Return 0 in all these cases.
-    const size_t ResultWidth = CHAR_BIT * sizeof(ResultType);
+    // 2**84 + 2**32.  Thus if UnsignedInteger is uint32_t, an exponent >= 84
+    // implies floor(abs(d)) == 0 mod 2**32.)  Return 0 in all these cases.
+    constexpr size_t ResultWidth = CHAR_BIT * sizeof(UnsignedInteger);
     if (exponent >= DoubleExponentShift + ResultWidth)
         return 0;
 
     // The significand contains the bits that will determine the final result.
     // Shift those bits left or right, according to the exponent, to their
     // locations in the unsigned binary representation of floor(abs(d)).
-    static_assert(sizeof(ResultType) <= sizeof(uint64_t),
-                  "Left-shifting below would lose upper bits");
-    ResultType result = (exponent > DoubleExponentShift)
-                        ? ResultType(bits << (exponent - DoubleExponentShift))
-                        : ResultType(bits >> (DoubleExponentShift - exponent));
+    static_assert(sizeof(UnsignedInteger) <= sizeof(uint64_t),
+                  "left-shifting below would lose upper bits");
+    UnsignedInteger result = (exponent > DoubleExponentShift)
+                             ? UnsignedInteger(bits << (exponent - DoubleExponentShift))
+                             : UnsignedInteger(bits >> (DoubleExponentShift - exponent));
 
     // Two further complications remain.  First, |result| may contain bogus
     // sign/exponent bits.  Second, IEEE-754 numbers' significands (excluding
     // subnormals, but we already handled those) have an implicit leading 1
     // which may affect the final result.
     //
     // It may appear that there's complexity here depending on how ResultWidth
     // and DoubleExponentShift relate, but it turns out there's not.
@@ -364,47 +362,46 @@ ToUintWidth(double d)
     //   bogus bits in |result|.  This implies |exponent < ResultWidth|.  Any
     //   right-shift less than |ResultWidth| does too, which implies
     //   |DoubleExponentShift - ResultWidth < exponent|.  By assumption, then,
     //   |exponent| is negative, but we excluded that above.  So bogus bits
     //   need only |exponent < ResultWidth|.
     //   The implicit leading bit matters identically to the other case, so
     //   again, |exponent < ResultWidth|.
     if (exponent < ResultWidth) {
-        ResultType implicitOne = ResultType(1) << exponent;
+        UnsignedInteger implicitOne = UnsignedInteger(1) << exponent;
         result &= implicitOne - 1; // remove bogus bits
         result += implicitOne; // add the implicit bit
     }
 
     // Compute the congruent value in the signed range.
     return (bits & mozilla::FloatingPoint<double>::kSignBit) ? ~result + 1 : result;
 }
 
-template<typename ResultType>
-inline ResultType
-ToIntWidth(double d)
+template<typename SignedInteger>
+inline SignedInteger
+ToSignedInteger(double d)
 {
-    static_assert(mozilla::IsSigned<ResultType>::value,
-                  "ResultType must be a signed type");
+    static_assert(mozilla::IsSigned<SignedInteger>::value,
+                  "SignedInteger must be a signed type");
 
-    using UnsignedResult = typename mozilla::MakeUnsigned<ResultType>::Type;
-    UnsignedResult u = ToUintWidth<UnsignedResult>(d);
+    using UnsignedInteger = typename mozilla::MakeUnsigned<SignedInteger>::Type;
+    UnsignedInteger u = ToUnsignedInteger<UnsignedInteger>(d);
 
     return mozilla::WrapToSigned(u);
 }
 
-} // namespace detail
+// clang crashes compiling this when targeting arm:
+// https://llvm.org/bugs/show_bug.cgi?id=22974
+#if defined (__arm__) && MOZ_IS_GCC
 
-/* ES5 9.5 ToInt32 (specialized for doubles). */
+template<>
 inline int32_t
-ToInt32(double d)
+ToSignedInteger<int32_t>(double d)
 {
-    // clang crashes compiling this when targeting arm:
-    // https://llvm.org/bugs/show_bug.cgi?id=22974
-#if defined (__arm__) && MOZ_IS_GCC
     int32_t i;
     uint32_t    tmp0;
     uint32_t    tmp1;
     uint32_t    tmp2;
     asm (
     // We use a pure integer solution here. In the 'softfp' ABI, the argument
     // will start in r0 and r1, and VFP can't do all of the necessary ECMA
     // conversions by itself so some integer code will be required anyway. A
@@ -515,64 +512,101 @@ ToInt32(double d)
     // will result in a conversion of '0'.
 "   mov     %0, #0\n"
 "9:\n"
     : "=r" (i), "=&r" (tmp0), "=&r" (tmp1), "=&r" (tmp2), "=&r" (d)
     : "4" (d)
     : "cc"
         );
     return i;
-#else
-    return detail::ToIntWidth<int32_t>(d);
-#endif
 }
 
-/* ES5 9.6 (specialized for doubles). */
-inline uint32_t
-ToUint32(double d)
+#endif // defined (__arm__) && MOZ_IS_GCC
+
+namespace detail {
+
+template<typename IntegerType,
+         bool IsUnsigned = mozilla::IsUnsigned<IntegerType>::value>
+struct ToSignedOrUnsignedInteger;
+
+template<typename IntegerType>
+struct ToSignedOrUnsignedInteger<IntegerType, true>
 {
-    return detail::ToUintWidth<uint32_t>(d);
+    static IntegerType compute(double d) {
+        return ToUnsignedInteger<IntegerType>(d);
+    }
+};
+
+template<typename IntegerType>
+struct ToSignedOrUnsignedInteger<IntegerType, false>
+{
+    static IntegerType compute(double d) {
+        return ToSignedInteger<IntegerType>(d);
+    }
+};
+
+} // namespace detail
+
+template<typename IntegerType>
+inline IntegerType
+ToSignedOrUnsignedInteger(double d)
+{
+    return detail::ToSignedOrUnsignedInteger<IntegerType>::compute(d);
 }
 
 /* WEBIDL 4.2.4 */
 inline int8_t
 ToInt8(double d)
 {
-    return detail::ToIntWidth<int8_t>(d);
+    return ToSignedInteger<int8_t>(d);
 }
 
 /* ECMA-262 7.1.10 ToUInt8() specialized for doubles. */
 inline int8_t
 ToUint8(double d)
 {
-    return detail::ToUintWidth<uint8_t>(d);
+    return ToUnsignedInteger<uint8_t>(d);
 }
 
 /* WEBIDL 4.2.6 */
 inline int16_t
 ToInt16(double d)
 {
-    return detail::ToIntWidth<int16_t>(d);
+    return ToSignedInteger<int16_t>(d);
 }
 
 inline uint16_t
 ToUint16(double d)
 {
-    return detail::ToUintWidth<uint16_t>(d);
+    return ToUnsignedInteger<uint16_t>(d);
+}
+
+/* ES5 9.5 ToInt32 (specialized for doubles). */
+inline int32_t
+ToInt32(double d)
+{
+    return ToSignedInteger<int32_t>(d);
+}
+
+/* ES5 9.6 (specialized for doubles). */
+inline uint32_t
+ToUint32(double d)
+{
+    return ToUnsignedInteger<uint32_t>(d);
 }
 
 /* WEBIDL 4.2.10 */
 inline int64_t
 ToInt64(double d)
 {
-    return detail::ToIntWidth<int64_t>(d);
+    return ToSignedInteger<int64_t>(d);
 }
 
 /* WEBIDL 4.2.11 */
 inline uint64_t
 ToUint64(double d)
 {
-    return detail::ToUintWidth<uint64_t>(d);
+    return ToUnsignedInteger<uint64_t>(d);
 }
 
 } // namespace JS
 
 #endif /* js_Conversions_h */
--- a/js/public/UbiNode.h
+++ b/js/public/UbiNode.h
@@ -752,24 +752,24 @@ class Node {
     template<typename T>
     bool is() const {
         return base()->typeName() == canonicalTypeName<T>();
     }
 
     template<typename T>
     T* as() const {
         MOZ_ASSERT(isLive());
-        MOZ_ASSERT(is<T>());
+        MOZ_ASSERT(this->is<T>());
         return static_cast<T*>(base()->ptr);
     }
 
     template<typename T>
     T* asOrNull() const {
         MOZ_ASSERT(isLive());
-        return is<T>() ? static_cast<T*>(base()->ptr) : nullptr;
+        return this->is<T>() ? static_cast<T*>(base()->ptr) : nullptr;
     }
 
     // If this node refers to something that can be represented as a JavaScript
     // value that is safe to expose to JavaScript code, return that value.
     // Otherwise return UndefinedValue(). JSStrings, JS::Symbols, and some (but
     // not all!) JSObjects can be exposed.
     JS::Value exposeToJS() const;
 
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/Array-inl.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MathAlgorithms.h"
+#include "mozilla/TextUtils.h"
 
 #include <algorithm>
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
@@ -50,16 +51,17 @@
 using namespace js;
 using namespace js::gc;
 
 using mozilla::Abs;
 using mozilla::ArrayLength;
 using mozilla::CeilingLog2;
 using mozilla::CheckedInt;
 using mozilla::DebugOnly;
+using mozilla::IsAsciiDigit;
 
 using JS::AutoCheckCannotGC;
 using JS::IsArrayAnswer;
 using JS::ToUint32;
 
 bool
 JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer)
 {
@@ -212,28 +214,28 @@ GetLengthProperty(JSContext* cx, HandleO
  *
  */
 template <typename CharT>
 static bool
 StringIsArrayIndex(const CharT* s, uint32_t length, uint32_t* indexp)
 {
     const CharT* end = s + length;
 
-    if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s))
+    if (length == 0 || length > (sizeof("4294967294") - 1) || !IsAsciiDigit(*s))
         return false;
 
     uint32_t c = 0, previous = 0;
     uint32_t index = JS7_UNDEC(*s++);
 
     /* Don't allow leading zeros. */
     if (index == 0 && s != end)
         return false;
 
     for (; s < end; s++) {
-        if (!JS7_ISDEC(*s))
+        if (!IsAsciiDigit(*s))
             return false;
 
         previous = index;
         c = JS7_UNDEC(*s);
         index = 10 * index + c;
     }
 
     /* Make sure we didn't overflow. */
--- a/js/src/builtin/Array.h
+++ b/js/src/builtin/Array.h
@@ -4,16 +4,18 @@
  * 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/. */
 
 /* JS Array interface. */
 
 #ifndef builtin_Array_h
 #define builtin_Array_h
 
+#include "mozilla/TextUtils.h"
+
 #include "jspubtd.h"
 
 #include "vm/ArrayObject.h"
 #include "vm/JSObject.h"
 
 namespace js {
 /* 2^32-2, inclusive */
 const uint32_t MAX_ARRAY_INDEX = 4294967294u;
@@ -27,17 +29,17 @@ IdIsIndex(jsid id, uint32_t* indexp)
         *indexp = (uint32_t)i;
         return true;
     }
 
     if (MOZ_UNLIKELY(!JSID_IS_STRING(id)))
         return false;
 
     JSAtom* atom = JSID_TO_ATOM(id);
-    if (atom->length() == 0 || !JS7_ISDEC(atom->latin1OrTwoByteChar(0)))
+    if (atom->length() == 0 || !mozilla::IsAsciiDigit(atom->latin1OrTwoByteChar(0)))
         return false;
 
     return js::StringIsArrayIndex(atom, indexp);
 }
 
 // The methods below only create dense boxed arrays.
 
 /* Create a dense array with no capacity allocated, length set to 0. */
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -20,16 +20,17 @@
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 
 using mozilla::CheckedInt;
+using mozilla::IsAsciiDigit;
 
 /*
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
  * steps 3, 16-25.
  */
 bool
 js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
                             MutableHandleValue rval)
@@ -1216,28 +1217,28 @@ InterpretDollar(JSLinearString* matched,
     MOZ_ASSERT(*currentDollar == '$');
 
     /* If there is only a dollar, bail now. */
     if (currentDollar + 1 >= replacementEnd)
         return false;
 
     /* ES 2016 draft Mar 25, 2016 Table 46. */
     char16_t c = currentDollar[1];
-    if (JS7_ISDEC(c)) {
+    if (IsAsciiDigit(c)) {
         /* $n, $nn */
         unsigned num = JS7_UNDEC(c);
         if (num > captures.length()) {
             // The result is implementation-defined, do not substitute.
             return false;
         }
 
         const CharT* currentChar = currentDollar + 2;
         if (currentChar < replacementEnd) {
             c = *currentChar;
-            if (JS7_ISDEC(c)) {
+            if (IsAsciiDigit(c)) {
                 unsigned tmpNum = 10 * num + JS7_UNDEC(c);
                 // If num > captures.length(), the result is implementation-defined.
                 // Consume next character only if num <= captures.length().
                 if (tmpNum <= captures.length()) {
                     currentChar++;
                     num = tmpNum;
                 }
             }
--- a/js/src/builtin/intl/SharedIntlData.cpp
+++ b/js/src/builtin/intl/SharedIntlData.cpp
@@ -5,35 +5,38 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Runtime-wide Intl data shared across compartments. */
 
 #include "builtin/intl/SharedIntlData.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/HashFunctions.h"
+#include "mozilla/TextUtils.h"
 
 #include <stdint.h>
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/TimeZoneDataGenerated.h"
 #include "builtin/String.h"
 #include "js/Utility.h"
 #include "vm/JSAtom.h"
 
+using mozilla::IsAsciiLowercaseAlpha;
+
 using js::HashNumber;
 using js::intl::StringsAreEqual;
 
 template<typename Char>
 static constexpr Char
 ToUpperASCII(Char c)
 {
-    return ('a' <= c && c <= 'z')
+    return IsAsciiLowercaseAlpha(c)
            ? (c & ~0x20)
            : c;
 }
 
 static_assert(ToUpperASCII('a') == 'A', "verifying 'a' uppercases correctly");
 static_assert(ToUpperASCII('m') == 'M', "verifying 'm' uppercases correctly");
 static_assert(ToUpperASCII('z') == 'Z', "verifying 'z' uppercases correctly");
 static_assert(ToUpperASCII(u'a') == u'A', "verifying u'a' uppercases correctly");
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -4,33 +4,36 @@
  * 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 "ctypes/CTypes.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/TextUtils.h"
 #include "mozilla/Vector.h"
-
-#include <limits>
-#include <math.h>
-#include <stdint.h>
+#include "mozilla/WrappingOperations.h"
+
+#if defined(XP_UNIX)
+# include <errno.h>
+#endif
 #if defined(XP_WIN)
 # include <float.h>
 #endif
 #if defined(SOLARIS)
 # include <ieeefp.h>
 #endif
+#include <limits>
+#include <math.h>
+#include <stdint.h>
 #ifdef HAVE_SSIZE_T
 # include <sys/types.h>
 #endif
-#if defined(XP_UNIX)
-# include <errno.h>
-#endif
+#include <type_traits>
 
 #include "jsexn.h"
 #include "jsnum.h"
 
 #include "builtin/TypedObject.h"
 #include "ctypes/Library.h"
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
@@ -40,16 +43,19 @@
 #include "util/Windows.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace std;
 
+using mozilla::IsAsciiAlpha;
+using mozilla::IsAsciiDigit;
+
 using JS::AutoCheckCannotGC;
 
 namespace js {
 namespace ctypes {
 
 template <typename CharT>
 size_t
 GetDeflatedUTF8StringLength(JSContext* maybecx, const CharT* chars,
@@ -2570,64 +2576,68 @@ JS_STATIC_ASSERT(sizeof(int) == 4);
 JS_STATIC_ASSERT(sizeof(unsigned) == 4);
 JS_STATIC_ASSERT(sizeof(long) == 4 || sizeof(long) == 8);
 JS_STATIC_ASSERT(sizeof(long long) == 8);
 JS_STATIC_ASSERT(sizeof(size_t) == sizeof(uintptr_t));
 JS_STATIC_ASSERT(sizeof(float) == 4);
 JS_STATIC_ASSERT(sizeof(PRFuncPtr) == sizeof(void*));
 JS_STATIC_ASSERT(numeric_limits<double>::is_signed);
 
-// Templated helper to convert FromType to TargetType, for the default case
-// where the trivial POD constructor will do.
-template<class TargetType, class FromType>
-struct ConvertImpl {
-  static MOZ_ALWAYS_INLINE TargetType Convert(FromType d) {
-    return TargetType(d);
+template<typename TargetType,
+         typename FromType,
+         bool FromIsIntegral = std::is_integral<FromType>::value>
+struct ConvertImpl;
+
+template<typename TargetType, typename FromType>
+struct ConvertImpl<TargetType, FromType, false>
+{
+  static MOZ_ALWAYS_INLINE TargetType Convert(FromType input) {
+    return JS::ToSignedOrUnsignedInteger<TargetType>(input);
   }
 };
 
-#ifdef _MSC_VER
-// MSVC can't perform double to unsigned __int64 conversion when the
-// double is greater than 2^63 - 1. Help it along a little.
-template<>
-struct ConvertImpl<uint64_t, double> {
-  static MOZ_ALWAYS_INLINE uint64_t Convert(double d) {
-    return d > 0x7fffffffffffffffui64 ?
-           uint64_t(d - 0x8000000000000000ui64) + 0x8000000000000000ui64 :
-           uint64_t(d);
-  }
-};
-#endif
-
-// C++ doesn't guarantee that exact values are the only ones that will
-// round-trip. In fact, on some platforms, including SPARC, there are pairs of
-// values, a uint64_t and a double, such that neither value is exactly
-// representable in the other type, but they cast to each other.
-#if defined(SPARC) || defined(__powerpc__)
-// Simulate x86 overflow behavior
-template<>
-struct ConvertImpl<uint64_t, double> {
-  static MOZ_ALWAYS_INLINE uint64_t Convert(double d) {
-    return d >= 0xffffffffffffffff ?
-           0x8000000000000000 : uint64_t(d);
+template<typename TargetType>
+struct ConvertUnsignedTargetTo
+{
+  static TargetType
+  convert(typename std::make_unsigned<TargetType>::type input)
+  {
+    return std::is_signed<TargetType>::value ? mozilla::WrapToSigned(input) : input;
   }
 };
 
 template<>
-struct ConvertImpl<int64_t, double> {
-  static MOZ_ALWAYS_INLINE int64_t Convert(double d) {
-    return d >= 0x7fffffffffffffff ?
-           0x8000000000000000 : int64_t(d);
+struct ConvertUnsignedTargetTo<char16_t>
+{
+  static char16_t
+  convert(char16_t input)
+  {
+    // mozilla::WrapToSigned can't be used on char16_t.
+    return input;
   }
 };
-#endif
+
+template<typename TargetType, typename FromType>
+struct ConvertImpl<TargetType, FromType, true>
+{
+  static MOZ_ALWAYS_INLINE TargetType Convert(FromType input) {
+    using UnsignedTargetType = typename std::make_unsigned<TargetType>::type;
+    auto resultUnsigned = static_cast<UnsignedTargetType>(input);
+
+    return ConvertUnsignedTargetTo<TargetType>::convert(resultUnsigned);
+  }
+};
 
 template<class TargetType, class FromType>
 static MOZ_ALWAYS_INLINE TargetType Convert(FromType d)
 {
+  static_assert(std::is_integral<FromType>::value !=
+                std::is_floating_point<FromType>::value,
+                "should only be converting from floating/integral type");
+
   return ConvertImpl<TargetType, FromType>::Convert(d);
 }
 
 template<class TargetType, class FromType>
 static MOZ_ALWAYS_INLINE bool IsAlwaysExact()
 {
   // Return 'true' if TargetType can always exactly represent FromType.
   // This means that:
@@ -2680,18 +2690,18 @@ struct IsExactImpl<TargetType, FromType,
   }
 };
 
 // Convert FromType 'i' to TargetType 'result', returning true iff 'result'
 // is an exact representation of 'i'.
 template<class TargetType, class FromType>
 static MOZ_ALWAYS_INLINE bool ConvertExact(FromType i, TargetType* result)
 {
-  // Require that TargetType is integral, to simplify conversion.
-  JS_STATIC_ASSERT(numeric_limits<TargetType>::is_exact);
+  static_assert(std::numeric_limits<TargetType>::is_exact,
+                "TargetType must be exact to simplify conversion");
 
   *result = Convert<TargetType>(i);
 
   // See if we can avoid a dynamic check.
   if (IsAlwaysExact<TargetType, FromType>())
     return true;
 
   // Return 'true' if 'i' is exactly representable in 'TargetType'.
@@ -2926,27 +2936,28 @@ StringToInteger(JSContext* cx, CharT* cp
     base = 16;
   }
 
   // Scan the string left to right and build the number,
   // checking for valid characters 0 - 9, a - f, A - F and overflow.
   IntegerType i = 0;
   while (cp != end) {
     char16_t c = *cp++;
-    if (c >= '0' && c <= '9')
-      c -= '0';
+    uint8_t digit;
+    if (IsAsciiDigit(c))
+      digit = c - '0';
     else if (base == 16 && c >= 'a' && c <= 'f')
-      c = c - 'a' + 10;
+      digit = c - 'a' + 10;
     else if (base == 16 && c >= 'A' && c <= 'F')
-      c = c - 'A' + 10;
+      digit = c - 'A' + 10;
     else
       return false;
 
     IntegerType ii = i;
-    i = ii * base + sign * c;
+    i = ii * base + sign * digit;
     if (i / base != ii) {
       *overflow = true;
       return false;
     }
   }
 
   *result = i;
   return true;
@@ -3100,19 +3111,21 @@ SizeTojsval(JSContext* cx, size_t size, 
 // Forcefully convert val to IntegerType when explicitly requested.
 template<class IntegerType>
 static bool
 jsvalToIntegerExplicit(HandleValue val, IntegerType* result)
 {
   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
 
   if (val.isDouble()) {
-    // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
+    // Convert using ToInt32-style semantics: non-finite numbers become 0, and
+    // everything else rounds toward zero then maps into |IntegerType| with
+    // wraparound semantics.
     double d = val.toDouble();
-    *result = mozilla::IsFinite(d) ? IntegerType(d) : 0;
+    *result = JS::ToSignedOrUnsignedInteger<IntegerType>(d);
     return true;
   }
   if (val.isObject()) {
     // Convert Int64 and UInt64 values by C-style cast.
     JSObject* obj = &val.toObject();
     if (Int64::IsInt64(obj)) {
       int64_t i = Int64Base::GetInt(obj);
       *result = IntegerType(i);
@@ -4050,19 +4063,17 @@ BuildTypeName(JSContext* cx, JSObject* t
       // Either a basic or struct type. Use the type's name as the base type.
       break;
     }
     break;
   }
 
   // If prepending the base type name directly would splice two
   // identifiers, insert a space.
-  if (('a' <= result[0] && result[0] <= 'z') ||
-      ('A' <= result[0] && result[0] <= 'Z') ||
-      (result[0] == '_'))
+  if (IsAsciiAlpha(result[0]) || result[0] == '_')
     PrependString(result, " ");
 
   // Stick the base type and derived type parts together.
   JSString* baseName = CType::GetName(cx, typeObj);
   PrependString(result, baseName);
   return NewUCString(cx, result);
 }
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -31,16 +31,17 @@
 #include "util/Unicode.h"
 #include "vm/HelperThreads.h"
 #include "vm/JSAtom.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
 
 using mozilla::ArrayLength;
 using mozilla::IsAsciiAlpha;
+using mozilla::IsAsciiDigit;
 using mozilla::MakeScopeExit;
 using mozilla::PodArrayZero;
 using mozilla::PodCopy;
 
 struct ReservedWordInfo
 {
     const char* chars;         // C string with reserved word text
     js::frontend::TokenKind tokentype;
@@ -1594,39 +1595,39 @@ TokenStreamSpecific<CharT, AnyCharsAcces
     //
     if (c1kind == Dec) {
         tp = newToken(-1);
         numStart = userbuf.addressOfNextRawChar() - 1;
 
       decimal:
         decimalPoint = NoDecimal;
         hasExp = false;
-        while (JS7_ISDEC(c))
+        while (IsAsciiDigit(c))
             c = getCharIgnoreEOL();
 
         if (c == '.') {
             decimalPoint = HasDecimal;
           decimal_dot:
             do {
                 c = getCharIgnoreEOL();
-            } while (JS7_ISDEC(c));
+            } while (IsAsciiDigit(c));
         }
         if (c == 'e' || c == 'E') {
             hasExp = true;
             c = getCharIgnoreEOL();
             if (c == '+' || c == '-')
                 c = getCharIgnoreEOL();
-            if (!JS7_ISDEC(c)) {
+            if (!IsAsciiDigit(c)) {
                 ungetCharIgnoreEOL(c);
                 reportError(JSMSG_MISSING_EXPONENT);
                 goto error;
             }
             do {
                 c = getCharIgnoreEOL();
-            } while (JS7_ISDEC(c));
+            } while (IsAsciiDigit(c));
         }
         ungetCharIgnoreEOL(c);
 
         if (c != EOF) {
             if (unicode::IsIdentifierStart(char16_t(c))) {
                 reportError(JSMSG_IDSTART_AFTER_NUMBER);
                 goto error;
             }
@@ -1717,20 +1718,20 @@ TokenStreamSpecific<CharT, AnyCharsAcces
             if (c < '0' || c > '7') {
                 ungetCharIgnoreEOL(c);
                 reportError(JSMSG_MISSING_OCTAL_DIGITS);
                 goto error;
             }
             numStart = userbuf.addressOfNextRawChar() - 1;  // one past the '0o'
             while ('0' <= c && c <= '7')
                 c = getCharIgnoreEOL();
-        } else if (JS7_ISDEC(c)) {
+        } else if (IsAsciiDigit(c)) {
             radix = 8;
             numStart = userbuf.addressOfNextRawChar() - 1;  // one past the '0'
-            while (JS7_ISDEC(c)) {
+            while (IsAsciiDigit(c)) {
                 // Octal integer literals are not permitted in strict mode code.
                 if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
                     goto error;
 
                 // Outside strict mode, we permit 08 and 09 as decimal numbers,
                 // which makes our behaviour a superset of the ECMA numeric
                 // grammar. We might not always be so permissive, so we warn
                 // about it.
@@ -1780,17 +1781,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
 
     // This handles everything else.
     //
     MOZ_ASSERT(c1kind == Other);
     tp = newToken(-1);
     switch (c) {
       case '.':
         c = getCharIgnoreEOL();
-        if (JS7_ISDEC(c)) {
+        if (IsAsciiDigit(c)) {
             numStart = userbuf.addressOfNextRawChar() - 2;
             decimalPoint = HasDecimal;
             hasExp = false;
             goto decimal_dot;
         }
         if (c == '.') {
             if (matchChar('.')) {
                 tp->type = TokenKind::TripleDot;
@@ -2261,17 +2262,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
                 // Octal character specification.
                 if (JS7_ISOCT(c)) {
                     int32_t val = JS7_UNOCT(c);
 
                     if (!peekChar(&c))
                         return false;
 
                     // Strict mode code allows only \0, then a non-digit.
-                    if (val != 0 || JS7_ISDEC(c)) {
+                    if (val != 0 || IsAsciiDigit(c)) {
                         TokenStreamAnyChars& anyChars = anyCharsAccess();
                         if (parsingTemplate) {
                             anyChars.setInvalidTemplateEscape(userbuf.offset() - 2,
                                                               InvalidEscapeType::Octal);
                             continue;
                         }
                         if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
                             return false;
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -94,17 +94,17 @@ UNIFIED_SOURCES += [
     'testSourcePolicy.cpp',
     'testStringBuffer.cpp',
     'testStructuredClone.cpp',
     'testSymbol.cpp',
     'testThreadingConditionVariable.cpp',
     'testThreadingExclusiveData.cpp',
     'testThreadingMutex.cpp',
     'testThreadingThread.cpp',
-    'testToIntWidth.cpp',
+    'testToSignedOrUnsignedInteger.cpp',
     'testTypedArrays.cpp',
     'testUbiNode.cpp',
     'testUncaughtSymbol.cpp',
     'testUTF8.cpp',
     'testWasmLEB128.cpp',
     'testWeakMap.cpp',
     'testXDR.cpp',
 ]
rename from js/src/jsapi-tests/testToIntWidth.cpp
rename to js/src/jsapi-tests/testToSignedOrUnsignedInteger.cpp
--- a/js/src/jsapi-tests/testToIntWidth.cpp
+++ b/js/src/jsapi-tests/testToSignedOrUnsignedInteger.cpp
@@ -6,64 +6,64 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <math.h>
 
 #include "js/Conversions.h"
 
 #include "jsapi-tests/tests.h"
 
-using JS::detail::ToIntWidth;
-using JS::detail::ToUintWidth;
+using JS::ToSignedInteger;
+using JS::ToUnsignedInteger;
 
 BEGIN_TEST(testToUint8TwiceUint8Range)
 {
     double d = -256;
     uint8_t expected = 0;
     do {
-        CHECK(ToUintWidth<uint8_t>(d) == expected);
+        CHECK(ToUnsignedInteger<uint8_t>(d) == expected);
 
         d++;
         expected++;
     } while (d <= 256);
     return true;
 }
 END_TEST(testToUint8TwiceUint8Range)
 
 BEGIN_TEST(testToInt8)
 {
     double d = -128;
     int8_t expected = -128;
     do {
-        CHECK(ToIntWidth<int8_t>(d) == expected);
+        CHECK(ToSignedInteger<int8_t>(d) == expected);
 
         d++;
         expected++;
     } while (expected < 127);
     return true;
 }
 END_TEST(testToInt8)
 
 BEGIN_TEST(testToUint32Large)
 {
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 83)) == 0);
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 83) + pow(2.0, 31)) == (1U << 31));
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 83) + 2 * pow(2.0, 31)) == 0);
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 83) + 3 * pow(2.0, 31)) == (1U << 31));
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 84)) == 0);
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 84) + pow(2.0, 31)) == 0);
-    CHECK(ToUintWidth<uint32_t>(pow(2.0, 84) + pow(2.0, 32)) == 0);
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 83)) == 0);
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 83) + pow(2.0, 31)) == (1U << 31));
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 83) + 2 * pow(2.0, 31)) == 0);
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 83) + 3 * pow(2.0, 31)) == (1U << 31));
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 84)) == 0);
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 84) + pow(2.0, 31)) == 0);
+    CHECK(ToUnsignedInteger<uint32_t>(pow(2.0, 84) + pow(2.0, 32)) == 0);
     return true;
 }
 END_TEST(testToUint32Large)
 
 BEGIN_TEST(testToUint64Large)
 {
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 115)) == 0);
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 115) + pow(2.0, 63)) == (1ULL << 63));
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 115) + 2 * pow(2.0, 63)) == 0);
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 115) + 3 * pow(2.0, 63)) == (1ULL << 63));
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 116)) == 0);
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 116) + pow(2.0, 63)) == 0);
-    CHECK(ToUintWidth<uint64_t>(pow(2.0, 116) + pow(2.0, 64)) == 0);
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 115)) == 0);
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 115) + pow(2.0, 63)) == (1ULL << 63));
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 115) + 2 * pow(2.0, 63)) == 0);
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 115) + 3 * pow(2.0, 63)) == (1ULL << 63));
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 116)) == 0);
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 116) + pow(2.0, 63)) == 0);
+    CHECK(ToUnsignedInteger<uint64_t>(pow(2.0, 116) + pow(2.0, 64)) == 0);
     return true;
 }
 END_TEST(testToUint64Large)
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -16,16 +16,17 @@
  */
 
 #include "jsdate.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/TextUtils.h"
 
 #include <ctype.h>
 #include <math.h>
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsnum.h"
 #include "jstypes.h"
@@ -45,16 +46,17 @@
 #include "vm/Time.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using mozilla::Atomic;
 using mozilla::ArrayLength;
+using mozilla::IsAsciiAlpha;
 using mozilla::IsFinite;
 using mozilla::IsNaN;
 using mozilla::NumbersAreIdentical;
 using mozilla::Relaxed;
 
 using JS::AutoCheckCannotGC;
 using JS::ClippedTime;
 using JS::GenericNaN;
@@ -1117,17 +1119,17 @@ ParseDate(const CharT* s, size_t length,
             }
             prevc = 0;
         } else if (c == '/' || c == ':' || c == '+' || c == '-') {
             prevc = c;
         } else {
             size_t st = i - 1;
             while (i < length) {
                 c = s[i];
-                if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
+                if (!IsAsciiAlpha(c))
                     break;
                 i++;
             }
 
             if (i <= st + 1)
                 return false;
 
             int k;
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -9,16 +9,17 @@
  */
 
 #include "jsnum.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/RangedPtr.h"
+#include "mozilla/TextUtils.h"
 
 #ifdef HAVE_LOCALECONV
 #include <locale.h>
 #endif
 #include <math.h>
 #include <string.h>
 
 #include "jstypes.h"
@@ -36,16 +37,18 @@
 #include "vm/NativeObject-inl.h"
 #include "vm/NumberObject-inl.h"
 #include "vm/StringType-inl.h"
 
 using namespace js;
 
 using mozilla::Abs;
 using mozilla::ArrayLength;
+using mozilla::AsciiAlphanumericToNumber;
+using mozilla::IsAsciiAlphanumeric;
 using mozilla::Maybe;
 using mozilla::MinNumberValue;
 using mozilla::NegativeInfinity;
 using mozilla::PositiveInfinity;
 using mozilla::RangedPtr;
 
 using JS::AutoCheckCannotGC;
 using JS::GenericNaN;
@@ -79,17 +82,17 @@ ComputeAccurateDecimalInteger(JSContext*
 {
     size_t length = end - start;
     ScopedJSFreePtr<char> cstr(cx->pod_malloc<char>(length + 1));
     if (!cstr)
         return false;
 
     for (size_t i = 0; i < length; i++) {
         char c = char(start[i]);
-        MOZ_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
+        MOZ_ASSERT(IsAsciiAlphanumeric(c));
         cstr[i] = c;
     }
     cstr[length] = 0;
 
     if (!EnsureDtoaState(cx))
         return false;
 
     char* estr;
@@ -117,23 +120,18 @@ class BinaryDigitReader
 
     /* Return the next binary digit from the number, or -1 if done. */
     int nextDigit() {
         if (digitMask == 0) {
             if (start == end)
                 return -1;
 
             int c = *start++;
-            MOZ_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
-            if ('0' <= c && c <= '9')
-                digit = c - '0';
-            else if ('a' <= c && c <= 'z')
-                digit = c - 'a' + 10;
-            else
-                digit = c - 'A' + 10;
+            MOZ_ASSERT(IsAsciiAlphanumeric(c));
+            digit = AsciiAlphanumericToNumber(c);
             digitMask = base >> 1;
         }
 
         int bit = (digit & digitMask) != 0;
         digitMask >>= 1;
         return bit;
     }
 };
@@ -221,28 +219,24 @@ js::GetPrefixInteger(JSContext* cx, cons
                      const CharT** endp, double* dp)
 {
     MOZ_ASSERT(start <= end);
     MOZ_ASSERT(2 <= base && base <= 36);
 
     const CharT* s = start;
     double d = 0.0;
     for (; s < end; s++) {
-        int digit;
         CharT c = *s;
-        if ('0' <= c && c <= '9')
-            digit = c - '0';
-        else if ('a' <= c && c <= 'z')
-            digit = c - 'a' + 10;
-        else if ('A' <= c && c <= 'Z')
-            digit = c - 'A' + 10;
-        else
+        if (!IsAsciiAlphanumeric(c))
             break;
+
+        uint8_t digit = AsciiAlphanumericToNumber(c);
         if (digit >= base)
             break;
+
         d = d * base + digit;
     }
 
     *endp = s;
     *dp = d;
 
     /* If we haven't reached the limit of integer precision, we're done. */
     if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT)
--- a/js/src/util/Text.h
+++ b/js/src/util/Text.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef util_Text_h
 #define util_Text_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/TextUtils.h"
 
 #include <ctype.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string>
 
 #include "jsutil.h"
@@ -24,23 +25,22 @@
 #include "vm/Printer.h"
 
 class JSLinearString;
 
 /*
  * Shorthands for ASCII (7-bit) decimal and hex conversion.
  * Manually inline isdigit and isxdigit for performance; MSVC doesn't do this for us.
  */
-#define JS7_ISDEC(c)    ((((unsigned)(c)) - '0') <= 9)
 #define JS7_ISA2F(c)    ((((((unsigned)(c)) - 'a') <= 5) || (((unsigned)(c)) - 'A') <= 5))
 #define JS7_UNDEC(c)    ((c) - '0')
 #define JS7_ISOCT(c)    ((((unsigned)(c)) - '0') <= 7)
 #define JS7_UNOCT(c)    (JS7_UNDEC(c))
-#define JS7_ISHEX(c)    ((c) < 128 && (JS7_ISDEC(c) || JS7_ISA2F(c)))
-#define JS7_UNHEX(c)    (unsigned)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a')
+#define JS7_ISHEX(c)    ((c) < 128 && (mozilla::IsAsciiDigit(c) || JS7_ISA2F(c)))
+#define JS7_UNHEX(c)    (unsigned)(mozilla::IsAsciiDigit(c) ? (c) - '0' : 10 + tolower(c) - 'a')
 
 static MOZ_ALWAYS_INLINE size_t
 js_strlen(const char16_t* s)
 {
     return std::char_traits<char16_t>::length(s);
 }
 
 template <typename CharT>
--- a/js/src/vm/JSONParser.cpp
+++ b/js/src/vm/JSONParser.cpp
@@ -4,29 +4,31 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/JSONParser.h"
 
 #include "mozilla/Range.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/TextUtils.h"
 
 #include <ctype.h>
 
 #include "jsnum.h"
 
 #include "builtin/Array.h"
 #include "util/StringBuffer.h"
 #include "vm/JSCompartment.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
+using mozilla::IsAsciiDigit;
 using mozilla::RangedPtr;
 
 JSONParserBase::~JSONParserBase()
 {
     for (size_t i = 0; i < stack.length(); i++) {
         if (stack[i].state == FinishArrayElement)
             js_delete(&stack[i].elements());
         else
@@ -240,17 +242,17 @@ JSONParser<CharT>::readString()
     return token(Error);
 }
 
 template <typename CharT>
 JSONParserBase::Token
 JSONParser<CharT>::readNumber()
 {
     MOZ_ASSERT(current < end);
-    MOZ_ASSERT(JS7_ISDEC(*current) || *current == '-');
+    MOZ_ASSERT(IsAsciiDigit(*current) || *current == '-');
 
     /*
      * JSONNumber:
      *   /^-?(0|[1-9][0-9]+)(\.[0-9]+)?([eE][\+\-]?[0-9]+)?$/
      */
 
     bool negative = *current == '-';
 
@@ -258,23 +260,23 @@ JSONParser<CharT>::readNumber()
     if (negative && ++current == end) {
         error("no number after minus sign");
         return token(Error);
     }
 
     const CharPtr digitStart = current;
 
     /* 0|[1-9][0-9]+ */
-    if (!JS7_ISDEC(*current)) {
+    if (!IsAsciiDigit(*current)) {
         error("unexpected non-digit");
         return token(Error);
     }
     if (*current++ != '0') {
         for (; current < end; current++) {
-            if (!JS7_ISDEC(*current))
+            if (!IsAsciiDigit(*current))
                 break;
         }
     }
 
     /* Fast path: no fractional or exponent part. */
     if (current == end || (*current != '.' && *current != 'e' && *current != 'E')) {
         mozilla::Range<const CharT> chars(digitStart.get(), current - digitStart);
         if (chars.length() < strlen("9007199254740992")) {
@@ -295,44 +297,44 @@ JSONParser<CharT>::readNumber()
     }
 
     /* (\.[0-9]+)? */
     if (current < end && *current == '.') {
         if (++current == end) {
             error("missing digits after decimal point");
             return token(Error);
         }
-        if (!JS7_ISDEC(*current)) {
+        if (!IsAsciiDigit(*current)) {
             error("unterminated fractional number");
             return token(Error);
         }
         while (++current < end) {
-            if (!JS7_ISDEC(*current))
+            if (!IsAsciiDigit(*current))
                 break;
         }
     }
 
     /* ([eE][\+\-]?[0-9]+)? */
     if (current < end && (*current == 'e' || *current == 'E')) {
         if (++current == end) {
             error("missing digits after exponent indicator");
             return token(Error);
         }
         if (*current == '+' || *current == '-') {
             if (++current == end) {
                 error("missing digits after exponent sign");
                 return token(Error);
             }
         }
-        if (!JS7_ISDEC(*current)) {
+        if (!IsAsciiDigit(*current)) {
             error("exponent part is missing a number");
             return token(Error);
         }
         while (++current < end) {
-            if (!JS7_ISDEC(*current))
+            if (!IsAsciiDigit(*current))
                 break;
         }
     }
 
     double d;
     const CharT* finish;
     if (!js_strtod(cx, digitStart.get(), current.get(), &finish, &d))
         return token(OOM);
@@ -473,17 +475,17 @@ AssertPastValue(const RangedPtr<const Ch
                (current[-1] == 'e' &&
                 current[-2] == 's' &&
                 current[-3] == 'l' &&
                 current[-4] == 'a' &&
                 current[-5] == 'f') ||
                current[-1] == '}' ||
                current[-1] == ']' ||
                current[-1] == '"' ||
-               JS7_ISDEC(current[-1]));
+               IsAsciiDigit(current[-1]));
 }
 
 template <typename CharT>
 JSONParserBase::Token
 JSONParser<CharT>::advanceAfterArrayElement()
 {
     AssertPastValue(current);
 
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -6,32 +6,34 @@
 
 #include "vm/StringType-inl.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/RangedPtr.h"
+#include "mozilla/TextUtils.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 
 #include "gc/Marking.h"
 #include "gc/Nursery.h"
 #include "js/UbiNode.h"
 #include "util/StringBuffer.h"
 #include "vm/GeckoProfiler.h"
 
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
+using mozilla::IsAsciiDigit;
 using mozilla::IsNegativeZero;
 using mozilla::IsSame;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::RangedPtr;
 using mozilla::RoundUpPow2;
 using mozilla::Unused;
 
@@ -920,17 +922,17 @@ js::StringEqualsAscii(JSLinearString* st
 }
 
 template <typename CharT>
 /* static */ bool
 JSFlatString::isIndexSlow(const CharT* s, size_t length, uint32_t* indexp)
 {
     CharT ch = *s;
 
-    if (!JS7_ISDEC(ch))
+    if (!IsAsciiDigit(ch))
         return false;
 
     if (length > UINT32_CHAR_BUFFER_LENGTH)
         return false;
 
     /*
      * Make sure to account for the '\0' at the end of characters, dereferenced
      * in the loop below.
@@ -938,17 +940,17 @@ JSFlatString::isIndexSlow(const CharT* s
     RangedPtr<const CharT> cp(s, length + 1);
     const RangedPtr<const CharT> end(s + length, s, length + 1);
 
     uint32_t index = JS7_UNDEC(*cp++);
     uint32_t oldIndex = 0;
     uint32_t c = 0;
 
     if (index != 0) {
-        while (JS7_ISDEC(*cp)) {
+        while (IsAsciiDigit(*cp)) {
             oldIndex = index;
             c = JS7_UNDEC(*cp);
             index = 10 * index + c;
             cp++;
         }
     }
 
     /* It's not an element if there are characters after the number. */
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_StringType_h
 #define vm_StringType_h
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/Range.h"
+#include "mozilla/TextUtils.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "builtin/String.h"
 #include "gc/Barrier.h"
 #include "gc/Cell.h"
 #include "gc/Heap.h"
@@ -885,20 +886,20 @@ class JSFlatString : public JSLinearStri
     static inline JSFlatString* new_(JSContext* cx,
                                      const CharT* chars, size_t length);
 
     inline bool isIndexSlow(uint32_t* indexp) const {
         MOZ_ASSERT(JSString::isFlat());
         JS::AutoCheckCannotGC nogc;
         if (hasLatin1Chars()) {
             const JS::Latin1Char* s = latin1Chars(nogc);
-            return JS7_ISDEC(*s) && isIndexSlow(s, length(), indexp);
+            return mozilla::IsAsciiDigit(*s) && isIndexSlow(s, length(), indexp);
         }
         const char16_t* s = twoByteChars(nogc);
-        return JS7_ISDEC(*s) && isIndexSlow(s, length(), indexp);
+        return mozilla::IsAsciiDigit(*s) && isIndexSlow(s, length(), indexp);
     }
 
     /*
      * Returns true if this string's characters store an unsigned 32-bit
      * integer value, initializing *indexp to that value if so.  (Thus if
      * calling isIndex returns true, js::IndexToString(cx, *indexp) will be a
      * string equal to this string.)
      */
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -6,16 +6,17 @@
 
 #include "vm/TypedArrayObject-inl.h"
 #include "vm/TypedArrayObject.h"
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Casting.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/TextUtils.h"
 
 #include <string.h>
 #ifndef XP_WIN
 # include <sys/mman.h>
 #endif
 
 #include "jsapi.h"
 #include "jsnum.h"
@@ -47,16 +48,17 @@
 #include "vm/JSAtom-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::AssertedCast;
+using mozilla::IsAsciiDigit;
 using JS::CanonicalizeNaN;
 using JS::ToInt32;
 using JS::ToUint32;
 
 /*
  * TypedArrayObject
  *
  * The non-templated base class for the specific typed implementations.
@@ -2168,30 +2170,30 @@ js::StringIsTypedArrayIndex(const CharT*
 
     bool negative = false;
     if (*s == '-') {
         negative = true;
         if (++s == end)
             return false;
     }
 
-    if (!JS7_ISDEC(*s))
+    if (!IsAsciiDigit(*s))
         return false;
 
     uint64_t index = 0;
     uint32_t digit = JS7_UNDEC(*s++);
 
     /* Don't allow leading zeros. */
     if (digit == 0 && s != end)
         return false;
 
     index = digit;
 
     for (; s < end; s++) {
-        if (!JS7_ISDEC(*s))
+        if (!IsAsciiDigit(*s))
             return false;
 
         digit = JS7_UNDEC(*s);
 
         /* Watch for overflows. */
         if ((UINT64_MAX - digit) / 10 < index)
             index = UINT64_MAX;
         else
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_TypedArrayObject_h
 #define vm_TypedArrayObject_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/TextUtils.h"
 
 #include "gc/Barrier.h"
 #include "js/Class.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/JSObject.h"
 #include "vm/SharedArrayObject.h"
 
 #define JS_FOR_EACH_TYPED_ARRAY(macro) \
@@ -358,23 +359,23 @@ IsTypedArrayIndex(jsid id, uint64_t* ind
         return false;
 
     JS::AutoCheckCannotGC nogc;
     JSAtom* atom = JSID_TO_ATOM(id);
     size_t length = atom->length();
 
     if (atom->hasLatin1Chars()) {
         const Latin1Char* s = atom->latin1Chars(nogc);
-        if (!JS7_ISDEC(*s) && *s != '-')
+        if (!mozilla::IsAsciiDigit(*s) && *s != '-')
             return false;
         return StringIsTypedArrayIndex(s, length, indexp);
     }
 
     const char16_t* s = atom->twoByteChars(nogc);
-    if (!JS7_ISDEC(*s) && *s != '-')
+    if (!mozilla::IsAsciiDigit(*s) && *s != '-')
         return false;
     return StringIsTypedArrayIndex(s, length, indexp);
 }
 
 /*
  * Implements [[DefineOwnProperty]] for TypedArrays when the property
  * key is a TypedArray index.
  */
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -3051,16 +3051,17 @@ static void
 VerifyFlatTree(const nsIContent& aContent)
 {
   StyleChildrenIterator iter(&aContent);
 
   for (auto* content = iter.GetNextChild();
        content;
        content = iter.GetNextChild()) {
     MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle() == &aContent);
+    MOZ_ASSERT(!content->IsActiveChildrenElement());
     VerifyFlatTree(*content);
   }
 }
 #endif
 
 void
 RestyleManager::ProcessPendingRestyles()
 {
--- a/layout/mathml/nsMathMLmpaddedFrame.cpp
+++ b/layout/mathml/nsMathMLmpaddedFrame.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "nsMathMLmpaddedFrame.h"
 #include "nsMathMLElement.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/TextUtils.h"
 #include <algorithm>
 
 //
 // <mpadded> -- adjust space around content - implementation
 //
 
 #define NS_MATHML_SIGN_INVALID           -1 // if the attribute is not there
 #define NS_MATHML_SIGN_UNSPECIFIED        0
@@ -155,17 +156,17 @@ nsMathMLmpaddedFrame::ParseAttribute(nsS
     if (gotDot && c == '.') {
       // error - two dots encountered
       aSign = NS_MATHML_SIGN_INVALID;
       return false;
     }
 
     if (c == '.')
       gotDot = true;
-    else if (!nsCRT::IsAsciiDigit(c)) {
+    else if (!IsAsciiDigit(c)) {
       break;
     }
     number.Append(c);
   }
 
   // catch error if we didn't enter the loop above... we could simply initialize
   // floatValue = 1, to cater for cases such as width="height", but that wouldn't
   // be in line with the spec which requires an explicit number
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1453206.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<div id="host"></div>
+<script>
+document.addEventListener('DOMContentLoaded', () => {
+  let shadow = host.attachShadow({ mode: 'open' });
+  shadow.appendChild(document.createElement('marquee'));
+});
+</script>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -266,8 +266,9 @@ pref(dom.webcomponents.shadowdom.enabled
 load 1418059.html
 test-pref(dom.animations-api.core.enabled,true) load 1418867.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1419554.html
 load 1426312.html
 load 1439793.html
 load 1409183.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1445682.html
 load 1450691.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1453206.html
--- a/mfbt/TextUtils.h
+++ b/mfbt/TextUtils.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Character/text operations. */
 
 #ifndef mozilla_TextUtils_h
 #define mozilla_TextUtils_h
 
+#include "mozilla/Assertions.h"
 #include "mozilla/TypeTraits.h"
 
 namespace mozilla {
 
 namespace detail {
 
 template<typename Char>
 class MakeUnsignedChar
@@ -32,25 +33,109 @@ class MakeUnsignedChar<char32_t>
 {
 public:
   using Type = char32_t;
 };
 
 } // namespace detail
 
 /**
+ * Returns true iff |aChar| matches [a-z].
+ *
+ * This function is basically what you thought islower was, except its behavior
+ * doesn't depend on the user's current locale.
+ */
+template<typename Char>
+constexpr bool
+IsAsciiLowercaseAlpha(Char aChar)
+{
+  using UnsignedChar = typename detail::MakeUnsignedChar<Char>::Type;
+  auto uc = static_cast<UnsignedChar>(aChar);
+  return 'a' <= uc && uc <= 'z';
+}
+
+/**
+ * Returns true iff |aChar| matches [A-Z].
+ *
+ * This function is basically what you thought isupper was, except its behavior
+ * doesn't depend on the user's current locale.
+ */
+template<typename Char>
+constexpr bool
+IsAsciiUppercaseAlpha(Char aChar)
+{
+  using UnsignedChar = typename detail::MakeUnsignedChar<Char>::Type;
+  auto uc = static_cast<UnsignedChar>(aChar);
+  return 'A' <= uc && uc <= 'Z';
+}
+
+/**
  * Returns true iff |aChar| matches [a-zA-Z].
  *
  * This function is basically what you thought isalpha was, except its behavior
  * doesn't depend on the user's current locale.
  */
 template<typename Char>
 constexpr bool
 IsAsciiAlpha(Char aChar)
 {
+  return IsAsciiLowercaseAlpha(aChar) || IsAsciiUppercaseAlpha(aChar);
+}
+
+/**
+ * Returns true iff |aChar| matches [0-9].
+ *
+ * This function is basically what you thought isdigit was, except its behavior
+ * doesn't depend on the user's current locale.
+ */
+template<typename Char>
+constexpr bool
+IsAsciiDigit(Char aChar)
+{
   using UnsignedChar = typename detail::MakeUnsignedChar<Char>::Type;
   auto uc = static_cast<UnsignedChar>(aChar);
-  return ('a' <= uc && uc <= 'z') || ('A' <= uc && uc <= 'Z');
+  return '0' <= uc && uc <= '9';
+}
+
+/**
+ * Returns true iff |aChar| matches [a-zA-Z0-9].
+ *
+ * This function is basically what you thought isalnum was, except its behavior
+ * doesn't depend on the user's current locale.
+ */
+template<typename Char>
+constexpr bool
+IsAsciiAlphanumeric(Char aChar)
+{
+  return IsAsciiDigit(aChar) || IsAsciiAlpha(aChar);
+}
+
+/**
+ * Converts an ASCII alphanumeric digit [0-9a-zA-Z] to number as if in base-36.
+ * (This function therefore works for decimal, hexadecimal, etc.).
+ */
+template<typename Char>
+uint8_t
+AsciiAlphanumericToNumber(Char aChar)
+{
+  using UnsignedChar = typename detail::MakeUnsignedChar<Char>::Type;
+  auto uc = static_cast<UnsignedChar>(aChar);
+
+  if ('0' <= uc && uc <= '9') {
+    return uc - '0';
+  }
+
+  if ('A' <= uc && uc <= 'Z') {
+    return uc - 'A' + 10;
+  }
+
+  // Ideally this function would be constexpr, but unfortunately gcc at least as
+  // of 6.4 forbids non-constexpr function calls in unevaluated constexpr
+  // function calls.  See bug 1453456.  So for now, just assert and leave the
+  // entire function non-constexpr.
+  MOZ_ASSERT('a' <= uc && uc <= 'z',
+             "non-ASCII alphanumeric character can't be converted to number");
+  return uc - 'a' + 10;
 }
 
 } // namespace mozilla
 
 #endif /* mozilla_TextUtils_h */
--- a/mfbt/tests/TestTextUtils.cpp
+++ b/mfbt/tests/TestTextUtils.cpp
@@ -2,105 +2,717 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/TextUtils.h"
 
+using mozilla::AsciiAlphanumericToNumber;
 using mozilla::IsAsciiAlpha;
+using mozilla::IsAsciiAlphanumeric;
+using mozilla::IsAsciiDigit;
+using mozilla::IsAsciiLowercaseAlpha;
+using mozilla::IsAsciiUppercaseAlpha;
+
+static void
+TestIsAsciiAlpha()
+{
+  // char
+
+  static_assert(!IsAsciiAlpha('@'), "'@' isn't ASCII alpha");
+  static_assert('@' == 0x40, "'@' has value 0x40");
+
+  static_assert('A' == 0x41, "'A' has value 0x41");
+  static_assert(IsAsciiAlpha('A'), "'A' is ASCII alpha");
+  static_assert(IsAsciiAlpha('B'), "'B' is ASCII alpha");
+  static_assert(IsAsciiAlpha('M'), "'M' is ASCII alpha");
+  static_assert(IsAsciiAlpha('Y'), "'Y' is ASCII alpha");
+  static_assert(IsAsciiAlpha('Z'), "'Z' is ASCII alpha");
+
+  static_assert('Z' == 0x5A, "'Z' has value 0x5A");
+  static_assert('[' == 0x5B, "'[' has value 0x5B");
+  static_assert(!IsAsciiAlpha('['), "'[' isn't ASCII alpha");
+
+  static_assert(!IsAsciiAlpha('`'), "'`' isn't ASCII alpha");
+  static_assert('`' == 0x60, "'`' has value 0x60");
+
+  static_assert('a' == 0x61, "'a' has value 0x61");
+  static_assert(IsAsciiAlpha('a'), "'a' is ASCII alpha");
+  static_assert(IsAsciiAlpha('b'), "'b' is ASCII alpha");
+  static_assert(IsAsciiAlpha('m'), "'m' is ASCII alpha");
+  static_assert(IsAsciiAlpha('y'), "'y' is ASCII alpha");
+  static_assert(IsAsciiAlpha('z'), "'z' is ASCII alpha");
 
-// char
+  static_assert('z' == 0x7A, "'z' has value 0x7A");
+  static_assert('{' == 0x7B, "'{' has value 0x7B");
+  static_assert(!IsAsciiAlpha('{'), "'{' isn't ASCII alpha");
+
+  static_assert(!IsAsciiAlpha('5'), "'5' isn't ASCII alpha");
+
+  // char16_t
+
+  static_assert(!IsAsciiAlpha(u'@'), "u'@' isn't ASCII alpha");
+  static_assert(u'@' == 0x40, "u'@' has value 0x40");
+
+  static_assert(u'A' == 0x41, "u'A' has value 0x41");
+  static_assert(IsAsciiAlpha(u'A'), "u'A' is ASCII alpha");
+  static_assert(IsAsciiAlpha(u'B'), "u'B' is ASCII alpha");
+  static_assert(IsAsciiAlpha(u'M'), "u'M' is ASCII alpha");
+  static_assert(IsAsciiAlpha(u'Y'), "u'Y' is ASCII alpha");
+  static_assert(IsAsciiAlpha(u'Z'), "u'Z' is ASCII alpha");
 
-static_assert(!IsAsciiAlpha('@'), "'@' isn't ASCII alpha");
-static_assert('@' == 0x40, "'@' has value 0x40");
+  static_assert(u'Z' == 0x5A, "u'Z' has value 0x5A");
+  static_assert(u'[' == 0x5B, "u'[' has value 0x5B");
+  static_assert(!IsAsciiAlpha(u'['), "u'[' isn't ASCII alpha");
+
+  static_assert(!IsAsciiAlpha(u'`'), "u'`' isn't ASCII alpha");
+  static_assert(u'`' == 0x60, "u'`' has value 0x60");
+
+  static_assert(u'a' == 0x61, "u'a' has value 0x61");
+  static_assert(IsAsciiAlpha(u'a'), "u'a' is ASCII alpha");
+  static_assert(IsAsciiAlpha(u'b'), "u'b' is ASCII alpha");
+  static_assert(IsAsciiAlpha(u'm'), "u'm' is ASCII alpha");
+  static_assert(IsAsciiAlpha(u'y'), "u'y' is ASCII alpha");
+  static_assert(IsAsciiAlpha(u'z'), "u'z' is ASCII alpha");
+
+  static_assert(u'z' == 0x7A, "u'z' has value 0x7A");
+  static_assert(u'{' == 0x7B, "u'{' has value 0x7B");
+  static_assert(!IsAsciiAlpha(u'{'), "u'{' isn't ASCII alpha");
+
+  static_assert(!IsAsciiAlpha(u'5'), "u'5' isn't ASCII alpha");
+
+  // char32_t
+
+  static_assert(!IsAsciiAlpha(U'@'), "U'@' isn't ASCII alpha");
+  static_assert(U'@' == 0x40, "U'@' has value 0x40");
 
-static_assert('A' == 0x41, "'A' has value 0x41");
-static_assert(IsAsciiAlpha('A'), "'A' is ASCII alpha");
-static_assert(IsAsciiAlpha('B'), "'B' is ASCII alpha");
-static_assert(IsAsciiAlpha('M'), "'M' is ASCII alpha");
-static_assert(IsAsciiAlpha('Y'), "'Y' is ASCII alpha");
-static_assert(IsAsciiAlpha('Z'), "'Z' is ASCII alpha");
+  static_assert(U'A' == 0x41, "U'A' has value 0x41");
+  static_assert(IsAsciiAlpha(U'A'), "U'A' is ASCII alpha");
+  static_assert(IsAsciiAlpha(U'B'), "U'B' is ASCII alpha");
+  static_assert(IsAsciiAlpha(U'M'), "U'M' is ASCII alpha");
+  static_assert(IsAsciiAlpha(U'Y'), "U'Y' is ASCII alpha");
+  static_assert(IsAsciiAlpha(U'Z'), "U'Z' is ASCII alpha");
+
+  static_assert(U'Z' == 0x5A, "U'Z' has value 0x5A");
+  static_assert(U'[' == 0x5B, "U'[' has value 0x5B");
+  static_assert(!IsAsciiAlpha(U'['), "U'[' isn't ASCII alpha");
+
+  static_assert(!IsAsciiAlpha(U'`'), "U'`' isn't ASCII alpha");
+  static_assert(U'`' == 0x60, "U'`' has value 0x60");
+
+  static_assert(U'a' == 0x61, "U'a' has value 0x61");
+  static_assert(IsAsciiAlpha(U'a'), "U'a' is ASCII alpha");
+  static_assert(IsAsciiAlpha(U'b'), "U'b' is ASCII alpha");
+  static_assert(IsAsciiAlpha(U'm'), "U'm' is ASCII alpha");
+  static_assert(IsAsciiAlpha(U'y'), "U'y' is ASCII alpha");
+  static_assert(IsAsciiAlpha(U'z'), "U'z' is ASCII alpha");
+
+  static_assert(U'z' == 0x7A, "U'z' has value 0x7A");
+  static_assert(U'{' == 0x7B, "U'{' has value 0x7B");
+  static_assert(!IsAsciiAlpha(U'{'), "U'{' isn't ASCII alpha");
+
+  static_assert(!IsAsciiAlpha(U'5'), "U'5' isn't ASCII alpha");
+}
+
+static void
+TestIsAsciiUppercaseAlpha()
+{
+  // char
+
+  static_assert(!IsAsciiUppercaseAlpha('@'), "'@' isn't ASCII alpha uppercase");
+  static_assert('@' == 0x40, "'@' has value 0x40");
+
+  static_assert('A' == 0x41, "'A' has value 0x41");
+  static_assert(IsAsciiUppercaseAlpha('A'), "'A' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha('B'), "'B' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha('M'), "'M' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha('Y'), "'Y' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha('Z'), "'Z' is ASCII alpha uppercase");
 
-static_assert('Z' == 0x5A, "'Z' has value 0x5A");
-static_assert('[' == 0x5B, "'[' has value 0x5B");
-static_assert(!IsAsciiAlpha('['), "'[' isn't ASCII alpha");
+  static_assert('Z' == 0x5A, "'Z' has value 0x5A");
+  static_assert('[' == 0x5B, "'[' has value 0x5B");
+  static_assert(!IsAsciiUppercaseAlpha('['), "'[' isn't ASCII alpha uppercase");
+
+  static_assert(!IsAsciiUppercaseAlpha('`'), "'`' isn't ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha('a'), "'a' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha('b'), "'b' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha('m'), "'m' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha('y'), "'y' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha('z'), "'z' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha('{'), "'{' isn't ASCII alpha uppercase");
+
+  // char16_t
+
+  static_assert(!IsAsciiUppercaseAlpha(u'@'), "u'@' isn't ASCII alpha uppercase");
+  static_assert(u'@' == 0x40, "u'@' has value 0x40");
+
+  static_assert(u'A' == 0x41, "u'A' has value 0x41");
+  static_assert(IsAsciiUppercaseAlpha(u'A'), "u'A' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha(u'B'), "u'B' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha(u'M'), "u'M' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha(u'Y'), "u'Y' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha(u'Z'), "u'Z' is ASCII alpha uppercase");
 
-static_assert(!IsAsciiAlpha('`'), "'`' isn't ASCII alpha");
-static_assert('`' == 0x60, "'`' has value 0x60");
+  static_assert(u'Z' == 0x5A, "u'Z' has value 0x5A");
+  static_assert(u'[' == 0x5B, "u'[' has value 0x5B");
+  static_assert(!IsAsciiUppercaseAlpha(u'['), "u'[' isn't ASCII alpha uppercase");
+
+  static_assert(!IsAsciiUppercaseAlpha(u'`'), "u'`' isn't ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(u'a'), "u'a' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(u'b'), "u'b' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(u'm'), "u'm' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(u'y'), "u'y' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(u'z'), "u'z' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(u'{'), "u'{' isn't ASCII alpha uppercase");
+
+  // char32_t
+
+  static_assert(!IsAsciiUppercaseAlpha(U'@'), "U'@' isn't ASCII alpha uppercase");
+  static_assert(U'@' == 0x40, "U'@' has value 0x40");
+
+  static_assert(U'A' == 0x41, "U'A' has value 0x41");
+  static_assert(IsAsciiUppercaseAlpha(U'A'), "U'A' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha(U'B'), "U'B' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha(U'M'), "U'M' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha(U'Y'), "U'Y' is ASCII alpha uppercase");
+  static_assert(IsAsciiUppercaseAlpha(U'Z'), "U'Z' is ASCII alpha uppercase");
 
-static_assert('a' == 0x61, "'a' has value 0x61");
-static_assert(IsAsciiAlpha('a'), "'a' is ASCII alpha");
-static_assert(IsAsciiAlpha('b'), "'b' is ASCII alpha");
-static_assert(IsAsciiAlpha('m'), "'m' is ASCII alpha");
-static_assert(IsAsciiAlpha('y'), "'y' is ASCII alpha");
-static_assert(IsAsciiAlpha('z'), "'z' is ASCII alpha");
+  static_assert(U'Z' == 0x5A, "U'Z' has value 0x5A");
+  static_assert(U'[' == 0x5B, "U'[' has value 0x5B");
+  static_assert(!IsAsciiUppercaseAlpha(U'['), "U'[' isn't ASCII alpha uppercase");
+
+  static_assert(!IsAsciiUppercaseAlpha(U'`'), "U'`' isn't ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(U'a'), "U'a' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(U'b'), "U'b' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(U'm'), "U'm' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(U'y'), "U'y' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(U'z'), "U'z' is ASCII alpha uppercase");
+  static_assert(!IsAsciiUppercaseAlpha(U'{'), "U'{' isn't ASCII alpha uppercase");
+}
+
+static void
+TestIsAsciiLowercaseAlpha()
+{
+  // char
+
+  static_assert(!IsAsciiLowercaseAlpha('`'), "'`' isn't ASCII alpha lowercase");
+  static_assert('`' == 0x60, "'`' has value 0x60");
+
+  static_assert('a' == 0x61, "'a' has value 0x61");
+  static_assert(IsAsciiLowercaseAlpha('a'), "'a' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha('b'), "'b' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha('m'), "'m' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha('y'), "'y' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha('z'), "'z' is ASCII alpha lowercase");
+
+  static_assert('z' == 0x7A, "'z' has value 0x7A");
+  static_assert('{' == 0x7B, "'{' has value 0x7B");
+  static_assert(!IsAsciiLowercaseAlpha('{'), "'{' isn't ASCII alpha lowercase");
+
+  static_assert(!IsAsciiLowercaseAlpha('@'), "'@' isn't ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha('A'), "'A' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha('B'), "'B' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha('M'), "'M' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha('Y'), "'Y' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha('Z'), "'Z' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha('['), "'[' isn't ASCII alpha lowercase");
 
-static_assert('z' == 0x7A, "'z' has value 0x7A");
-static_assert('{' == 0x7B, "'{' has value 0x7B");
-static_assert(!IsAsciiAlpha('{'), "'{' isn't ASCII alpha");
+  // char16_t
+
+  static_assert(!IsAsciiLowercaseAlpha(u'`'), "u'`' isn't ASCII alpha lowercase");
+  static_assert(u'`' == 0x60, "u'`' has value 0x60");
+
+  static_assert(u'a' == 0x61, "u'a' has value 0x61");
+  static_assert(IsAsciiLowercaseAlpha(u'a'), "u'a' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha(u'b'), "u'b' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha(u'm'), "u'm' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha(u'y'), "u'y' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha(u'z'), "u'z' is ASCII alpha lowercase");
+
+  static_assert(u'z' == 0x7A, "u'z' has value 0x7A");
+  static_assert(u'{' == 0x7B, "u'{' has value 0x7B");
+  static_assert(!IsAsciiLowercaseAlpha(u'{'), "u'{' isn't ASCII alpha lowercase");
+
+  static_assert(!IsAsciiLowercaseAlpha(u'@'), "u'@' isn't ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(u'A'), "u'A' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(u'B'), "u'B' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(u'M'), "u'M' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(u'Y'), "u'Y' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(u'Z'), "u'Z' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(u'['), "u'[' isn't ASCII alpha lowercase");
+
+  // char32_t
+
+  static_assert(!IsAsciiLowercaseAlpha(U'`'), "U'`' isn't ASCII alpha lowercase");
+  static_assert(U'`' == 0x60, "U'`' has value 0x60");
+
+  static_assert(U'a' == 0x61, "U'a' has value 0x61");
+  static_assert(IsAsciiLowercaseAlpha(U'a'), "U'a' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha(U'b'), "U'b' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha(U'm'), "U'm' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha(U'y'), "U'y' is ASCII alpha lowercase");
+  static_assert(IsAsciiLowercaseAlpha(U'z'), "U'z' is ASCII alpha lowercase");
+
+  static_assert(U'z' == 0x7A, "U'z' has value 0x7A");
+  static_assert(U'{' == 0x7B, "U'{' has value 0x7B");
+  static_assert(!IsAsciiLowercaseAlpha(U'{'), "U'{' isn't ASCII alpha lowercase");
 
-// char16_t
+  static_assert(!IsAsciiLowercaseAlpha(U'@'), "U'@' isn't ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(U'A'), "U'A' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(U'B'), "U'B' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(U'M'), "U'M' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(U'Y'), "U'Y' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(U'Z'), "U'Z' is ASCII alpha lowercase");
+  static_assert(!IsAsciiLowercaseAlpha(U'['), "U'[' isn't ASCII alpha lowercase");
+}
+
+static void
+TestIsAsciiAlphanumeric()
+{
+  // char
+
+  static_assert(!IsAsciiAlphanumeric('/'), "'/' isn't ASCII alphanumeric");
+  static_assert('/' == 0x2F, "'/' has value 0x2F");
 
-static_assert(!IsAsciiAlpha(u'@'), "u'@' isn't ASCII alpha");
-static_assert(u'@' == 0x40, "u'@' has value 0x40");
+  static_assert('0' == 0x30, "'0' has value 0x30");
+  static_assert(IsAsciiAlphanumeric('0'), "'0' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('1'), "'1' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('5'), "'5' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('8'), "'8' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('9'), "'9' is ASCII alphanumeric");
+
+  static_assert('9' == 0x39, "'9' has value 0x39");
+  static_assert(':' == 0x3A, "':' has value 0x3A");
+  static_assert(!IsAsciiAlphanumeric(':'), "':' isn't ASCII alphanumeric");
+
+  static_assert(!IsAsciiAlphanumeric('@'), "'@' isn't ASCII alphanumeric");
+  static_assert('@' == 0x40, "'@' has value 0x40");
+
+  static_assert('A' == 0x41, "'A' has value 0x41");
+  static_assert(IsAsciiAlphanumeric('A'), "'A' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('B'), "'B' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('M'), "'M' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('Y'), "'Y' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('Z'), "'Z' is ASCII alphanumeric");
+
+  static_assert('Z' == 0x5A, "'Z' has value 0x5A");
+  static_assert('[' == 0x5B, "'[' has value 0x5B");
+  static_assert(!IsAsciiAlphanumeric('['), "'[' isn't ASCII alphanumeric");
+
+  static_assert(!IsAsciiAlphanumeric('`'), "'`' isn't ASCII alphanumeric");
+  static_assert('`' == 0x60, "'`' has value 0x60");
 
-static_assert(u'A' == 0x41, "u'A' has value 0x41");
-static_assert(IsAsciiAlpha(u'A'), "u'A' is ASCII alpha");
-static_assert(IsAsciiAlpha(u'B'), "u'B' is ASCII alpha");
-static_assert(IsAsciiAlpha(u'M'), "u'M' is ASCII alpha");
-static_assert(IsAsciiAlpha(u'Y'), "u'Y' is ASCII alpha");
-static_assert(IsAsciiAlpha(u'Z'), "u'Z' is ASCII alpha");
+  static_assert('a' == 0x61, "'a' has value 0x61");
+  static_assert(IsAsciiAlphanumeric('a'), "'a' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('b'), "'b' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('m'), "'m' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('y'), "'y' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric('z'), "'z' is ASCII alphanumeric");
+
+  static_assert('z' == 0x7A, "'z' has value 0x7A");
+  static_assert('{' == 0x7B, "'{' has value 0x7B");
+  static_assert(!IsAsciiAlphanumeric('{'), "'{' isn't ASCII alphanumeric");
+
+  // char16_t
+
+  static_assert(!IsAsciiAlphanumeric(u'/'), "u'/' isn't ASCII alphanumeric");
+  static_assert(u'/' == 0x2F, "u'/' has value 0x2F");
+
+  static_assert(u'0' == 0x30, "u'0' has value 0x30");
+  static_assert(IsAsciiAlphanumeric(u'0'), "u'0' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'1'), "u'1' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'5'), "u'5' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'8'), "u'8' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'9'), "u'9' is ASCII alphanumeric");
+
+  static_assert(u'9' == 0x39, "u'9' has value 0x39");
+  static_assert(u':' == 0x3A, "u':' has value 0x3A");
+  static_assert(!IsAsciiAlphanumeric(u':'), "u':' isn't ASCII alphanumeric");
+
+  static_assert(!IsAsciiAlphanumeric(u'@'), "u'@' isn't ASCII alphanumeric");
+  static_assert(u'@' == 0x40, "u'@' has value 0x40");
+
+  static_assert(u'A' == 0x41, "u'A' has value 0x41");
+  static_assert(IsAsciiAlphanumeric(u'A'), "u'A' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'B'), "u'B' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'M'), "u'M' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'Y'), "u'Y' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'Z'), "u'Z' is ASCII alphanumeric");
+
+  static_assert(u'Z' == 0x5A, "u'Z' has value 0x5A");
+  static_assert(u'[' == 0x5B, "u'[' has value 0x5B");
+  static_assert(!IsAsciiAlphanumeric(u'['), "u'[' isn't ASCII alphanumeric");
+
+  static_assert(!IsAsciiAlphanumeric(u'`'), "u'`' isn't ASCII alphanumeric");
+  static_assert(u'`' == 0x60, "u'`' has value 0x60");
+
+  static_assert(u'a' == 0x61, "u'a' has value 0x61");
+  static_assert(IsAsciiAlphanumeric(u'a'), "u'a' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'b'), "u'b' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'm'), "u'm' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'y'), "u'y' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(u'z'), "u'z' is ASCII alphanumeric");
 
-static_assert(u'Z' == 0x5A, "u'Z' has value 0x5A");
-static_assert(u'[' == 0x5B, "u'[' has value 0x5B");
-static_assert(!IsAsciiAlpha(u'['), "u'[' isn't ASCII alpha");
+  static_assert(u'z' == 0x7A, "u'z' has value 0x7A");
+  static_assert(u'{' == 0x7B, "u'{' has value 0x7B");
+  static_assert(!IsAsciiAlphanumeric(u'{'), "u'{' isn't ASCII alphanumeric");
+
+  // char32_t
+
+  static_assert(!IsAsciiAlphanumeric(U'/'), "U'/' isn't ASCII alphanumeric");
+  static_assert(U'/' == 0x2F, "U'/' has value 0x2F");
+
+  static_assert(U'0' == 0x30, "U'0' has value 0x30");
+  static_assert(IsAsciiAlphanumeric(U'0'), "U'0' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'1'), "U'1' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'5'), "U'5' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'8'), "U'8' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'9'), "U'9' is ASCII alphanumeric");
+
+  static_assert(U'9' == 0x39, "U'9' has value 0x39");
+  static_assert(U':' == 0x3A, "U':' has value 0x3A");
+  static_assert(!IsAsciiAlphanumeric(U':'), "U':' isn't ASCII alphanumeric");
+
+  static_assert(!IsAsciiAlphanumeric(U'@'), "U'@' isn't ASCII alphanumeric");
+  static_assert(U'@' == 0x40, "U'@' has value 0x40");
+
+  static_assert(U'A' == 0x41, "U'A' has value 0x41");
+  static_assert(IsAsciiAlphanumeric(U'A'), "U'A' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'B'), "U'B' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'M'), "U'M' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'Y'), "U'Y' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'Z'), "U'Z' is ASCII alphanumeric");
+
+  static_assert(U'Z' == 0x5A, "U'Z' has value 0x5A");
+  static_assert(U'[' == 0x5B, "U'[' has value 0x5B");
+  static_assert(!IsAsciiAlphanumeric(U'['), "U'[' isn't ASCII alphanumeric");
 
-static_assert(!IsAsciiAlpha(u'`'), "u'`' isn't ASCII alpha");
-static_assert(u'`' == 0x60, "u'`' has value 0x60");
+  static_assert(!IsAsciiAlphanumeric(U'`'), "U'`' isn't ASCII alphanumeric");
+  static_assert(U'`' == 0x60, "U'`' has value 0x60");
+
+  static_assert(U'a' == 0x61, "U'a' has value 0x61");
+  static_assert(IsAsciiAlphanumeric(U'a'), "U'a' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'b'), "U'b' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'm'), "U'm' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'y'), "U'y' is ASCII alphanumeric");
+  static_assert(IsAsciiAlphanumeric(U'z'), "U'z' is ASCII alphanumeric");
+
+  static_assert(U'z' == 0x7A, "U'z' has value 0x7A");
+  static_assert(U'{' == 0x7B, "U'{' has value 0x7B");
+  static_assert(!IsAsciiAlphanumeric(U'{'), "U'{' isn't ASCII alphanumeric");
+}
+
+static void
+TestAsciiAlphanumericToNumber()
+{
+  // When AsciiAlphanumericToNumber becomes constexpr, make sure to convert all
+  // these to just static_assert.
+
+  // char
+
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('0') == 0, "'0' converts to 0");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('1') == 1, "'1' converts to 1");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('2') == 2, "'2' converts to 2");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('3') == 3, "'3' converts to 3");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('4') == 4, "'4' converts to 4");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('5') == 5, "'5' converts to 5");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('6') == 6, "'6' converts to 6");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('7') == 7, "'7' converts to 7");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('8') == 8, "'8' converts to 8");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('9') == 9, "'9' converts to 9");
 
-static_assert(u'a' == 0x61, "u'a' has value 0x61");
-static_assert(IsAsciiAlpha(u'a'), "u'a' is ASCII alpha");
-static_assert(IsAsciiAlpha(u'b'), "u'b' is ASCII alpha");
-static_assert(IsAsciiAlpha(u'm'), "u'm' is ASCII alpha");
-static_assert(IsAsciiAlpha(u'y'), "u'y' is ASCII alpha");
-static_assert(IsAsciiAlpha(u'z'), "u'z' is ASCII alpha");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('A') == 10, "'A' converts to 10");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('B') == 11, "'B' converts to 11");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('C') == 12, "'C' converts to 12");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('D') == 13, "'D' converts to 13");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('E') == 14, "'E' converts to 14");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('F') == 15, "'F' converts to 15");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('G') == 16, "'G' converts to 16");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('H') == 17, "'H' converts to 17");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('I') == 18, "'I' converts to 18");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('J') == 19, "'J' converts to 19");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('K') == 20, "'K' converts to 20");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('L') == 21, "'L' converts to 21");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('M') == 22, "'M' converts to 22");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('N') == 23, "'N' converts to 23");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('O') == 24, "'O' converts to 24");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('P') == 25, "'P' converts to 25");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('Q') == 26, "'Q' converts to 26");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('R') == 27, "'R' converts to 27");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('S') == 28, "'S' converts to 28");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('T') == 29, "'T' converts to 29");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('U') == 30, "'U' converts to 30");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('V') == 31, "'V' converts to 31");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('W') == 32, "'W' converts to 32");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('X') == 33, "'X' converts to 33");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('Y') == 34, "'Y' converts to 34");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('Z') == 35, "'Z' converts to 35");
 
-static_assert(u'z' == 0x7A, "u'z' has value 0x7A");
-static_assert(u'{' == 0x7B, "u'{' has value 0x7B");
-static_assert(!IsAsciiAlpha(u'{'), "u'{' isn't ASCII alpha");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('a') == 10, "'a' converts to 10");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('b') == 11, "'b' converts to 11");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('c') == 12, "'c' converts to 12");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('d') == 13, "'d' converts to 13");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('e') == 14, "'e' converts to 14");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('f') == 15, "'f' converts to 15");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('g') == 16, "'g' converts to 16");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('h') == 17, "'h' converts to 17");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('i') == 18, "'i' converts to 18");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('j') == 19, "'j' converts to 19");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('k') == 20, "'k' converts to 20");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('l') == 21, "'l' converts to 21");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('m') == 22, "'m' converts to 22");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('n') == 23, "'n' converts to 23");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('o') == 24, "'o' converts to 24");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('p') == 25, "'p' converts to 25");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('q') == 26, "'q' converts to 26");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('r') == 27, "'r' converts to 27");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('s') == 28, "'s' converts to 28");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('t') == 29, "'t' converts to 29");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('u') == 30, "'u' converts to 30");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('v') == 31, "'v' converts to 31");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('w') == 32, "'w' converts to 32");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('x') == 33, "'x' converts to 33");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('y') == 34, "'y' converts to 34");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber('z') == 35, "'z' converts to 35");
+
+  // char16_t
 
-// char32_t
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'0') == 0, "u'0' converts to 0");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'1') == 1, "u'1' converts to 1");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'2') == 2, "u'2' converts to 2");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'3') == 3, "u'3' converts to 3");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'4') == 4, "u'4' converts to 4");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'5') == 5, "u'5' converts to 5");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'6') == 6, "u'6' converts to 6");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'7') == 7, "u'7' converts to 7");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'8') == 8, "u'8' converts to 8");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'9') == 9, "u'9' converts to 9");
 
-static_assert(!IsAsciiAlpha(U'@'), "U'@' isn't ASCII alpha");
-static_assert(U'@' == 0x40, "U'@' has value 0x40");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'A') == 10, "u'A' converts to 10");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'B') == 11, "u'B' converts to 11");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'C') == 12, "u'C' converts to 12");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'D') == 13, "u'D' converts to 13");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'E') == 14, "u'E' converts to 14");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'F') == 15, "u'F' converts to 15");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'G') == 16, "u'G' converts to 16");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'H') == 17, "u'H' converts to 17");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'I') == 18, "u'I' converts to 18");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'J') == 19, "u'J' converts to 19");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'K') == 20, "u'K' converts to 20");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'L') == 21, "u'L' converts to 21");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'M') == 22, "u'M' converts to 22");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'N') == 23, "u'N' converts to 23");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'O') == 24, "u'O' converts to 24");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'P') == 25, "u'P' converts to 25");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'Q') == 26, "u'Q' converts to 26");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'R') == 27, "u'R' converts to 27");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'S') == 28, "u'S' converts to 28");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'T') == 29, "u'T' converts to 29");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'U') == 30, "u'U' converts to 30");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'V') == 31, "u'V' converts to 31");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'W') == 32, "u'W' converts to 32");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'X') == 33, "u'X' converts to 33");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'Y') == 34, "u'Y' converts to 34");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'Z') == 35, "u'Z' converts to 35");
 
-static_assert(U'A' == 0x41, "U'A' has value 0x41");
-static_assert(IsAsciiAlpha(U'A'), "U'A' is ASCII alpha");
-static_assert(IsAsciiAlpha(U'B'), "U'B' is ASCII alpha");
-static_assert(IsAsciiAlpha(U'M'), "U'M' is ASCII alpha");
-static_assert(IsAsciiAlpha(U'Y'), "U'Y' is ASCII alpha");
-static_assert(IsAsciiAlpha(U'Z'), "U'Z' is ASCII alpha");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'a') == 10, "u'a' converts to 10");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'b') == 11, "u'b' converts to 11");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'c') == 12, "u'c' converts to 12");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'd') == 13, "u'd' converts to 13");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'e') == 14, "u'e' converts to 14");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'f') == 15, "u'f' converts to 15");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'g') == 16, "u'g' converts to 16");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'h') == 17, "u'h' converts to 17");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'i') == 18, "u'i' converts to 18");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'j') == 19, "u'j' converts to 19");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'k') == 20, "u'k' converts to 20");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'l') == 21, "u'l' converts to 21");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'm') == 22, "u'm' converts to 22");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'n') == 23, "u'n' converts to 23");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'o') == 24, "u'o' converts to 24");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'p') == 25, "u'p' converts to 25");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'q') == 26, "u'q' converts to 26");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'r') == 27, "u'r' converts to 27");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u's') == 28, "u's' converts to 28");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u't') == 29, "u't' converts to 29");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'u') == 30, "u'u' converts to 30");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'v') == 31, "u'v' converts to 31");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'w') == 32, "u'w' converts to 32");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'x') == 33, "u'x' converts to 33");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'y') == 34, "u'y' converts to 34");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(u'z') == 35, "u'z' converts to 35");
+
+  // char32_t
+
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'0') == 0, "U'0' converts to 0");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'1') == 1, "U'1' converts to 1");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'2') == 2, "U'2' converts to 2");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'3') == 3, "U'3' converts to 3");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'4') == 4, "U'4' converts to 4");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'5') == 5, "U'5' converts to 5");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'6') == 6, "U'6' converts to 6");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'7') == 7, "U'7' converts to 7");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'8') == 8, "U'8' converts to 8");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'9') == 9, "U'9' converts to 9");
 
-static_assert(U'Z' == 0x5A, "U'Z' has value 0x5A");
-static_assert(U'[' == 0x5B, "U'[' has value 0x5B");
-static_assert(!IsAsciiAlpha(U'['), "U'[' isn't ASCII alpha");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'A') == 10, "U'A' converts to 10");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'B') == 11, "U'B' converts to 11");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'C') == 12, "U'C' converts to 12");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'D') == 13, "U'D' converts to 13");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'E') == 14, "U'E' converts to 14");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'F') == 15, "U'F' converts to 15");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'G') == 16, "U'G' converts to 16");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'H') == 17, "U'H' converts to 17");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'I') == 18, "U'I' converts to 18");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'J') == 19, "U'J' converts to 19");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'K') == 20, "U'K' converts to 20");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'L') == 21, "U'L' converts to 21");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'M') == 22, "U'M' converts to 22");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'N') == 23, "U'N' converts to 23");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'O') == 24, "U'O' converts to 24");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'P') == 25, "U'P' converts to 25");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'Q') == 26, "U'Q' converts to 26");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'R') == 27, "U'R' converts to 27");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'S') == 28, "U'S' converts to 28");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'T') == 29, "U'T' converts to 29");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'U') == 30, "U'U' converts to 30");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'V') == 31, "U'V' converts to 31");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'W') == 32, "U'W' converts to 32");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'X') == 33, "U'X' converts to 33");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'Y') == 34, "U'Y' converts to 34");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'Z') == 35, "U'Z' converts to 35");
+
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'a') == 10, "U'a' converts to 10");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'b') == 11, "U'b' converts to 11");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'c') == 12, "U'c' converts to 12");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'd') == 13, "U'd' converts to 13");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'e') == 14, "U'e' converts to 14");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'f') == 15, "U'f' converts to 15");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'g') == 16, "U'g' converts to 16");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'h') == 17, "U'h' converts to 17");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'i') == 18, "U'i' converts to 18");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'j') == 19, "U'j' converts to 19");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'k') == 20, "U'k' converts to 20");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'l') == 21, "U'l' converts to 21");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'm') == 22, "U'm' converts to 22");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'n') == 23, "U'n' converts to 23");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'o') == 24, "U'o' converts to 24");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'p') == 25, "U'p' converts to 25");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'q') == 26, "U'q' converts to 26");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'r') == 27, "U'r' converts to 27");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U's') == 28, "U's' converts to 28");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U't') == 29, "U't' converts to 29");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'u') == 30, "U'u' converts to 30");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'v') == 31, "U'v' converts to 31");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'w') == 32, "U'w' converts to 32");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'x') == 33, "U'x' converts to 33");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'y') == 34, "U'y' converts to 34");
+  MOZ_RELEASE_ASSERT(AsciiAlphanumericToNumber(U'z') == 35, "U'z' converts to 35");
+}
 
-static_assert(!IsAsciiAlpha(U'`'), "U'`' isn't ASCII alpha");
-static_assert(U'`' == 0x60, "U'`' has value 0x60");
+static void
+TestIsAsciiDigit()
+{
+  // char
+
+  static_assert(!IsAsciiDigit('/'), "'/' isn't an ASCII digit");
+  static_assert('/' == 0x2F, "'/' has value 0x2F");
+
+  static_assert('0' == 0x30, "'0' has value 0x30");
+  static_assert(IsAsciiDigit('0'), "'0' is an ASCII digit");
+  static_assert(IsAsciiDigit('1'), "'1' is an ASCII digit");
+  static_assert(IsAsciiDigit('5'), "'5' is an ASCII digit");
+  static_assert(IsAsciiDigit('8'), "'8' is an ASCII digit");
+  static_assert(IsAsciiDigit('9'), "'9' is an ASCII digit");
+
+  static_assert('9' == 0x39, "'9' has value 0x39");
+  static_assert(':' == 0x3A, "':' has value 0x3A");
+  static_assert(!IsAsciiDigit(':'), "':' isn't an ASCII digit");
+
+  static_assert(!IsAsciiDigit('@'), "'@' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('A'), "'A' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('B'), "'B' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('M'), "'M' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('Y'), "'Y' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('Z'), "'Z' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('['), "'[' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('`'), "'`' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('a'), "'a' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('b'), "'b' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('m'), "'m' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('y'), "'y' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('z'), "'z' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit('{'), "'{' isn't an ASCII digit");
+
+  // char16_t
+
+  static_assert(!IsAsciiDigit(u'/'), "u'/' isn't an ASCII digit");
+  static_assert(u'/' == 0x2F, "u'/' has value 0x2F");
+  static_assert(u'0' == 0x30, "u'0' has value 0x30");
+  static_assert(IsAsciiDigit(u'0'), "u'0' is an ASCII digit");
+  static_assert(IsAsciiDigit(u'1'), "u'1' is an ASCII digit");
+  static_assert(IsAsciiDigit(u'5'), "u'5' is an ASCII digit");
+  static_assert(IsAsciiDigit(u'8'), "u'8' is an ASCII digit");
+  static_assert(IsAsciiDigit(u'9'), "u'9' is an ASCII digit");
 
-static_assert(U'a' == 0x61, "U'a' has value 0x61");
-static_assert(IsAsciiAlpha(U'a'), "U'a' is ASCII alpha");
-static_assert(IsAsciiAlpha(U'b'), "U'b' is ASCII alpha");
-static_assert(IsAsciiAlpha(U'm'), "U'm' is ASCII alpha");
-static_assert(IsAsciiAlpha(U'y'), "U'y' is ASCII alpha");
-static_assert(IsAsciiAlpha(U'z'), "U'z' is ASCII alpha");
+  static_assert(u'9' == 0x39, "u'9' has value 0x39");
+  static_assert(u':' == 0x3A, "u':' has value 0x3A");
+  static_assert(!IsAsciiDigit(u':'), "u':' isn't an ASCII digit");
+
+  static_assert(!IsAsciiDigit(u'@'), "u'@' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'A'), "u'A' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'B'), "u'B' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'M'), "u'M' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'Y'), "u'Y' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'Z'), "u'Z' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'['), "u'[' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'`'), "u'`' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'a'), "u'a' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'b'), "u'b' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'm'), "u'm' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'y'), "u'y' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'z'), "u'z' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(u'{'), "u'{' isn't an ASCII digit");
+
+  // char32_t
+
+  static_assert(!IsAsciiDigit(U'/'), "U'/' isn't an ASCII digit");
+  static_assert(U'/' == 0x2F, "U'/' has value 0x2F");
 
-static_assert(U'z' == 0x7A, "U'z' has value 0x7A");
-static_assert(U'{' == 0x7B, "U'{' has value 0x7B");
-static_assert(!IsAsciiAlpha(U'{'), "U'{' isn't ASCII alpha");
+  static_assert(U'0' == 0x30, "U'0' has value 0x30");
+  static_assert(IsAsciiDigit(U'0'), "U'0' is an ASCII digit");
+  static_assert(IsAsciiDigit(U'1'), "U'1' is an ASCII digit");
+  static_assert(IsAsciiDigit(U'5'), "U'5' is an ASCII digit");
+  static_assert(IsAsciiDigit(U'8'), "U'8' is an ASCII digit");
+  static_assert(IsAsciiDigit(U'9'), "U'9' is an ASCII digit");
+
+  static_assert(U'9' == 0x39, "U'9' has value 0x39");
+  static_assert(U':' == 0x3A, "U':' has value 0x3A");
+  static_assert(!IsAsciiDigit(U':'), "U':' isn't an ASCII digit");
+
+  static_assert(!IsAsciiDigit(U'@'), "U'@' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'A'), "U'A' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'B'), "U'B' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'M'), "U'M' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'Y'), "U'Y' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'Z'), "U'Z' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'['), "U'[' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'`'), "U'`' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'a'), "U'a' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'b'), "U'b' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'm'), "U'm' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'y'), "U'y' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'z'), "U'z' isn't an ASCII digit");
+  static_assert(!IsAsciiDigit(U'{'), "U'{' isn't an ASCII digit");
+}
 
 int
 main()
 {
-  return 0;
+  TestIsAsciiAlpha();
+  TestIsAsciiUppercaseAlpha();
+  TestIsAsciiLowercaseAlpha();
+  TestIsAsciiAlphanumeric();
+  TestAsciiAlphanumericToNumber();
+  TestIsAsciiDigit();
 }
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -15,16 +15,17 @@
 #include "nsIObjectOutputStream.h"
 #include "nsIIDNService.h"
 #include "mozilla/Logging.h"
 #include "nsAutoPtr.h"
 #include "nsIURLParser.h"
 #include "nsNetCID.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ipc/URIUtils.h"
+#include "mozilla/TextUtils.h"
 #include <algorithm>
 #include "nsContentUtils.h"
 #include "prprf.h"
 #include "nsReadableUtils.h"
 #include "mozilla/net/MozURL_ffi.h"
 
 
 //
@@ -908,17 +909,17 @@ nsStandardURL::BuildNormalizedSpec(const
 
     buf[i] = '\0';
 
     // https://url.spec.whatwg.org/#path-state (1.4.1.2)
     // https://url.spec.whatwg.org/#windows-drive-letter
     if (SegmentIs(buf, mScheme, "file")) {
         char* path = &buf[mPath.mPos];
         if (mPath.mLen >= 3 && path[0] == '/'
-            && nsCRT::IsAsciiAlpha(path[1])
+            && IsAsciiAlpha(path[1])
             && path[2] == '|') {
             buf[mPath.mPos + 2] = ':';
         }
     }
 
     if (mDirectory.mLen > 1) {
         netCoalesceFlags coalesceFlag = NET_COALESCE_NORMAL;
         if (SegmentIs(buf,mScheme,"ftp")) {
--- a/netwerk/base/nsURLHelper.cpp
+++ b/netwerk/base/nsURLHelper.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim:set ts=4 sw=4 sts=4 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/RangedPtr.h"
+#include "mozilla/TextUtils.h"
 
 #include <algorithm>
 #include <iterator>
 
 #include "nsASCIIMask.h"
 #include "nsURLHelper.h"
 #include "nsIFile.h"
 #include "nsIURLParser.h"
@@ -472,24 +473,20 @@ net_ResolveRelativePath(const nsACString
     result = path;
     return NS_OK;
 }
 
 //----------------------------------------------------------------------------
 // scheme fu
 //----------------------------------------------------------------------------
 
-static bool isAsciiAlpha(char c) {
-    return nsCRT::IsAsciiAlpha(c);
-}
-
 static bool
 net_IsValidSchemeChar(const char aChar)
 {
-    if (nsCRT::IsAsciiAlpha(aChar) || nsCRT::IsAsciiDigit(aChar) ||
+    if (IsAsciiAlpha(aChar) || IsAsciiDigit(aChar) ||
         aChar == '+' || aChar == '.' || aChar == '-') {
         return true;
     }
     return false;
 }
 
 /* Extract URI-Scheme if possible */
 nsresult
@@ -505,17 +502,17 @@ net_ExtractURLScheme(const nsACString &i
         if ((uint8_t) *start > 0x20) {
             break;
         }
         start++;
     }
 
     Tokenizer p(Substring(start, end), "\r\n\t");
     p.Record();
-    if (!p.CheckChar(isAsciiAlpha)) {
+    if (!p.CheckChar(IsAsciiAlpha)) {
         // First char must be alpha
         return NS_ERROR_MALFORMED_URI;
     }
 
     while (p.CheckChar(net_IsValidSchemeChar) || p.CheckWhite()) {
         // Skip valid scheme characters or \r\n\t
     }
 
@@ -528,23 +525,23 @@ net_ExtractURLScheme(const nsACString &i
     ToLowerCase(scheme);
     return NS_OK;
 }
 
 bool
 net_IsValidScheme(const char *scheme, uint32_t schemeLen)
 {
     // first char must be alpha
-    if (!nsCRT::IsAsciiAlpha(*scheme))
+    if (!IsAsciiAlpha(*scheme))
         return false;
 
     // nsCStrings may have embedded nulls -- reject those too
     for (; schemeLen; ++scheme, --schemeLen) {
-        if (!(nsCRT::IsAsciiAlpha(*scheme) ||
-              nsCRT::IsAsciiDigit(*scheme) ||
+        if (!(IsAsciiAlpha(*scheme) ||
+              IsAsciiDigit(*scheme) ||
               *scheme == '+' ||
               *scheme == '.' ||
               *scheme == '-'))
             return false;
     }
 
     return true;
 }
@@ -562,17 +559,17 @@ net_IsAbsoluteURL(const nsACString& uri)
             break;
         }
         start++;
     }
 
     Tokenizer p(Substring(start, end), "\r\n\t");
 
     // First char must be alpha
-    if (!p.CheckChar(isAsciiAlpha)) {
+    if (!p.CheckChar(IsAsciiAlpha)) {
         return false;
     }
 
     while (p.CheckChar(net_IsValidSchemeChar) || p.CheckWhite()) {
         // Skip valid scheme characters or \r\n\t
     }
     if (!p.CheckChar(':')) {
         return false;
--- a/netwerk/base/nsURLParsers.cpp
+++ b/netwerk/base/nsURLParsers.cpp
@@ -1,21 +1,21 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <string.h>
 
 #include "mozilla/RangedPtr.h"
+#include "mozilla/TextUtils.h"
 
 #include "nsURLParsers.h"
 #include "nsURLHelper.h"
 #include "nsString.h"
-#include "nsCRT.h"
 
 using namespace mozilla;
 
 //----------------------------------------------------------------------------
 
 static uint32_t
 CountConsecutiveSlashes(const char *str, int32_t len)
 {
@@ -382,17 +382,17 @@ nsNoAuthURLParser::ParseAfterScheme(cons
             if (specLen > 2) {
                 // looks like there is an authority section
 
                 // if the authority looks like a drive number then we
                 // really want to treat it as part of the path
                 // [a-zA-Z][:|]{/\}
                 // i.e one of:   c:   c:\foo  c:/foo  c|  c|\foo  c|/foo
                 if ((specLen > 3) && (spec[3] == ':' || spec[3] == '|') &&
-                    nsCRT::IsAsciiAlpha(spec[2]) &&
+                    IsAsciiAlpha(spec[2]) &&
                     ((specLen == 4) || (spec[4] == '/') || (spec[4] == '\\'))) {
                     pos = 1;
                     break;
                 }
                 // Ignore apparent authority; path is everything after it
                 for (p = spec + 2; p < spec + specLen; ++p) {
                     if (*p == '/' || *p == '?' || *p == '#')
                         break;
@@ -429,17 +429,17 @@ nsNoAuthURLParser::ParseFilePath(const c
 
     // look for a filepath consisting of only a drive number, which may or
     // may not have a leading slash.
     if (filepathLen > 1 && filepathLen < 4) {
         const char *end = filepath + filepathLen;
         const char *p = filepath;
         if (*p == '/')
             p++;
-        if ((end-p == 2) && (p[1]==':' || p[1]=='|') && nsCRT::IsAsciiAlpha(*p)) {
+        if ((end-p == 2) && (p[1]==':' || p[1]=='|') && IsAsciiAlpha(*p)) {
             // filepath = <drive-number>:
             SET_RESULT(directory, 0, filepathLen);
             SET_RESULT(basename, 0, -1);
             SET_RESULT(extension, 0, -1);
             return NS_OK;
         }
     }
 
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -4082,17 +4082,17 @@ nsHalfOpenSocket::SetupStreams(nsISocket
         tmpFlags |= nsISocketTransport::RETRY_WITH_DIFFERENT_IP_FAMILY;
 
         // From the same reason, let the backup socket fail faster to try the other family.
         uint16_t fallbackTimeout = isBackup ? gHttpHandler->GetFallbackSynTimeout() : 0;
         if (fallbackTimeout) {
             socketTransport->SetTimeout(nsISocketTransport::TIMEOUT_CONNECT,
                                         fallbackTimeout);
         }
-    } else if (isBackup) {
+    } else if (isBackup && gHttpHandler->FastFallbackToIPv4()) {
         // For backup connections, we disable IPv6. That's because some users have
         // broken IPv6 connectivity (leading to very long timeouts), and disabling
         // IPv6 on the backup connection gives them a much better user experience
         // with dual-stack hosts, though they still pay the 250ms delay for each new
         // connection. This strategy is also known as "happy eyeballs".
         tmpFlags |= nsISocketTransport::DISABLE_IPV6;
     }
 
--- a/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp
+++ b/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp
@@ -1,28 +1,32 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/TextUtils.h"
 #include "mozTXTToHTMLConv.h"
 #include "nsNetUtil.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "nsIExternalProtocolHandler.h"
 #include "nsIIOService.h"
 #include "nsIURI.h"
 
 #include <algorithm>
 
 #ifdef DEBUG_BenB_Perf
 #include "prtime.h"
 #include "prinrval.h"
 #endif
 
+using mozilla::IsAsciiAlpha;
+using mozilla::IsAsciiDigit;
+
 const double growthRate = 1.2;
 
 // Bug 183111, editor now replaces multiple spaces with leading
 // 0xA0's and a single ending space, so need to treat 0xA0's as spaces.
 // 0xA0 is the Latin1/Unicode character for "non-breaking space (nbsp)"
 // Also recognize the Japanese ideographic space 0x3000 as a space.
 static inline bool IsSpace(const char16_t aChar)
 {
@@ -214,24 +218,24 @@ mozTXTToHTMLConv::FindURLStart(const cha
     }
     else
       return false;
   }
   case freetext:
   {
     int32_t i = pos - 1;
     for (; i >= 0 && (
-         nsCRT::IsAsciiAlpha(aInString[uint32_t(i)]) ||
-         nsCRT::IsAsciiDigit(aInString[uint32_t(i)]) ||
+         IsAsciiAlpha(aInString[uint32_t(i)]) ||
+         IsAsciiDigit(aInString[uint32_t(i)]) ||
          aInString[uint32_t(i)] == '+' ||
          aInString[uint32_t(i)] == '-' ||
          aInString[uint32_t(i)] == '.'
          ); i--)
       ;
-    if (++i >= 0 && uint32_t(i) < pos && nsCRT::IsAsciiAlpha(aInString[uint32_t(i)]))
+    if (++i >= 0 && uint32_t(i) < pos && IsAsciiAlpha(aInString[uint32_t(i)]))
     {
       start = uint32_t(i);
       return true;
     }
     else
       return false;
   }
   case abbreviated:
@@ -252,18 +256,18 @@ mozTXTToHTMLConv::FindURLStart(const cha
              && (!isEmail || nsCRT::IsAscii(aInString[uint32_t(i)]))
          ; i--)
       ;
     if
       (
         ++i >= 0 && uint32_t(i) < pos
           &&
           (
-            nsCRT::IsAsciiAlpha(aInString[uint32_t(i)]) ||
-            nsCRT::IsAsciiDigit(aInString[uint32_t(i)])
+            IsAsciiAlpha(aInString[uint32_t(i)]) ||
+            IsAsciiDigit(aInString[uint32_t(i)])
           )
       )
     {
       start = uint32_t(i);
       return true;
     }
     else
       return false;
@@ -592,35 +596,35 @@ mozTXTToHTMLConv::ItMatchesDelimited(con
     return false;
 
   char16_t text0 = aInString[0];
   char16_t textAfterPos = aInString[aRepLen + (before == LT_IGNORE ? 0 : 1)];
 
   if
     (
       (before == LT_ALPHA
-        && !nsCRT::IsAsciiAlpha(text0)) ||
+        && !IsAsciiAlpha(text0)) ||
       (before == LT_DIGIT
-        && !nsCRT::IsAsciiDigit(text0)) ||
+        && !IsAsciiDigit(text0)) ||
       (before == LT_DELIMITER
         &&
         (
-          nsCRT::IsAsciiAlpha(text0) ||
-          nsCRT::IsAsciiDigit(text0) ||
+          IsAsciiAlpha(text0) ||
+          IsAsciiDigit(text0) ||
           text0 == *rep
         )) ||
       (after == LT_ALPHA
-        && !nsCRT::IsAsciiAlpha(textAfterPos)) ||
+        && !IsAsciiAlpha(textAfterPos)) ||
       (after == LT_DIGIT
-        && !nsCRT::IsAsciiDigit(textAfterPos)) ||
+        && !IsAsciiDigit(textAfterPos)) ||
       (after == LT_DELIMITER
         &&
         (
-          nsCRT::IsAsciiAlpha(textAfterPos) ||
-          nsCRT::IsAsciiDigit(textAfterPos) ||
+          IsAsciiAlpha(textAfterPos) ||
+          IsAsciiDigit(textAfterPos) ||
           textAfterPos == *rep
         )) ||
         !Substring(Substring(aInString, aInString+aInLength),
                    (before == LT_IGNORE ? 0 : 1),
                    aRepLen).Equals(Substring(rep, rep+aRepLen),
                                    nsCaseInsensitiveStringComparator())
     )
     return false;
@@ -939,39 +943,39 @@ mozTXTToHTMLConv::GlyphHit(const char16_
 
   // x^2  =>  x<sup>2</sup>,   also handle powers x^-2,  x^0.5
   // implement regular expression /[\dA-Za-z\)\]}]\^-?\d+(\.\d+)*[^\dA-Za-z]/
   if
     (
       text1 == '^'
       &&
       (
-        nsCRT::IsAsciiDigit(text0) || nsCRT::IsAsciiAlpha(text0) ||
+        IsAsciiDigit(text0) || IsAsciiAlpha(text0) ||
         text0 == ')' || text0 == ']' || text0 == '}'
       )
       &&
       (
-        (2 < aInLength && nsCRT::IsAsciiDigit(aInString[2])) ||
-        (3 < aInLength && aInString[2] == '-' && nsCRT::IsAsciiDigit(aInString[3]))
+        (2 < aInLength && IsAsciiDigit(aInString[2])) ||
+        (3 < aInLength && aInString[2] == '-' && IsAsciiDigit(aInString[3]))
       )
     )
   {
     // Find first non-digit
     int32_t delimPos = 3;  // skip "^" and first digit (or '-')
     for (; delimPos < aInLength
            &&
            (
-             nsCRT::IsAsciiDigit(aInString[delimPos]) ||
+             IsAsciiDigit(aInString[delimPos]) ||
              (aInString[delimPos] == '.' && delimPos + 1 < aInLength &&
-               nsCRT::IsAsciiDigit(aInString[delimPos + 1]))
+               IsAsciiDigit(aInString[delimPos + 1]))
            );
          delimPos++)
       ;
 
-    if (delimPos < aInLength && nsCRT::IsAsciiAlpha(aInString[delimPos]))
+    if (delimPos < aInLength && IsAsciiAlpha(aInString[delimPos]))
     {
       return false;
     }
 
     outputHTML.Truncate();
     outputHTML += text0;
     outputHTML.AppendLiteral(
       "<sup class=\"moz-txt-sup\">"
@@ -1043,17 +1047,17 @@ mozTXTToHTMLConv::CiteLevelTXT(const cha
 
        logLineStart is the position of "t" in this example
     */
     uint32_t i = logLineStart;
 
 #ifdef QUOTE_RECOGNITION_AGGRESSIVE
     for (; int32_t(i) < lineLength && IsSpace(line[i]); i++)
       ;
-    for (; int32_t(i) < lineLength && nsCRT::IsAsciiAlpha(line[i])
+    for (; int32_t(i) < lineLength && IsAsciiAlpha(line[i])
                                    && nsCRT::IsUpper(line[i])   ; i++)
       ;
     if (int32_t(i) < lineLength && (line[i] == '>' || line[i] == ']'))
 #else
     if (int32_t(i) < lineLength && line[i] == '>')
 #endif
     {
       i++;
--- a/security/sandbox/win/SandboxInitialization.cpp
+++ b/security/sandbox/win/SandboxInitialization.cpp
@@ -78,17 +78,17 @@ EnableHandleCloseMonitoring()
   }
 
   return true;
 }
 
 static bool
 ShouldDisableHandleVerifier()
 {
-#if defined(_X86_) && (defined(NIGHTLY_BUILD) || defined(DEBUG))
+#if defined(_X86_) && (defined(EARLY_BETA_OR_EARLIER) || defined(DEBUG))
   // Chromium only has the verifier enabled for 32-bit and our close monitoring
   // hooks cause debug assertions for 64-bit anyway.
   // For x86 keep the verifier enabled by default only for Nightly or debug.
   return false;
 #else
   return !getenv("MOZ_ENABLE_HANDLE_VERIFIER");
 #endif
 }
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -632,20 +632,21 @@ impl<'le> GeckoElement<'le> {
         self.xbl_binding().and_then(|b| b.binding_with_content())
     }
 
     #[inline]
     fn has_xbl_binding_with_content(&self) -> bool {
         !self.xbl_binding_with_content().is_none()
     }
 
-    /// This and has_xbl_binding_parent duplicate the logic in Gecko's virtual
-    /// nsINode::GetBindingParent function, which only has two implementations:
-    /// one for XUL elements, and one for other elements.  We just hard code in
-    /// our knowledge of those two implementations here.
+    /// This duplicates the logic in Gecko's virtual nsINode::GetBindingParent
+    /// function, which only has two implementations: one for XUL elements, and
+    /// one for other elements.
+    ///
+    /// We just hard code in our knowledge of those two implementations here.
     fn xbl_binding_parent(&self) -> Option<Self> {
         if self.is_xul_element() {
             // FIXME(heycam): Having trouble with bindgen on nsXULElement,
             // where the binding parent is stored in a member variable
             // rather than in slots.  So just get it through FFI for now.
             unsafe { bindings::Gecko_GetBindingParent(self.0).map(GeckoElement) }
         } else {
             let binding_parent = unsafe { self.non_xul_xbl_binding_parent_raw_content().as_ref() }
@@ -662,27 +663,16 @@ impl<'le> GeckoElement<'le> {
     }
 
     fn non_xul_xbl_binding_parent_raw_content(&self) -> *mut nsIContent {
         debug_assert!(!self.is_xul_element());
         self.extended_slots()
             .map_or(ptr::null_mut(), |slots| slots._base.mBindingParent)
     }
 
-    fn has_xbl_binding_parent(&self) -> bool {
-        if self.is_xul_element() {
-            // FIXME(heycam): Having trouble with bindgen on nsXULElement,
-            // where the binding parent is stored in a member variable
-            // rather than in slots.  So just get it through FFI for now.
-            unsafe { bindings::Gecko_GetBindingParent(self.0).is_some() }
-        } else {
-            !self.non_xul_xbl_binding_parent_raw_content().is_null()
-        }
-    }
-
     #[inline]
     fn namespace_id(&self) -> i32 {
         self.as_node().node_info().mInner.mNamespaceID
     }
 
     #[inline]
     fn is_xul_element(&self) -> bool {
         self.namespace_id() == (structs::root::kNameSpaceID_XUL as i32)
@@ -810,18 +800,26 @@ impl<'le> GeckoElement<'le> {
     fn is_in_native_anonymous_subtree(&self) -> bool {
         use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
         self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0
     }
 
     /// This logic is duplicated in Gecko's nsIContent::IsInAnonymousSubtree.
     #[inline]
     fn is_in_anonymous_subtree(&self) -> bool {
-        self.is_in_native_anonymous_subtree() ||
-            (!self.as_node().is_in_shadow_tree() && self.has_xbl_binding_parent())
+        if self.is_in_native_anonymous_subtree() {
+            return true;
+        }
+
+        let binding_parent = match self.xbl_binding_parent() {
+            Some(p) => p,
+            None => return false,
+        };
+
+        binding_parent.shadow_root().is_none()
     }
 
     /// Returns true if this node is the shadow root of an use-element shadow tree.
     #[inline]
     fn is_root_of_use_element_shadow_tree(&self) -> bool {
         if !self.is_root_of_anonymous_subtree() {
             return false;
         }
--- a/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth.html
+++ b/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth.html
@@ -51,10 +51,90 @@
     window.scrollTo(0, 0);
     document.body.className = "smooth";
     window.scrollTo(0, 5000);
     assert_equals(window.scrollY, 5000, "scroll-behavior:smooth on BODY should scroll viewport instantly");
     document.body.className = "";
     window.scrollTo(0, 0);
   }, "BODY element scroll-behavior should not propagate to viewport");
 
+  var instantHistoryNavigationTest =
+    async_test("Instant scrolling while doing history navigation.");
+  var smoothHistoryNavigationTest =
+    async_test("Smooth scrolling while doing history navigation.");
+
+  function instant() {
+    document.documentElement.className = "";
+    document.body.className = "";
+    window.scrollTo(0, 0);
+    var p = document.createElement("pre");
+    p.textContent = new Array(1000).join("newline\n");
+    var a = document.createElement("a");
+    a.href = "#";
+    a.name = "foo";
+    a.textContent = "foo";
+    p.appendChild(a);
+    document.body.appendChild(p);
+    window.onhashchange = function() {
+      window.onhashchange = function() {
+        instantHistoryNavigationTest.step(function() {
+          assert_equals(location.hash, "", "Shouldn't be scrolled to a fragment.");
+          assert_equals(window.scrollY, 0, "Shouldn't be scrolled back to top yet.");
+        });
+        p.remove();
+        instantHistoryNavigationTest.done();
+        smooth();
+      }
+
+      instantHistoryNavigationTest.step(function() {
+        assert_equals(location.hash, "#foo", "Should be scrolled to a fragment.");
+        assert_not_equals(window.scrollY, 0, "Shouldn't be scrolled to top anymore.");
+      });
+      history.back();
+    }
+
+    instantHistoryNavigationTest.step(function() {
+      assert_equals(window.scrollY, 0, "Should be scrolled to top.");
+      assert_equals(location.hash, "", "Shouldn't be scrolled to a fragment.");
+    });
+    location.hash = "foo";
+  };
+  instant();
+
+  function smooth() {
+    document.documentElement.className = "";
+    document.body.className = "";
+    window.scrollTo(0, 0);
+    var p = document.createElement("pre");
+    p.textContent = new Array(1000).join("newline\n");
+    var a = document.createElement("a");
+    a.href = "#";
+    a.name = "bar";
+    a.textContent = "bar";
+    p.appendChild(a);
+    document.body.appendChild(p);
+    window.onhashchange = function() {
+      window.onhashchange = function() {
+        smoothHistoryNavigationTest.step(function() {
+          assert_equals(location.hash, "", "Shouldn't be scrolled to a fragment.");
+          assert_not_equals(window.scrollY, 0, "Shouldn't be scrolled back to top yet.");
+        });
+        p.remove();
+        smoothHistoryNavigationTest.done();
+      }
+
+      smoothHistoryNavigationTest.step(function() {
+        assert_equals(location.hash, "#bar", "Should be scrolled to a fragment.");
+        assert_not_equals(window.scrollY, 0, "Shouldn't be scrolled to top anymore.");
+      });
+      history.back();
+    }
+
+    smoothHistoryNavigationTest.step(function() {
+      assert_equals(window.scrollY, 0, "Should be scrolled to top.");
+      assert_equals(location.hash, "", "Shouldn't be scrolled to a fragment.");
+    });
+    location.hash = "bar";
+    document.documentElement.className = "smooth";
+  };
+
   testContainer.style.display = "none";
 </script>
--- a/testing/web-platform/tests/dom/lists/DOMTokenList-coverage-for-attributes.html
+++ b/testing/web-platform/tests/dom/lists/DOMTokenList-coverage-for-attributes.html
@@ -5,33 +5,38 @@
 <script src="/resources/testharnessreport.js"></script>
 <div id=log></div>
 <script>
 "use strict";
 
 var pairs = [
   // Defined in DOM
   {attr: "classList", sup: ["anyElement"]},
+  // Defined in HTML except for a which is also SVG
+  {attr: "relList", sup: ["a", "area", "link"]},
   // Defined in HTML
   {attr: "htmlFor", sup: ["output"]},
-  {attr: "relList", sup: ["a", "area", "link"]},
   {attr: "sandbox", sup: ["iframe"]},
   {attr: "sizes", sup: ["link"]}
 ];
 var namespaces = [
   "http://www.w3.org/1999/xhtml",
   "http://www.w3.org/2000/svg",
   "http://www.w3.org/1998/Math/MathML",
   "http://example.com/",
   ""
 ];
 
 var elements = ["a", "area", "link", "iframe", "output", "td", "th"];
 function testAttr(pair, new_el){
-  return (pair.attr === "classList" || (new_el.namespaceURI === "http://www.w3.org/1999/xhtml" && pair.sup.indexOf(new_el.localName) != -1));
+  return (pair.attr === "classList" ||
+          (pair.attr === "relList" && new_el.localName === "a" &&
+           new_el.namespaceURI === "http://www.w3.org/2000/svg") ||
+          (new_el.namespaceURI === "http://www.w3.org/1999/xhtml" &&
+           pair.sup.indexOf(new_el.localName) != -1));
 }
 
 pairs.forEach(function(pair) {
   namespaces.forEach(function(ns) {
     elements.forEach(function(el) {
       var new_el = document.createElementNS(ns, el);
       if (testAttr(pair, new_el)) {
         test(function() {
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -67,17 +67,16 @@ skip-if = os == "android"
 tags = blocklist
 [test_blocklist_url_ping_count.js]
 tags = blocklist
 [test_blocklistchange.js]
 # Times out during parallel runs on desktop
 requesttimeoutfactor = 2
 tags = blocklist
 [test_bootstrap.js]
-skip-if = true # Bug 1358846 Bug 1365021 Bug 676992
 [test_bootstrap_const.js]
 [test_bootstrap_globals.js]
 [test_bootstrapped_chrome_manifest.js]
 [test_cache_certdb.js]
 run-if = addon_signing
 [test_cacheflush.js]
 [test_childprocess.js]
 [test_compatoverrides.js]
--- a/xpcom/base/nsCRTGlue.h
+++ b/xpcom/base/nsCRTGlue.h
@@ -130,37 +130,24 @@ constexpr bool
 NS_ConstExprIsAscii(const char* aString, uint32_t aLength)
 {
   return aLength == 0 ? true :
     !NS_IsAscii(*aString) ? false :
     NS_ConstExprIsAscii(aString + 1, aLength - 1);
 }
 
 constexpr bool
-NS_IsAsciiAlpha(char16_t aChar)
-{
-  return (aChar >= 'A' && aChar <= 'Z') ||
-         (aChar >= 'a' && aChar <= 'z');
-}
-
-constexpr bool
 NS_IsAsciiWhitespace(char16_t aChar)
 {
   return aChar == ' ' ||
          aChar == '\r' ||
          aChar == '\n' ||
          aChar == '\t';
 }
 
-constexpr bool
-NS_IsAsciiDigit(char16_t aChar)
-{
-  return aChar >= '0' && aChar <= '9';
-}
-
 #ifndef XPCOM_GLUE_AVOID_NSPR
 void NS_MakeRandomString(char* aBuf, int32_t aBufLen);
 #endif
 
 #define FF '\f'
 #define TAB '\t'
 
 #define CRSTR "\015"
--- a/xpcom/ds/nsCRT.h
+++ b/xpcom/ds/nsCRT.h
@@ -91,18 +91,16 @@ public:
   static char ToUpper(char aChar) { return NS_ToUpper(aChar); }
   static char ToLower(char aChar) { return NS_ToLower(aChar); }
 
   static bool IsUpper(char aChar) { return NS_IsUpper(aChar); }
   static bool IsLower(char aChar) { return NS_IsLower(aChar); }
 
   static bool IsAscii(char16_t aChar) { return NS_IsAscii(aChar); }
   static bool IsAscii(const char16_t* aString) { return NS_IsAscii(aString); }
-  static bool IsAsciiAlpha(char16_t aChar) { return NS_IsAsciiAlpha(aChar); }
-  static bool IsAsciiDigit(char16_t aChar) { return NS_IsAsciiDigit(aChar); }
   static bool IsAsciiSpace(char16_t aChar) { return NS_IsAsciiWhitespace(aChar); }
   static bool IsAscii(const char* aString) { return NS_IsAscii(aString); }
   static bool IsAscii(const char* aString, uint32_t aLength)
   {
     return NS_IsAscii(aString, aLength);
   }
 };