From d135e3b3c48811c577e1632a41c5c50bc55c035c Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari <ehsan@mozilla.com>
Date: Tue, 20 Oct 2015 11:40:12 +0200
Subject: [PATCH] Bug 1210302 - Add a NS_ParseRequestContentType API; ba=al, 
 r=mcmanus, r=sicking, a=al

--HG--
extra : amend_source : d93021b626709b03f6499029dc3d1813cccba386
---
 docshell/base/nsDocShell.cpp       |  2 +-
 dom/base/Navigator.cpp             |  4 +--
 dom/base/nsContentUtils.cpp        |  2 +-
 dom/html/nsHTMLDocument.cpp        |  2 +-
 dom/manifest/ManifestProcessor.jsm |  6 ++--
 netwerk/base/moz.build             |  1 +
 netwerk/base/nsINetUtil_ESR_38.idl | 14 +++++++++
 netwerk/base/nsIOService.cpp       | 12 ++++++++
 netwerk/base/nsIOService.h         |  3 ++
 netwerk/base/nsNetUtil.h           | 21 +++++++++++++
 netwerk/base/nsURLHelper.cpp       | 60 +++++++++++++++++++++++++++++++++++---
 netwerk/base/nsURLHelper.h         | 32 +++++++++++++++-----
 12 files changed, 139 insertions(+), 20 deletions(-)
 create mode 100644 netwerk/base/nsINetUtil_ESR_38.idl

diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index bcc205c..4fc7c34 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13519,7 +13519,7 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent,
     anchor->GetType(typeHint);
     NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
     nsAutoCString type, dummy;
