Bug 1454590 - Align overrideMIMEType with the XMLHttpRequest Standard. r=hsivonen
authorThomas Wisniewski <wisniewskit@gmail.com>
Mon, 13 Aug 2018 16:12:10 -0400
changeset 431717 55a9d10ee43254e3cb5d694fc434f5ca01e1c137
parent 431623 27bd852b023ea5ee93af47e434c90a9518fc3d87
child 431718 c9d65d413d0660beb01bea01c9eff47b00b0705d
push id106519
push userbtara@mozilla.com
push dateWed, 15 Aug 2018 17:58:53 +0000
treeherdermozilla-inbound@0fd6d03ce4a1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsivonen
bugs1454590
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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
-