Bug 1454590 - Align overrideMIMEType with the XMLHttpRequest Standard. r?hsivonen draft
authorThomas Wisniewski <wisniewskit@gmail.com>
Mon, 13 Aug 2018 16:12:10 -0400
changeset 829289 9741efd071fdc5001a39a33bc4d34f6b9a7ab1ab
parent 829275 8546719c58dc2310bdafb81fd64a6295c59cd10e
push id118758
push userwisniewskit@gmail.com
push dateWed, 15 Aug 2018 06:15:32 +0000
reviewershsivonen
bugs1454590
milestone63.0a1
Bug 1454590 - Align overrideMIMEType with the XMLHttpRequest Standard. r?hsivonen MozReview-Commit-ID: 5k34F96jRPX
dom/base/MimeType.cpp
dom/base/MimeType.h
dom/base/moz.build
dom/base/test/gtest/TestMimeType.cpp
dom/base/test/gtest/moz.build
dom/xhr/XMLHttpRequestMainThread.cpp
dom/xhr/XMLHttpRequestMainThread.h
intl/unicharutil/util/nsUnicharUtils.cpp
intl/unicharutil/util/nsUnicharUtils.h
mfbt/TextUtils.h
testing/web-platform/meta/xhr/overridemimetype-blob.html.ini
new file mode 100644
--- /dev/null
+++ b/dom/base/MimeType.cpp
@@ -0,0 +1,256 @@
+#include "MimeType.h"
+#include "nsUnicharUtils.h"
+
+namespace {
+  static inline bool IsHTTPTokenPoint(const char16_t c) {
+    return c == '!' || c == '#' || c == '$' || c == '%' || c == '&' ||
+           c == '\'' || c == '*' || c == '+' || c == '-' || c == '.' ||
+           c == '^' || c == '_' || c == '`' || c == '|' || c == '~' ||
+           mozilla::IsAsciiAlphanumeric(c);
+  }
+
+  static inline bool IsHTTPQuotedStringTokenPoint(const char16_t c) {
+    return c == 0x9 || (c >= ' ' && c <= '~') || (c >= 0x80 && c <= 0xFF);
+  }
+}
+
+/* static */ mozilla::UniquePtr<MimeType>
+MimeType::Parse(const nsAString& aMimeType)
+{
+  // See https://mimesniff.spec.whatwg.org/#parsing-a-mime-type
+
+  // Steps 1-2
+  const char16_t* pos = aMimeType.BeginReading();
+  const char16_t* end = aMimeType.EndReading();
+  while (pos < end && mozilla::IsAsciiWhitespace(*pos)) {
+    ++pos;
+  }
+  if (pos == end) {
+    return nullptr;
+  }
+  while (end > pos && mozilla::IsAsciiWhitespace(*(end - 1))) {
+    --end;
+  }
+
+  // Steps 3-4
+  const char16_t* typeStart = pos;
+  while (pos < end && *pos != '/') {
+    if (!IsHTTPTokenPoint(*pos)) {
+      return nullptr;
+    }
+    ++pos;
+  }
+  const char16_t* typeEnd = pos;
+  if (typeStart == typeEnd) {
+    return nullptr;
+  }
+
+  // Step 5
+  if (pos == end) {
+    return nullptr;
+  }
+
+  // Step 6
+  ++pos;
+
+  // Step 7-9
+  const char16_t* subtypeStart = pos;
+  const char16_t* subtypeEnd = nullptr;
+  while (pos < end && *pos != ';') {
+    if (!IsHTTPTokenPoint(*pos)) {
+      // If we hit a whitespace, check that the rest of
+      // the subtype is whitespace, otherwise fail.
+      if (mozilla::IsAsciiWhitespace(*pos)) {
+        subtypeEnd = pos;
+        ++pos;
+        while (pos < end && *pos != ';') {
+          if (!mozilla::IsAsciiWhitespace(*pos)) {
+            return nullptr;
+          }
+          ++pos;
+        }
+        break;
+      } else {
+        return nullptr;
+      }
+    }
+    ++pos;
+  }
+  if (subtypeEnd == nullptr) {
+    subtypeEnd = pos;
+  }
+  if (subtypeStart == subtypeEnd) {
+    return nullptr;
+  }
+
+  // Step 10
+  nsString type;
+  nsString subtype;
+  for (const char16_t* c = typeStart; c < typeEnd; ++c) {
+    type.Append(ToLowerCaseASCII(*c));
+  }
+  for (const char16_t* c = subtypeStart; c < subtypeEnd; ++c) {
+    subtype.Append(ToLowerCaseASCII(*c));
+  }
+  mozilla::UniquePtr<MimeType> mimeType(mozilla::MakeUnique<MimeType>(type, subtype));
+
+  // Step 11
+  while (pos < end) {
+    // Step 11.1
+    ++pos;
+
+    // Step 11.2
+    while (pos < end && mozilla::IsAsciiWhitespace(*pos)) {
+      ++pos;
+    }
+
+    // Steps 11.3 and 11.4
+    nsString paramName;
+    bool paramNameHadInvalidChars = false;
+    while (pos < end && *pos != ';' && *pos != '=') {
+      if (!IsHTTPTokenPoint(*pos)) {
+        paramNameHadInvalidChars = true;
+      }
+      paramName.Append(ToLowerCaseASCII(*pos));
+      ++pos;
+    }
+
+    // Step 11.5
+    if (pos < end) {
+      if (*pos == ';') {
+        continue;
+      }
+      ++pos;
+    }
+
+    // Step 11.6
+    ParameterValue paramValue;
+    bool paramValueHadInvalidChars = false;
+
+    // Step 11.7
+    if (pos < end) {
+
+      // Step 11.7.1
+      if (*pos == '"') {
+
+        // Step 11.7.1.1
+        ++pos;
+
+        // Step 11.7.1.2
+        while (true) {
+
+          // Step 11.7.1.2.1
+          while (pos < end && *pos != '"' && *pos != '\\') {
+            if (!IsHTTPQuotedStringTokenPoint(*pos)) {
+              paramValueHadInvalidChars = true;
+            }
+            if (!IsHTTPTokenPoint(*pos)) {
+              paramValue.mRequiresQuoting = true;
+            }
+            paramValue.Append(*pos);
+            ++pos;
+          }
+
+          // Step 11.7.1.2.2
+          if (pos < end && *pos == '\\') {
+            // Step 11.7.1.2.2.1
+            ++pos;
+
+            // Step 11.7.1.2.2.2
+            if (pos < end) {
+              if (!IsHTTPQuotedStringTokenPoint(*pos)) {
+                paramValueHadInvalidChars = true;
+              }
+              if (!IsHTTPTokenPoint(*pos)) {
+                paramValue.mRequiresQuoting = true;
+              }
+              paramValue.Append(*pos);
+              ++pos;
+              continue;
+            }
+
+            // Step 11.7.1.2.2.3
+            paramValue.Append('\\');
+            paramValue.mRequiresQuoting = true;
+            break;
+          } else {
+            // Step 11.7.1.2.3
+            break;
+          }
+        }
+
+        // Step 11.7.1.3
+        while (pos < end && *pos != ';') {
+          ++pos;
+        }
+
+      } else {
+
+        const char16_t* paramValueStart = pos;
+
+        // Step 11.7.2.1
+        while (pos < end && *pos != ';') {
+          if (!IsHTTPQuotedStringTokenPoint(*pos)) {
+            paramValueHadInvalidChars = true;
+          }
+          if (!IsHTTPTokenPoint(*pos)) {
+            paramValue.mRequiresQuoting = true;
+          }
+          ++pos;
+        }
+
+        // Step 11.7.2.2
+        const char16_t* paramValueEnd = pos - 1;
+        while (paramValueEnd >= paramValueStart &&
+               mozilla::IsAsciiWhitespace(*paramValueEnd)) {
+          --paramValueEnd;
+        }
+
+        for (const char16_t* c = paramValueStart; c <= paramValueEnd; ++c) {
+          paramValue.Append(*c);
+        }
+      }
+
+      // Step 11.8
+      if (!paramName.IsEmpty() && !paramValue.IsEmpty() &&
+          !paramNameHadInvalidChars && !paramValueHadInvalidChars &&
+          !mimeType->mParameters.Get(paramName, &paramValue)) {
+        mimeType->mParameters.Put(paramName, paramValue);
+        mimeType->mParameterNames.AppendElement(paramName);
+      }
+    }
+  }
+
+  return mimeType;
+}
+
+void
+MimeType::Serialize(nsAString& aOutput) const
+{
+  aOutput.Assign(mType);
+  aOutput.AppendLiteral("/");
+  aOutput.Append(mSubtype);
+  for (uint32_t i = 0; i < mParameterNames.Length(); i++) {
+    auto name = mParameterNames[i];
+    ParameterValue value;
+    mParameters.Get(name, &value);
+    aOutput.AppendLiteral(";");
+    aOutput.Append(name);
+    aOutput.AppendLiteral("=");
+    if (value.mRequiresQuoting) {
+      aOutput.AppendLiteral("\"");
+      const char16_t* vcur = value.BeginReading();
+      const char16_t* vend = value.EndReading();
+      while (vcur < vend) {
+        if (*vcur == '"' || *vcur == '\\') {
+          aOutput.AppendLiteral("\\");
+        }
+        aOutput.Append(*vcur);
+        vcur++;
+      }
+      aOutput.AppendLiteral("\"");
+    } else {
+      aOutput.Append(value);
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/MimeType.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_MimeType_h
+#define mozilla_dom_MimeType_h
+
+#include "mozilla/TextUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "nsDataHashtable.h"
+#include "nsTArray.h"
+
+class MimeType final
+{
+private:
+  class ParameterValue : public nsString
+  {
+  public:
+    bool mRequiresQuoting;
+
+    ParameterValue()
+      : mRequiresQuoting(false)
+    {}
+  };
+
+  nsString mType;
+  nsString mSubtype;
+  nsDataHashtable<nsStringHashKey, ParameterValue> mParameters;
+  nsTArray<nsString> mParameterNames;
+
+public:
+  MimeType(const nsAString& aType, const nsAString& aSubtype)
+    : mType(aType), mSubtype(aSubtype)
+  {}
+
+  static mozilla::UniquePtr<MimeType> Parse(const nsAString& aStr);
+  void Serialize(nsAString& aStr) const;
+};
+
+#endif // mozilla_dom_MimeType_h
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -189,16 +189,17 @@ EXPORTS.mozilla.dom += [
     'ImageTracker.h',
     'IntlUtils.h',
     'Link.h',
     'Location.h',
     'MessageBroadcaster.h',
     'MessageListenerManager.h',
     'MessageManagerGlobal.h',
     'MessageSender.h',
+    'MimeType.h',
     'MozQueryInterface.h',
     'NameSpaceConstants.h',
     'Navigator.h',
     'NodeInfo.h',
     'NodeInfoInlines.h',
     'NodeIterator.h',
     'ParentProcessMessageManager.h',
     'PlacesEvent.h',
@@ -283,16 +284,17 @@ UNIFIED_SOURCES += [
     'InProcessTabChildMessageManager.cpp',
     'IntlUtils.cpp',
     'Link.cpp',
     'Location.cpp',
     'MessageBroadcaster.cpp',
     'MessageListenerManager.cpp',
     'MessageManagerGlobal.cpp',
     'MessageSender.cpp',
+    'MimeType.cpp',
     'MozQueryInterface.cpp',
     'Navigator.cpp',
     'NodeInfo.cpp',
     'NodeIterator.cpp',
     'NodeUbiReporting.cpp',
     'nsAttrValue.cpp',
     'nsAttrValueOrString.cpp',
     'nsCCUncollectableMarker.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/base/test/gtest/TestMimeType.cpp
@@ -0,0 +1,708 @@
+/* -*- 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 "gtest/gtest.h"
+
+#include "MimeType.h"
+#include "nsString.h"
+
+using mozilla::UniquePtr;
+
+TEST(MimeType, EmptyString)
+{
+  const auto in = NS_LITERAL_STRING("");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Empty string";
+}
+
+TEST(MimeType, JustWhitespace)
+{
+  const auto in = NS_LITERAL_STRING(" \t\r\n ");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Just whitespace";
+}
+
+TEST(MimeType, JustBackslash)
+{
+  const auto in = NS_LITERAL_STRING("\\");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Just backslash";
+}
+
+TEST(MimeType, JustForwardslash)
+{
+  const auto in = NS_LITERAL_STRING("/");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Just forward slash";
+}
+
+TEST(MimeType, MissingType1)
+{
+  const auto in = NS_LITERAL_STRING("/bogus");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Missing type #1";
+}
+
+TEST(MimeType, MissingType2)
+{
+  const auto in = NS_LITERAL_STRING(" \r\n\t/bogus");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Missing type #2";
+}
+
+TEST(MimeType, MissingSubtype1)
+{
+  const auto in = NS_LITERAL_STRING("bogus");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Missing subtype #1";
+}
+
+TEST(MimeType, MissingSubType2)
+{
+  const auto in = NS_LITERAL_STRING("bogus/");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Missing subtype #2";
+}
+
+TEST(MimeType, MissingSubType3)
+{
+  const auto in = NS_LITERAL_STRING("bogus;");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Missing subtype #3";
+}
+
+TEST(MimeType, MissingSubType4)
+{
+  const auto in = NS_LITERAL_STRING("bogus; \r\n\t");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Missing subtype #3";
+}
+
+TEST(MimeType, ExtraForwardSlash)
+{
+  const auto in = NS_LITERAL_STRING("bogus/bogus/;");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Extra forward slash";
+}
+
+TEST(MimeType, WhitespaceInType)
+{
+  const auto in = NS_LITERAL_STRING("t\re\nx\tt /html");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Type with whitespace";
+}
+
+TEST(MimeType, WhitespaceInSubtype)
+{
+  const auto in = NS_LITERAL_STRING("text/ h\rt\nm\tl");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Subtype with whitespace";
+}
+
+TEST(MimeType, NonAlphanumericMediaType1)
+{
+  const auto in = NS_LITERAL_STRING("</>");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Non-alphanumeric media type #1";
+}
+
+TEST(MimeType, NonAlphanumericMediaType2)
+{
+  const auto in = NS_LITERAL_STRING("(/)");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Non-alphanumeric media type #2";
+}
+
+TEST(MimeType, NonAlphanumericMediaType3)
+{
+  const auto in = NS_LITERAL_STRING("{/}");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Non-alphanumeric media type #3";
+}
+
+TEST(MimeType, NonAlphanumericMediaType4)
+{
+  const auto in = NS_LITERAL_STRING("\"/\"");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Non-alphanumeric media type #4";
+}
+
+TEST(MimeType, NonAlphanumericMediaType5)
+{
+  const auto in = NS_LITERAL_STRING("\0/\0");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Non-alphanumeric media type #5";
+}
+
+TEST(MimeType, NonAlphanumericMediaType6)
+{
+  const auto in = NS_LITERAL_STRING("text/html(;doesnot=matter");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Non-alphanumeric media type #6";
+}
+
+TEST(MimeType, NonLatin1MediaType1)
+{
+  const auto in = NS_LITERAL_STRING("ÿ/ÿ");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Non-latin1 media type #1";
+}
+
+TEST(MimeType, NonLatin1MediaType2)
+{
+  const auto in = NS_LITERAL_STRING("\x0100/\x0100");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_FALSE(parsed) <<
+    "Non-latin1 media type #2";
+}
+
+TEST(MimeType, MultipleParameters)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=gbk;no=1;charset_=gbk_;yes=2");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.Equals(NS_LITERAL_STRING("text/html;charset=gbk;no=1;charset_=gbk_;yes=2"))) <<
+    "Multiple parameters";
+}
+
+TEST(MimeType, DuplicateParameter1)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=gbk;charset=windows-1255");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.Equals(NS_LITERAL_STRING("text/html;charset=gbk"))) <<
+    "Duplicate parameter #1";
+}
+
+TEST(MimeType, DuplicateParameter2)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=();charset=GBK");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.Equals(NS_LITERAL_STRING("text/html;charset=\"()\""))) <<
+    "Duplicate parameter #2";
+}
+
+TEST(MimeType, NonAlphanumericParametersAreQuoted)
+{
+  const auto in = NS_LITERAL_STRING("text/html;test=\x00FF\\;charset=gbk");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.Equals(NS_LITERAL_STRING("text/html;test=\"\x00FF\\\\\";charset=gbk"))) <<
+    "Non-alphanumeric parameters are quoted";
+}
+
+TEST(MimeType, ParameterQuotedIfHasLeadingWhitespace1)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset= g\\\"bk");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" g\\\\\\\"bk\"")) <<
+    "Parameter is quoted if has leading whitespace #1";
+}
+
+TEST(MimeType, ParameterQuotedIfHasLeadingWhitespace2)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset= \"g\\bk\"");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" \\\"g\\\\bk\\\"\"")) <<
+    "Parameter is quoted if has leading whitespace #2";
+}
+
+TEST(MimeType, ParameterQuotedIfHasInternalWhitespace)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=g \\b\"k");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"g \\\\b\\\"k\"")) <<
+    "Parameter is quoted if has internal whitespace";
+}
+
+TEST(MimeType, ImproperlyQuotedParameter1)
+{
+  const auto in = NS_LITERAL_STRING("x/x;test=\"");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("x/x")) <<
+    "Improperly-quoted parameter is handled properly #1";
+}
+
+TEST(MimeType, ImproperlyQuotedParameter2)
+{
+  const auto in = NS_LITERAL_STRING("x/x;test=\"\\");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("x/x;test=\"\\\\\"")) <<
+    "Improperly-quoted parameter is handled properly #2";
+}
+
+TEST(MimeType, NonLatin1ParameterIgnored)
+{
+  const auto in = NS_LITERAL_STRING("x/x;test=\xFFFD;x=x");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("x/x;x=x")) <<
+    "Non latin-1 parameters are ignored";
+}
+
+TEST(MimeType, ParameterIgnoredIfWhitespaceInName1)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset =gbk;charset=123");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=123")) <<
+    "Parameter ignored if whitespace in name #1";
+}
+
+TEST(MimeType, ParameterIgnoredIfWhitespaceInName2)
+{
+  const auto in = NS_LITERAL_STRING("text/html;cha rset =gbk;charset=123");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=123")) <<
+    "Parameter ignored if whitespace in name #2";
+}
+
+TEST(MimeType, WhitespaceTrimmed)
+{
+  const auto in = NS_LITERAL_STRING("\n\r\t  text/plain\n\r\t  ;\n\r\t  charset=123\n\r\t ");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/plain;charset=123")) <<
+    "Whitespace appropriately ignored";
+}
+
+TEST(MimeType, WhitespaceOnlyParameterIgnored)
+{
+  const auto in = NS_LITERAL_STRING("x/x;x= \r\n\t");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("x/x")) <<
+    "Whitespace-only parameter is ignored";
+}
+
+TEST(MimeType, IncompleteParameterIgnored1)
+{
+  const auto in = NS_LITERAL_STRING("x/x;test");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("x/x")) <<
+    "Incomplete parameter is ignored #1";
+}
+
+TEST(MimeType, IncompleteParameterIgnored2)
+{
+  const auto in = NS_LITERAL_STRING("x/x;test=");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("x/x")) <<
+    "Incomplete parameter is ignored #2";
+}
+
+TEST(MimeType, IncompleteParameterIgnored3)
+{
+  const auto in = NS_LITERAL_STRING("x/x;test= \r\n\t");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("x/x")) <<
+    "Incomplete parameter is ignored #3";
+}
+
+TEST(MimeType, IncompleteParameterIgnored4)
+{
+  const auto in = NS_LITERAL_STRING("text/html;test;charset=gbk");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) <<
+    "Incomplete parameter is ignored #4";
+}
+
+TEST(MimeType, IncompleteParameterIgnored5)
+{
+  const auto in = NS_LITERAL_STRING("text/html;test=;charset=gbk");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) <<
+    "Incomplete parameter is ignored #5";
+}
+
+TEST(MimeType, EmptyParameterIgnored1)
+{
+  const auto in = NS_LITERAL_STRING("text/html ; ; charset=gbk");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) <<
+    "Empty parameter ignored #1";
+}
+
+TEST(MimeType, EmptyParameterIgnored2)
+{
+  const auto in = NS_LITERAL_STRING("text/html;;;;charset=gbk");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) <<
+    "Empty parameter ignored #2";
+}
+
+TEST(MimeType, InvalidParameterIgnored1)
+{
+  const auto in = NS_LITERAL_STRING("text/html;';charset=gbk");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) <<
+    "Invalid parameter ignored #1";
+}
+
+TEST(MimeType, InvalidParameterIgnored2)
+{
+  const auto in = NS_LITERAL_STRING("text/html;\";charset=gbk;=123; =321");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) <<
+    "Invalid parameter ignored #2";
+}
+
+TEST(MimeType, InvalidParameterIgnored3)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset= \"\u007F;charset=GBK");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=GBK")) <<
+    "Invalid parameter ignored #3";
+}
+
+TEST(MimeType, InvalidParameterIgnored4)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=\"\u007F;charset=foo\";charset=GBK;charset=");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=GBK")) <<
+    "Invalid parameter ignored #4";
+}
+
+TEST(MimeType, SingleQuotes1)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset='gbk'");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset='gbk'")) <<
+    "Single quotes handled properly #1";
+}
+
+TEST(MimeType, SingleQuotes2)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset='gbk");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset='gbk")) <<
+    "Single quotes handled properly #2";
+}
+
+TEST(MimeType, SingleQuotes3)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=gbk'");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk'")) <<
+    "Single quotes handled properly #3";
+}
+
+TEST(MimeType, SingleQuotes4)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=';charset=GBK");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset='")) <<
+    "Single quotes handled properly #4";
+}
+
+TEST(MimeType, SingleQuotes5)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=''';charset=GBK");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset='''")) <<
+    "Single quotes handled properly #5";
+}
+
+TEST(MimeType, DoubleQuotes1)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=\"gbk\"");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) <<
+    "Double quotes handled properly #1";
+}
+
+TEST(MimeType, DoubleQuotes2)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=\"gbk");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) <<
+    "Double quotes handled properly #2";
+}
+
+TEST(MimeType, DoubleQuotes3)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=gbk\"");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"gbk\\\"\"")) <<
+    "Double quotes handled properly #3";
+}
+
+TEST(MimeType, DoubleQuotes4)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=\" gbk\"");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" gbk\"")) <<
+    "Double quotes handled properly #4";
+}
+
+TEST(MimeType, DoubleQuotes5)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=\"gbk \"");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"gbk \"")) <<
+    "Double quotes handled properly #5";
+}
+
+TEST(MimeType, DoubleQuotes6)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=\"\\ gbk\"");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\" gbk\"")) <<
+    "Double quotes handled properly #6";
+}
+
+TEST(MimeType, DoubleQuotes7)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=\"\\g\\b\\k\"");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) <<
+    "Double quotes handled properly #7";
+}
+
+TEST(MimeType, DoubleQuotes8)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=\"gbk\"x");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=gbk")) <<
+    "Double quotes handled properly #8";
+}
+
+TEST(MimeType, DoubleQuotes9)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=\"\";charset=GBK");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=GBK")) <<
+    "Double quotes handled properly #9";
+}
+
+TEST(MimeType, DoubleQuotes10)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=\";charset=GBK");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\";charset=GBK\"")) <<
+    "Double quotes handled properly #10";
+}
+
+TEST(MimeType, UnexpectedCodePoints)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset={gbk}");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"{gbk}\"")) <<
+    "Unexpected code points handled properly";
+}
+
+TEST(MimeType, LongTypesSubtypesAccepted)
+{
+  const auto in = NS_LITERAL_STRING("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.Equals(in)) <<
+    "Long type/subtype accepted";
+}
+
+TEST(MimeType, LongParametersAccepted)
+{
+  const auto in = NS_LITERAL_STRING("text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.Equals(in)) <<
+    "Long parameters accepted";
+}
+
+TEST(MimeType, AllValidCharactersAccepted1)
+{
+  const auto in = NS_LITERAL_STRING("x/x;x=\"\t !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F\u00A0\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB\u00AC\u00AD\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF\"");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.Equals(in)) <<
+    "All valid characters accepted #1";
+}
+
+TEST(MimeType, CaseNormalization1)
+{
+  const auto in = NS_LITERAL_STRING("TEXT/PLAIN;CHARSET=TEST");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/plain;charset=TEST")) <<
+    "Case normalized properly #1";
+}
+
+TEST(MimeType, CaseNormalization2)
+{
+  const auto in = NS_LITERAL_STRING("!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")) <<
+    "Case normalized properly #2";
+}
+
+TEST(MimeType, LegacyCommentSyntax1)
+{
+  const auto in = NS_LITERAL_STRING("text/html;charset=gbk(");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;charset=\"gbk(\"")) <<
+    "Legacy comment syntax #1";
+}
+
+TEST(MimeType, LegacyCommentSyntax2)
+{
+  const auto in = NS_LITERAL_STRING("text/html;x=(;charset=gbk");
+  UniquePtr<MimeType> parsed = MimeType::Parse(in);
+  ASSERT_TRUE(parsed) << "Parsing succeeded";
+  nsAutoString out;
+  parsed->Serialize(out);
+  ASSERT_TRUE(out.EqualsLiteral("text/html;x=\"(\";charset=gbk")) <<
+    "Legacy comment syntax #2";
+}
--- a/dom/base/test/gtest/moz.build
+++ b/dom/base/test/gtest/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, you can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'TestContentUtils.cpp',
+    'TestMimeType.cpp',
     'TestPlainTextSerializer.cpp',
     'TestXPathGenerator.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base'
 ]
 
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -2167,17 +2167,21 @@ XMLHttpRequestMainThread::OnStopRequest(
   }
 
   if (NS_SUCCEEDED(status) &&
       mResponseType == XMLHttpRequestResponseType::Blob &&
       !waitingForBlobCreation) {
     // Smaller files may be written in cache map instead of separate files.
     // Also, no-store response cannot be written in persistent cache.
     nsAutoCString contentType;
-    mChannel->GetContentType(contentType);
+    if (!mOverrideMimeType.IsEmpty()) {
+      contentType.Assign(NS_ConvertUTF16toUTF8(mOverrideMimeType));
+    } else {
+      mChannel->GetContentType(contentType);
+    }
 
     // mBlobStorage can be null if the channel is non-file non-cacheable
     // and if the response length is zero.
     MaybeCreateBlobStorage();
     mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this);
     waitingForBlobCreation = true;
 
     NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
@@ -3130,17 +3134,22 @@ XMLHttpRequestMainThread::OverrideMimeTy
   NOT_CALLABLE_IN_SYNC_SEND_RV
 
   if (mState == XMLHttpRequest_Binding::LOADING ||
       mState == XMLHttpRequest_Binding::DONE) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
     return;
   }
 
-  mOverrideMimeType = aMimeType;
+  UniquePtr<MimeType> parsed = MimeType::Parse(aMimeType);
+  if (parsed) {
+    parsed->Serialize(mOverrideMimeType);
+  } else {
+    mOverrideMimeType.AssignLiteral(APPLICATION_OCTET_STREAM);
+  }
 }
 
 bool
 XMLHttpRequestMainThread::MozBackgroundRequest() const
 {
   return mFlagBackgroundRequest;
 }
 
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -34,16 +34,17 @@
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/dom/MutableBlobStorage.h"
 #include "mozilla/dom/BodyExtractor.h"
 #include "mozilla/dom/ClientInfo.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FormData.h"
+#include "mozilla/dom/MimeType.h"
 #include "mozilla/dom/PerformanceStorage.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 #include "mozilla/dom/URLSearchParams.h"
 #include "mozilla/dom/XMLHttpRequest.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestEventTarget.h"
 #include "mozilla/dom/XMLHttpRequestString.h"
 #include "mozilla/Encoding.h"
--- a/intl/unicharutil/util/nsUnicharUtils.cpp
+++ b/intl/unicharutil/util/nsUnicharUtils.cpp
@@ -54,16 +54,43 @@ ToLowerCase(nsAString& aString)
 
 void
 ToLowerCaseASCII(nsAString& aString)
 {
   char16_t *buf = aString.BeginWriting();
   ToLowerCaseASCII(buf, buf, aString.Length());
 }
 
+char
+ToLowerCaseASCII(char aChar)
+{
+  if (aChar >= 'A' && aChar <= 'Z') {
+    return aChar + 0x20;
+  }
+  return aChar;
+}
+
+char16_t
+ToLowerCaseASCII(char16_t aChar)
+{
+  if (aChar >= 'A' && aChar <= 'Z') {
+    return aChar + 0x20;
+  }
+  return aChar;
+}
+
+char32_t
+ToLowerCaseASCII(char32_t aChar)
+{
+  if (aChar >= 'A' && aChar <= 'Z') {
+    return aChar + 0x20;
+  }
+  return aChar;
+}
+
 void
 ToLowerCase(const nsAString& aSource,
             nsAString& aDest)
 {
   const char16_t *in = aSource.BeginReading();
   uint32_t len = aSource.Length();
 
   aDest.SetLength(len);
--- a/intl/unicharutil/util/nsUnicharUtils.h
+++ b/intl/unicharutil/util/nsUnicharUtils.h
@@ -35,16 +35,20 @@ void ToUpperCase(const nsAString& aSourc
 uint32_t ToLowerCase(uint32_t aChar);
 uint32_t ToUpperCase(uint32_t aChar);
 uint32_t ToTitleCase(uint32_t aChar);
 
 void ToLowerCase(const char16_t *aIn, char16_t *aOut, uint32_t aLen);
 void ToLowerCaseASCII(const char16_t *aIn, char16_t *aOut, uint32_t aLen);
 void ToUpperCase(const char16_t *aIn, char16_t *aOut, uint32_t aLen);
 
+char ToLowerCaseASCII(const char aChar);
+char16_t ToLowerCaseASCII(const char16_t aChar);
+char32_t ToLowerCaseASCII(const char32_t aChar);
+
 inline bool IsUpperCase(uint32_t c) {
   return ToLowerCase(c) != c;
 }
 
 inline bool IsLowerCase(uint32_t c) {
   return ToUpperCase(c) != c;
 }
 
--- a/mfbt/TextUtils.h
+++ b/mfbt/TextUtils.h
@@ -43,16 +43,31 @@ constexpr bool
 IsAscii(Char aChar)
 {
   using UnsignedChar = typename detail::MakeUnsignedChar<Char>::Type;
   auto uc = static_cast<UnsignedChar>(aChar);
   return uc < 0x80;
 }
 
 /**
+ * Returns true iff |aChar| matches Ascii Whitespace.
+ *
+ * This function is intended to match the Infra standard
+ * (https://infra.spec.whatwg.org/#ascii-whitespace)
+ */
+template<typename Char>
+constexpr bool
+IsAsciiWhitespace(Char aChar)
+{
+  using UnsignedChar = typename detail::MakeUnsignedChar<Char>::Type;
+  auto uc = static_cast<UnsignedChar>(aChar);
+  return uc == 0x9 || uc == 0xA || uc == 0xC || uc == 0xD || uc == 0x20;
+}
+
+/**
  * 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)
deleted file mode 100644
--- a/testing/web-platform/meta/xhr/overridemimetype-blob.html.ini
+++ /dev/null
@@ -1,286 +0,0 @@
-[overridemimetype-blob.html]
-  [Bogus MIME type should end up as application/octet-stream, 2]
-    expected: FAIL
-
-  [Bogus MIME type should end up as application/octet-stream, 3]
-    expected: FAIL
-
-  [Bogus MIME type should end up as application/octet-stream, 4]
-    expected: FAIL
-
-  [Bogus MIME type should end up as application/octet-stream, 5]
-    expected: FAIL
-
-  [Bogus MIME type should end up as application/octet-stream, 6]
-    expected: FAIL
-
-  [Bogus MIME type should end up as application/octet-stream, 7]
-    expected: FAIL
-
-  [Bogus MIME type should end up as application/octet-stream, 9]
-    expected: FAIL
-
-  [Valid MIME types need to be parsed and serialized 1]
-    expected: FAIL
-
-  [Valid MIME types need to be parsed and serialized 4]
-    expected: FAIL
-
-  [Valid MIME types need to be parsed and serialized 5]
-    expected: FAIL
-
-  [1) MIME types need to be parsed and serialized: text/html;charset=gbk]
-    expected: FAIL
-
-  [2) MIME types need to be parsed and serialized: TEXT/HTML;CHARSET=GBK]
-    expected: FAIL
-
-  [3) MIME types need to be parsed and serialized: text/html;charset=gbk(]
-    expected: FAIL
-
-  [4) MIME types need to be parsed and serialized: text/html;x=(;charset=gbk]
-    expected: FAIL
-
-  [5) MIME types need to be parsed and serialized: text/html;charset=gbk;charset=windows-1255]
-    expected: FAIL
-
-  [7) MIME types need to be parsed and serialized: text/html ;charset=gbk]
-    expected: FAIL
-
-  [8) MIME types need to be parsed and serialized: text/html; charset=gbk]
-    expected: FAIL
-
-  [9) MIME types need to be parsed and serialized: text/html;charset= gbk]
-    expected: FAIL
-
-  [10) MIME types need to be parsed and serialized: text/html;charset='gbk']
-    expected: FAIL
-
-  [11) MIME types need to be parsed and serialized: text/html;charset='gbk]
-    expected: FAIL
-
-  [12) MIME types need to be parsed and serialized: text/html;charset=gbk']
-    expected: FAIL
-
-  [13) MIME types need to be parsed and serialized: text/html;test;charset=gbk]
-    expected: FAIL
-
-  [14) MIME types need to be parsed and serialized: text/html;test=;charset=gbk]
-    expected: FAIL
-
-  [15) MIME types need to be parsed and serialized: text/html;';charset=gbk]
-    expected: FAIL
-
-  [16) MIME types need to be parsed and serialized: text/html;";charset=gbk]
-    expected: FAIL
-
-  [17) MIME types need to be parsed and serialized: text/html ; ; charset=gbk]
-    expected: FAIL
-
-  [18) MIME types need to be parsed and serialized: text/html;;;;charset=gbk]
-    expected: FAIL
-
-  [19) MIME types need to be parsed and serialized: text/html;charset="gbk"]
-    expected: FAIL
-
-  [20) MIME types need to be parsed and serialized: text/html;charset="gbk]
-    expected: FAIL
-
-  [21) MIME types need to be parsed and serialized: text/html;charset=gbk"]
-    expected: FAIL
-
-  [22) MIME types need to be parsed and serialized: text/html;charset=" gbk"]
-    expected: FAIL
-
-  [23) MIME types need to be parsed and serialized: text/html;charset="\\ gbk"]
-    expected: FAIL
-
-  [24) MIME types need to be parsed and serialized: text/html;charset="\\g\\b\\k"]
-    expected: FAIL
-
-  [25) MIME types need to be parsed and serialized: text/html;charset="gbk"x]
-    expected: FAIL
-
-  [26) MIME types need to be parsed and serialized: text/html;charset={gbk}]
-    expected: FAIL
-
-  [27) MIME types need to be parsed and serialized: text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk]
-    expected: FAIL
-
-  [29) MIME types need to be parsed and serialized: !#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]
-    expected: FAIL
-
-  [30) MIME types need to be parsed and serialized: x/x;x="\t !\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"]
-    expected: FAIL
-
-  [32) MIME types need to be parsed and serialized: x/x;test="\\]
-    expected: FAIL
-
-  [35) MIME types need to be parsed and serialized: text/html;test=ÿ;charset=gbk]
-    expected: FAIL
-
-  [36) MIME types need to be parsed and serialized: x/x;test=�;x=x]
-    expected: FAIL
-
-  [37) MIME types need to be parsed and serialized: ]
-    expected: FAIL
-
-  [39) MIME types need to be parsed and serialized: /]
-    expected: FAIL
-
-  [41) MIME types need to be parsed and serialized: bogus/]
-    expected: FAIL
-
-  [42) MIME types need to be parsed and serialized: bogus/ ]
-    expected: FAIL
-
-  [43) MIME types need to be parsed and serialized: bogus/bogus/;]
-    expected: FAIL
-
-  [44) MIME types need to be parsed and serialized: </>]
-    expected: FAIL
-
-  [46) MIME types need to be parsed and serialized: ÿ/ÿ]
-    expected: FAIL
-
-  [47) MIME types need to be parsed and serialized: text/html(;doesnot=matter]
-    expected: FAIL
-
-  [48) MIME types need to be parsed and serialized: {/}]
-    expected: FAIL
-
-  [49) MIME types need to be parsed and serialized: Ā/Ā]
-    expected: FAIL
-
-  [6) MIME types need to be parsed and serialized: text/html;charset=();charset=GBK]
-    expected: FAIL
-
-  [8) MIME types need to be parsed and serialized: text/html ;charset=gbk]
-    expected: FAIL
-
-  [9) MIME types need to be parsed and serialized: text/html; charset=gbk]
-    expected: FAIL
-
-  [10) MIME types need to be parsed and serialized: text/html;charset= gbk]
-    expected: FAIL
-
-  [11) MIME types need to be parsed and serialized: text/html;charset= "gbk"]
-    expected: FAIL
-
-  [12) MIME types need to be parsed and serialized: text/html;charset='gbk']
-    expected: FAIL
-
-  [13) MIME types need to be parsed and serialized: text/html;charset='gbk]
-    expected: FAIL
-
-  [14) MIME types need to be parsed and serialized: text/html;charset=gbk']
-    expected: FAIL
-
-  [15) MIME types need to be parsed and serialized: text/html;charset=';charset=GBK]
-    expected: FAIL
-
-  [16) MIME types need to be parsed and serialized: text/html;test;charset=gbk]
-    expected: FAIL
-
-  [17) MIME types need to be parsed and serialized: text/html;test=;charset=gbk]
-    expected: FAIL
-
-  [18) MIME types need to be parsed and serialized: text/html;';charset=gbk]
-    expected: FAIL
-
-  [19) MIME types need to be parsed and serialized: text/html;";charset=gbk]
-    expected: FAIL
-
-  [20) MIME types need to be parsed and serialized: text/html ; ; charset=gbk]
-    expected: FAIL
-
-  [21) MIME types need to be parsed and serialized: text/html;;;;charset=gbk]
-    expected: FAIL
-
-  [22) MIME types need to be parsed and serialized: text/html;charset= ";charset=GBK]
-    expected: FAIL
-
-  [23) MIME types need to be parsed and serialized: text/html;charset=";charset=foo";charset=GBK]
-    expected: FAIL
-
-  [24) MIME types need to be parsed and serialized: text/html;charset="gbk"]
-    expected: FAIL
-
-  [25) MIME types need to be parsed and serialized: text/html;charset="gbk]
-    expected: FAIL
-
-  [26) MIME types need to be parsed and serialized: text/html;charset=gbk"]
-    expected: FAIL
-
-  [27) MIME types need to be parsed and serialized: text/html;charset=" gbk"]
-    expected: FAIL
-
-  [28) MIME types need to be parsed and serialized: text/html;charset="gbk "]
-    expected: FAIL
-
-  [29) MIME types need to be parsed and serialized: text/html;charset="\\ gbk"]
-    expected: FAIL
-
-  [30) MIME types need to be parsed and serialized: text/html;charset="\\g\\b\\k"]
-    expected: FAIL
-
-  [31) MIME types need to be parsed and serialized: text/html;charset="gbk"x]
-    expected: FAIL
-
-  [32) MIME types need to be parsed and serialized: text/html;charset="";charset=GBK]
-    expected: FAIL
-
-  [33) MIME types need to be parsed and serialized: text/html;charset=";charset=GBK]
-    expected: FAIL
-
-  [34) MIME types need to be parsed and serialized: text/html;charset={gbk}]
-    expected: FAIL
-
-  [35) MIME types need to be parsed and serialized: text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk]
-    expected: FAIL
-
-  [37) MIME types need to be parsed and serialized: !#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]
-    expected: FAIL
-
-  [38) MIME types need to be parsed and serialized: x/x;x="\t !\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"]
-    expected: FAIL
-
-  [40) MIME types need to be parsed and serialized: x/x;test="\\]
-    expected: FAIL
-
-  [43) MIME types need to be parsed and serialized: text/html;test=ÿ;charset=gbk]
-    expected: FAIL
-
-  [44) MIME types need to be parsed and serialized: x/x;test=�;x=x]
-    expected: FAIL
-
-  [45) MIME types need to be parsed and serialized: ]
-    expected: FAIL
-
-  [47) MIME types need to be parsed and serialized: /]
-    expected: FAIL
-
-  [49) MIME types need to be parsed and serialized: bogus/]
-    expected: FAIL
-
-  [50) MIME types need to be parsed and serialized: bogus/ ]
-    expected: FAIL
-
-  [51) MIME types need to be parsed and serialized: bogus/bogus/;]
-    expected: FAIL
-
-  [52) MIME types need to be parsed and serialized: </>]
-    expected: FAIL
-
-  [54) MIME types need to be parsed and serialized: ÿ/ÿ]
-    expected: FAIL
-
-  [55) MIME types need to be parsed and serialized: text/html(;doesnot=matter]
-    expected: FAIL
-
-  [56) MIME types need to be parsed and serialized: {/}]
-    expected: FAIL
-
-  [57) MIME types need to be parsed and serialized: Ā/Ā]
-    expected: FAIL
-