-    NS_ParseContentType(utf8Hint, type, dummy);
+    NS_ParseRequestContentType(utf8Hint, type, dummy);
     CopyUTF8toUTF16(type, typeHint);
   }
 
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
index f4ea502..7288420 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1221,9 +1221,9 @@ Navigator::SendBeacon(const nsAString& aUrl,
   rv = secMan->CheckSameOriginURI(documentURI, uri, false);
   bool crossOrigin = NS_FAILED(rv);
   nsAutoCString contentType, parsedCharset;
-  rv = NS_ParseContentType(mimeType, contentType, parsedCharset);
+  rv = NS_ParseRequestContentType(mimeType, contentType, parsedCharset);
   if (crossOrigin &&
-      contentType.Length() > 0 &&
+      mimeType.Length() > 0 &&
       !contentType.Equals(APPLICATION_WWW_FORM_URLENCODED) &&
       !contentType.Equals(MULTIPART_FORM_DATA) &&
       !contentType.Equals(TEXT_PLAIN)) {
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 5e8dbd6..686f7bf 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7001,7 +7001,7 @@ nsContentUtils::IsAllowedNonCorsContentType(const nsACString& aHeaderValue)
   nsAutoCString contentType;
   nsAutoCString unused;
 
-  nsresult rv = NS_ParseContentType(aHeaderValue, contentType, unused);
+  nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
   if (NS_FAILED(rv)) {
     return false;
   }
diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp
index 7481109..d195792 100644
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1422,7 +1422,7 @@ nsHTMLDocument::Open(JSContext* cx,
   nsAutoString type;
   nsContentUtils::ASCIIToLower(aType, type);
   nsAutoCString actualType, dummy;
-  NS_ParseContentType(NS_ConvertUTF16toUTF8(type), actualType, dummy);
+  NS_ParseRequestContentType(NS_ConvertUTF16toUTF8(type), actualType, dummy);
   if (!actualType.EqualsLiteral("text/html") &&
       !type.EqualsLiteral("replace")) {
     contentType.AssignLiteral("text/plain");
diff --git a/dom/manifest/ManifestProcessor.jsm b/dom/manifest/ManifestProcessor.jsm
index b6df920..f16881a 100644
--- a/dom/manifest/ManifestProcessor.jsm
+++ b/dom/manifest/ManifestProcessor.jsm
@@ -31,7 +31,7 @@ const imports = {};
 Cu.import('resource://gre/modules/Services.jsm', imports);
 Cu.importGlobalProperties(['URL']);
 const securityManager = imports.Services.scriptSecurityManager;
-const netutil = Cc['@mozilla.org/network/util;1'].getService(Ci.nsINetUtil);
+const netutil = Cc['@mozilla.org/network/util;1'].getService(Ci.nsINetUtil_ESR_38);
 const defaultDisplayMode = 'browser';
 const displayModes = new Set([
   'fullscreen',
@@ -258,7 +258,7 @@ this.ManifestProcessor.prototype.process = function({
         };
       let value = extractValue(obj),
         isParsable = (typeof value === 'string' && value.length > 0);
-      value = (isParsable) ? netutil.parseContentType(value.trim(), charset, hadCharset) : undefined;
+      value = (isParsable) ? netutil.parseRequestContentType(value.trim(), charset, hadCharset) : undefined;
       return (value === '') ? undefined : value;
     }
 
@@ -354,4 +354,4 @@ this.ManifestProcessor.prototype.process = function({
   };
   processedManifest.scope = processScopeMember(manifest, manifestURL, docURL, processedManifest.start_url);
   return processedManifest;
-};
\ No newline at end of file
+};
diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build
index 877365b..deedf76 100644
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -59,6 +59,7 @@ XPIDL_SOURCES += [
     'nsINestedURI.idl',
     'nsINetAddr.idl',
     'nsINetUtil.idl',
+    'nsINetUtil_ESR_38.idl',
     'nsINetworkInterceptController.idl',
     'nsINetworkLinkService.idl',
     'nsINetworkPredictor.idl',
diff --git a/netwerk/base/nsINetUtil_ESR_38.idl b/netwerk/base/nsINetUtil_ESR_38.idl
new file mode 100644
index 0000000..7ef40e9
--- /dev/null
+++ b/netwerk/base/nsINetUtil_ESR_38.idl
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(e82f2b9d-8bac-48bb-ade7-26a7cd4fb894)]
+interface nsINetUtil_ESR_38 : nsISupports
+{
+  AUTF8String parseRequestContentType(in AUTF8String aTypeHeader,
+                                      out AUTF8String aCharset,
+                                      out boolean aHadCharset);
+};
diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp
index 83db86f..9a17e8b 100644
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -321,6 +321,7 @@ NS_IMPL_ISUPPORTS(nsIOService,
                   nsIIOService,
                   nsIIOService2,
                   nsINetUtil,
+                  nsINetUtil_ESR_38,
                   nsISpeculativeConnect,
                   nsIObserver,
                   nsISupportsWeakReference)
@@ -1280,6 +1281,17 @@ nsIOService::Observe(nsISupports *subject,
 
 // nsINetUtil interface
 NS_IMETHODIMP
+nsIOService::ParseRequestContentType(const nsACString &aTypeHeader,
+                                     nsACString &aCharset,
+                                     bool *aHadCharset,
+                                     nsACString &aContentType)
+{
+    net_ParseRequestContentType(aTypeHeader, aContentType, aCharset, aHadCharset);
+    return NS_OK;
+}
+
+// nsINetUtil interface
+NS_IMETHODIMP
 nsIOService::ParseContentType(const nsACString &aTypeHeader,
                               nsACString &aCharset,
                               bool *aHadCharset,
diff --git a/netwerk/base/nsIOService.h b/netwerk/base/nsIOService.h
index acd501c..b125709 100644
--- a/netwerk/base/nsIOService.h
+++ b/netwerk/base/nsIOService.h
@@ -14,6 +14,7 @@
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "nsINetUtil.h"
+#include "nsINetUtil_ESR_38.h"
 #include "nsIChannelEventSink.h"
 #include "nsCategoryCache.h"
 #include "nsISpeculativeConnect.h"
@@ -47,6 +48,7 @@ namespace net {
 class nsIOService final : public nsIIOService2
                             , public nsIObserver
                             , public nsINetUtil
+                            , public nsINetUtil_ESR_38
                             , public nsISpeculativeConnect
                             , public nsSupportsWeakReference
 {
@@ -56,6 +58,7 @@ public:
     NS_DECL_NSIIOSERVICE2
     NS_DECL_NSIOBSERVER
     NS_DECL_NSINETUTIL
+    NS_DECL_NSINETUTIL_ESR_38
     NS_DECL_NSISPECULATIVECONNECT
 
     // Gets the singleton instance of the IO Service, creating it as needed
diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h
index ec69716..df8874c 100644
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -56,6 +56,7 @@
 #include "nsISyncStreamListener.h"
 #include "nsInterfaceRequestorAgg.h"
 #include "nsINetUtil.h"
+#include "nsINetUtil_ESR_38.h"
 #include "nsIURIWithPrincipal.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
@@ -1228,6 +1229,26 @@ NS_GetReferrerFromChannel(nsIChannel *channel,
 }
 
 inline nsresult
+NS_ParseRequestContentType(const nsACString &rawContentType,
+                           nsCString        &contentType,
+                           nsCString        &contentCharset)
+{
+    // contentCharset is left untouched if not present in rawContentType
+    nsresult rv;
+    nsCOMPtr<nsINetUtil> util = do_GetNetUtil(&rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsINetUtil_ESR_38> utilESR38 = do_QueryInterface(util, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsCString charset;
+    bool hadCharset;
+    rv = utilESR38->ParseRequestContentType(rawContentType, charset, &hadCharset,
+                                            contentType);
+    if (NS_SUCCEEDED(rv) && hadCharset)
+        contentCharset = charset;
+    return rv;
+}
+
+inline nsresult
 NS_ParseContentType(const nsACString &rawContentType,
                     nsCString        &contentType,
                     nsCString        &contentCharset)
diff --git a/netwerk/base/nsURLHelper.cpp b/netwerk/base/nsURLHelper.cpp
index 10ea849..cdb2120 100644
--- a/netwerk/base/nsURLHelper.cpp
+++ b/netwerk/base/nsURLHelper.cpp
@@ -803,7 +803,8 @@ net_ParseMediaType(const nsACString &aMediaTypeStr,
                    int32_t          aOffset,
                    bool             *aHadCharset,
                    int32_t          *aCharsetStart,
-                   int32_t          *aCharsetEnd)
+                   int32_t          *aCharsetEnd,
+                   bool             aStrict)
 {
     const nsCString& flatStr = PromiseFlatCString(aMediaTypeStr);
     const char* start = flatStr.get();
@@ -820,6 +821,8 @@ net_ParseMediaType(const nsACString &aMediaTypeStr,
     int32_t charsetParamStart = 0;
     int32_t charsetParamEnd = 0;
 
+    uint32_t consumed = typeEnd - type;
+
     // Iterate over parameters
     bool typeHasCharset = false;
     uint32_t paramStart = flatStr.FindChar(';', typeEnd - start);
@@ -843,6 +846,7 @@ net_ParseMediaType(const nsACString &aMediaTypeStr,
                 charsetParamEnd = curParamEnd;
             }
 
+            consumed = curParamEnd;
             curParamStart = curParamEnd + 1;
         } while (curParamStart < flatStr.Length());
     }
@@ -872,8 +876,10 @@ net_ParseMediaType(const nsACString &aMediaTypeStr,
     // some servers give junk after the charset parameter, which may
     // include a comma, so this check makes us a bit more tolerant.
 
-    if (type != typeEnd && strncmp(type, "*/*", typeEnd - type) != 0 &&
-        memchr(type, '/', typeEnd - type) != nullptr) {
+    if (type != typeEnd &&
+        memchr(type, '/', typeEnd - type) != nullptr &&
+        (aStrict ? (net_FindCharNotInSet(start + consumed, end, HTTP_LWS) == end) :
+                   (strncmp(type, "*/*", typeEnd - type) != 0))) {
         // Common case here is that aContentType is empty
         bool eq = !aContentType.IsEmpty() &&
             aContentType.Equals(Substring(type, typeEnd),
@@ -980,13 +986,59 @@ net_ParseContentType(const nsACString &aHeaderStr,
         net_ParseMediaType(Substring(flatStr, curTypeStart,
                                      curTypeEnd - curTypeStart),
                            aContentType, aContentCharset, curTypeStart,
-                           aHadCharset, aCharsetStart, aCharsetEnd);
+                           aHadCharset, aCharsetStart, aCharsetEnd, false);
 
         // And let's move on to the next media-type
         curTypeStart = curTypeEnd + 1;
     } while (curTypeStart < flatStr.Length());
 }
 
+void
+net_ParseRequestContentType(const nsACString &aHeaderStr,
+                            nsACString       &aContentType,
+                            nsACString       &aContentCharset,
+                            bool             *aHadCharset)
+{
+    //
+    // Augmented BNF (from RFC 7231 section 3.1.1.1):
+    //
+    //   media-type   = type "/" subtype *( OWS ";" OWS parameter )
+    //   type         = token
+    //   subtype      = token
+    //   parameter    = token "=" ( token / quoted-string )
+    //
+    // Examples:
+    //
+    //   text/html
+    //   text/html; charset=ISO-8859-1
+    //   text/html; charset="ISO-8859-1"
+    //   application/octet-stream
+    //
+
+    aContentType.Truncate();
+    aContentCharset.Truncate();
+    *aHadCharset = false;
+    const nsCString& flatStr = PromiseFlatCString(aHeaderStr);
+
+    // At this point curTypeEnd points to the spot where the media-type
+    // starting at curTypeEnd ends.  Time to parse that!
+    nsAutoCString contentType, contentCharset;
+    bool hadCharset = false;
+    int32_t dummy1, dummy2;
+    uint32_t typeEnd = net_FindMediaDelimiter(flatStr, 0, ',');
+    if (typeEnd != flatStr.Length()) {
+        // We have some stuff left at the end, so this is not a valid
+        // request Content-Type header.
+        return;
+    }
+    net_ParseMediaType(flatStr, contentType, contentCharset, 0,
+                       &hadCharset, &dummy1, &dummy2, true);
+
+    aContentType = contentType;
+    aContentCharset = contentCharset;
+    *aHadCharset = hadCharset;
+}
+
 bool
 net_IsValidHostName(const nsCSubstring &host)
 {
diff --git a/netwerk/base/nsURLHelper.h b/netwerk/base/nsURLHelper.h
index 816a3c5..21e17be 100644
--- a/netwerk/base/nsURLHelper.h
+++ b/netwerk/base/nsURLHelper.h
@@ -172,11 +172,27 @@ char * net_RFindCharNotInSet(const char *str, const char *end, const char *set);
  * specified), aHadCharset is set to false.  Otherwise, it's set to
  * true.  Note that aContentCharset can be empty even if aHadCharset
  * is true.
+ *
+ * This parsing is suitable for HTTP request.  Use net_ParseContentType
+ * for parsing this header in HTTP responses.
+ */
+void net_ParseRequestContentType(const nsACString &aHeaderStr,
+                                 nsACString       &aContentType,
+                                 nsACString       &aContentCharset,
+                                 bool*          aHadCharset);
+
+/**
+ * Parses a content-type header and returns the content type and
+ * charset (if any).  aCharset is not modified if no charset is
+ * specified in anywhere in aHeaderStr.  In that case (no charset
+ * specified), aHadCharset is set to false.  Otherwise, it's set to
+ * true.  Note that aContentCharset can be empty even if aHadCharset
+ * is true.
  */
 void net_ParseContentType(const nsACString &aHeaderStr,
-                                      nsACString       &aContentType,
-                                      nsACString       &aContentCharset,
-                                      bool*          aHadCharset);
+                          nsACString       &aContentType,
+                          nsACString       &aContentCharset,
+                          bool*          aHadCharset);
 /**
  * As above, but also returns the start and end indexes for the charset
  * parameter in aHeaderStr.  These are indices for the entire parameter, NOT
@@ -187,11 +203,11 @@ void net_ParseContentType(const nsACString &aHeaderStr,
  * *aCharsetStart is nonnegative; this corresponds to charset="".
  */
 void net_ParseContentType(const nsACString &aHeaderStr,
-                                      nsACString       &aContentType,
-                                      nsACString       &aContentCharset,
-                                      bool             *aHadCharset,
-                                      int32_t          *aCharsetStart,
-                                      int32_t          *aCharsetEnd);
+                          nsACString       &aContentType,
+                          nsACString       &aContentCharset,
+                          bool             *aHadCharset,
+                          int32_t          *aCharsetStart,
+                          int32_t          *aCharsetEnd);
 
 /* inline versions */
 
-- 
2.5.0

