From d135e3b3c48811c577e1632a41c5c50bc55c035c Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari 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 util = do_GetNetUtil(&rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr 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