mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 14:52:16 +00:00
Bug 1845006 - store the fully-serialized MimeType on data url channels so XHR and fetch may use it for content-type response headers, and clean up the data url parsing code to better match the spec. r=kershaw,sunil,necko-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D184713
This commit is contained in:
parent
2d9c0d60d0
commit
07889f5cff
@ -5,34 +5,9 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MimeType.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
|
||||
namespace {
|
||||
template <typename Char>
|
||||
constexpr bool IsHTTPTokenPoint(Char aChar) {
|
||||
using UnsignedChar = typename mozilla::detail::MakeUnsignedChar<Char>::Type;
|
||||
auto c = static_cast<UnsignedChar>(aChar);
|
||||
return c == '!' || c == '#' || c == '$' || c == '%' || c == '&' ||
|
||||
c == '\'' || c == '*' || c == '+' || c == '-' || c == '.' ||
|
||||
c == '^' || c == '_' || c == '`' || c == '|' || c == '~' ||
|
||||
mozilla::IsAsciiAlphanumeric(c);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
constexpr bool IsHTTPQuotedStringTokenPoint(Char aChar) {
|
||||
using UnsignedChar = typename mozilla::detail::MakeUnsignedChar<Char>::Type;
|
||||
auto c = static_cast<UnsignedChar>(aChar);
|
||||
return c == 0x9 || (c >= ' ' && c <= '~') || mozilla::IsNonAsciiLatin1(c);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
constexpr bool IsHTTPWhitespace(Char aChar) {
|
||||
using UnsignedChar = typename mozilla::detail::MakeUnsignedChar<Char>::Type;
|
||||
auto c = static_cast<UnsignedChar>(aChar);
|
||||
return c == 0x9 || c == 0xA || c == 0xD || c == 0x20;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <typename char_type>
|
||||
/* static */ mozilla::UniquePtr<TMimeType<char_type>>
|
||||
TMimeType<char_type>::Parse(const nsTSubstring<char_type>& aMimeType) {
|
||||
@ -41,20 +16,20 @@ TMimeType<char_type>::Parse(const nsTSubstring<char_type>& aMimeType) {
|
||||
// Steps 1-2
|
||||
const char_type* pos = aMimeType.BeginReading();
|
||||
const char_type* end = aMimeType.EndReading();
|
||||
while (pos < end && IsHTTPWhitespace(*pos)) {
|
||||
while (pos < end && NS_IsHTTPWhitespace(*pos)) {
|
||||
++pos;
|
||||
}
|
||||
if (pos == end) {
|
||||
return nullptr;
|
||||
}
|
||||
while (end > pos && IsHTTPWhitespace(*(end - 1))) {
|
||||
while (end > pos && NS_IsHTTPWhitespace(*(end - 1))) {
|
||||
--end;
|
||||
}
|
||||
|
||||
// Steps 3-4
|
||||
const char_type* typeStart = pos;
|
||||
while (pos < end && *pos != '/') {
|
||||
if (!IsHTTPTokenPoint(*pos)) {
|
||||
if (!NS_IsHTTPTokenPoint(*pos)) {
|
||||
return nullptr;
|
||||
}
|
||||
++pos;
|
||||
@ -76,14 +51,14 @@ TMimeType<char_type>::Parse(const nsTSubstring<char_type>& aMimeType) {
|
||||
const char_type* subtypeStart = pos;
|
||||
const char_type* subtypeEnd = nullptr;
|
||||
while (pos < end && *pos != ';') {
|
||||
if (!IsHTTPTokenPoint(*pos)) {
|
||||
if (!NS_IsHTTPTokenPoint(*pos)) {
|
||||
// If we hit a whitespace, check that the rest of
|
||||
// the subtype is whitespace, otherwise fail.
|
||||
if (IsHTTPWhitespace(*pos)) {
|
||||
if (NS_IsHTTPWhitespace(*pos)) {
|
||||
subtypeEnd = pos;
|
||||
++pos;
|
||||
while (pos < end && *pos != ';') {
|
||||
if (!IsHTTPWhitespace(*pos)) {
|
||||
if (!NS_IsHTTPWhitespace(*pos)) {
|
||||
return nullptr;
|
||||
}
|
||||
++pos;
|
||||
@ -119,21 +94,41 @@ TMimeType<char_type>::Parse(const nsTSubstring<char_type>& aMimeType) {
|
||||
++pos;
|
||||
|
||||
// Step 11.2
|
||||
while (pos < end && IsHTTPWhitespace(*pos)) {
|
||||
while (pos < end && NS_IsHTTPWhitespace(*pos)) {
|
||||
++pos;
|
||||
}
|
||||
|
||||
const char_type* namePos = pos;
|
||||
|
||||
// Steps 11.3 and 11.4
|
||||
nsTString<char_type> paramName;
|
||||
bool paramNameHadInvalidChars = false;
|
||||
while (pos < end && *pos != ';' && *pos != '=') {
|
||||
if (!IsHTTPTokenPoint(*pos)) {
|
||||
if (!NS_IsHTTPTokenPoint(*pos)) {
|
||||
paramNameHadInvalidChars = true;
|
||||
}
|
||||
paramName.Append(ToLowerCaseASCII(*pos));
|
||||
++pos;
|
||||
}
|
||||
|
||||
// Might as well check for base64 now
|
||||
if (*pos != '=') {
|
||||
// trim leading and trailing spaces
|
||||
while (namePos < pos && NS_IsHTTPWhitespace(*namePos)) {
|
||||
++namePos;
|
||||
}
|
||||
if (namePos < pos && ToLowerCaseASCII(*namePos) == 'b' &&
|
||||
++namePos < pos && ToLowerCaseASCII(*namePos) == 'a' &&
|
||||
++namePos < pos && ToLowerCaseASCII(*namePos) == 's' &&
|
||||
++namePos < pos && ToLowerCaseASCII(*namePos) == 'e' &&
|
||||
++namePos < pos && ToLowerCaseASCII(*namePos) == '6' &&
|
||||
++namePos < pos && ToLowerCaseASCII(*namePos) == '4') {
|
||||
while (++namePos < pos && NS_IsHTTPWhitespace(*namePos)) {
|
||||
}
|
||||
mimeType->mIsBase64 = namePos == pos;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 11.5
|
||||
if (pos < end) {
|
||||
if (*pos == ';') {
|
||||
@ -160,10 +155,10 @@ TMimeType<char_type>::Parse(const nsTSubstring<char_type>& aMimeType) {
|
||||
while (true) {
|
||||
// Step 11.8.2.1
|
||||
while (pos < end && *pos != '"' && *pos != '\\') {
|
||||
if (!IsHTTPQuotedStringTokenPoint(*pos)) {
|
||||
if (!NS_IsHTTPQuotedStringTokenPoint(*pos)) {
|
||||
paramValueHadInvalidChars = true;
|
||||
}
|
||||
if (!IsHTTPTokenPoint(*pos)) {
|
||||
if (!NS_IsHTTPTokenPoint(*pos)) {
|
||||
paramValue.mRequiresQuoting = true;
|
||||
}
|
||||
paramValue.Append(*pos);
|
||||
@ -177,10 +172,10 @@ TMimeType<char_type>::Parse(const nsTSubstring<char_type>& aMimeType) {
|
||||
|
||||
// Step 11.8.2.2.2
|
||||
if (pos < end) {
|
||||
if (!IsHTTPQuotedStringTokenPoint(*pos)) {
|
||||
if (!NS_IsHTTPQuotedStringTokenPoint(*pos)) {
|
||||
paramValueHadInvalidChars = true;
|
||||
}
|
||||
if (!IsHTTPTokenPoint(*pos)) {
|
||||
if (!NS_IsHTTPTokenPoint(*pos)) {
|
||||
paramValue.mRequiresQuoting = true;
|
||||
}
|
||||
paramValue.Append(*pos);
|
||||
@ -213,7 +208,7 @@ TMimeType<char_type>::Parse(const nsTSubstring<char_type>& aMimeType) {
|
||||
// Step 11.9.2
|
||||
const char_type* paramValueLastChar = pos - 1;
|
||||
while (paramValueLastChar >= paramValueStart &&
|
||||
IsHTTPWhitespace(*paramValueLastChar)) {
|
||||
NS_IsHTTPWhitespace(*paramValueLastChar)) {
|
||||
--paramValueLastChar;
|
||||
}
|
||||
|
||||
@ -223,10 +218,10 @@ TMimeType<char_type>::Parse(const nsTSubstring<char_type>& aMimeType) {
|
||||
}
|
||||
|
||||
for (const char_type* c = paramValueStart; c <= paramValueLastChar; ++c) {
|
||||
if (!IsHTTPQuotedStringTokenPoint(*c)) {
|
||||
if (!NS_IsHTTPQuotedStringTokenPoint(*c)) {
|
||||
paramValueHadInvalidChars = true;
|
||||
}
|
||||
if (!IsHTTPTokenPoint(*c)) {
|
||||
if (!NS_IsHTTPTokenPoint(*c)) {
|
||||
paramValue.mRequiresQuoting = true;
|
||||
}
|
||||
paramValue.Append(*c);
|
||||
|
@ -33,6 +33,7 @@ class TMimeType final {
|
||||
ParameterValue() : mRequiresQuoting(false) {}
|
||||
};
|
||||
|
||||
bool mIsBase64{false};
|
||||
nsTString<char_type> mType;
|
||||
nsTString<char_type> mSubtype;
|
||||
nsTHashMap<typename HashKeyType<char_type>::HashType, ParameterValue>
|
||||
@ -52,6 +53,8 @@ class TMimeType final {
|
||||
// Returns the `<mType>/<mSubtype>`
|
||||
void GetFullType(nsTSubstring<char_type>& aStr) const;
|
||||
|
||||
bool IsBase64() const { return mIsBase64; }
|
||||
|
||||
// @param aName - the name of the parameter
|
||||
// @return true if the parameter name is found, false otherwise.
|
||||
bool HasParameter(const nsTSubstring<char_type>& aName) const;
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "nsBaseChannel.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
#include "nsDataChannel.h"
|
||||
#include "nsDataHandler.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPrintfCString.h"
|
||||
@ -1017,7 +1018,6 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest) {
|
||||
bool foundOpaqueRedirect = false;
|
||||
|
||||
nsAutoCString contentType;
|
||||
channel->GetContentType(contentType);
|
||||
|
||||
int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
|
||||
rv = channel->GetContentLength(&contentLength);
|
||||
@ -1025,6 +1025,8 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest) {
|
||||
contentLength == InternalResponse::UNKNOWN_BODY_SIZE);
|
||||
|
||||
if (httpChannel) {
|
||||
channel->GetContentType(contentType);
|
||||
|
||||
uint32_t responseStatus = 0;
|
||||
rv = httpChannel->GetResponseStatus(&responseStatus);
|
||||
if (NS_FAILED(rv)) {
|
||||
@ -1098,11 +1100,18 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest) {
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
}
|
||||
|
||||
if (!contentType.IsEmpty()) {
|
||||
nsAutoCString contentCharset;
|
||||
channel->GetContentCharset(contentCharset);
|
||||
if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
|
||||
contentType += ";charset="_ns + contentCharset;
|
||||
if (uri && uri->SchemeIs("data")) {
|
||||
nsDataChannel* dchan = static_cast<nsDataChannel*>(channel.get());
|
||||
MOZ_ASSERT(dchan);
|
||||
contentType.Assign(dchan->MimeType());
|
||||
} else {
|
||||
channel->GetContentType(contentType);
|
||||
if (!contentType.IsEmpty()) {
|
||||
nsAutoCString contentCharset;
|
||||
channel->GetContentCharset(contentCharset);
|
||||
if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
|
||||
contentType += ";charset="_ns + contentCharset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "mozilla/StaticPrefs_network.h"
|
||||
#include "mozilla/StaticPrefs_privacy.h"
|
||||
#include "mozilla/dom/ProgressEvent.h"
|
||||
#include "nsDataChannel.h"
|
||||
#include "nsIJARChannel.h"
|
||||
#include "nsIJARURI.h"
|
||||
#include "nsLayoutCID.h"
|
||||
@ -1144,6 +1145,26 @@ bool XMLHttpRequestMainThread::IsSafeHeader(
|
||||
return isSafe;
|
||||
}
|
||||
|
||||
bool XMLHttpRequestMainThread::GetContentType(nsACString& aValue) const {
|
||||
MOZ_ASSERT(mChannel);
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (NS_SUCCEEDED(mChannel->GetURI(getter_AddRefs(uri))) &&
|
||||
uri->SchemeIs("data")) {
|
||||
nsDataChannel* dchan = static_cast<nsDataChannel*>(mChannel.get());
|
||||
MOZ_ASSERT(dchan);
|
||||
aValue.Assign(dchan->MimeType());
|
||||
return true;
|
||||
}
|
||||
if (NS_SUCCEEDED(mChannel->GetContentType(aValue))) {
|
||||
nsCString value;
|
||||
if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && !value.IsEmpty()) {
|
||||
aValue.AppendLiteral(";charset=");
|
||||
aValue.Append(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void XMLHttpRequestMainThread::GetAllResponseHeaders(
|
||||
nsACString& aResponseHeaders, ErrorResult& aRv) {
|
||||
NOT_CALLABLE_IN_SYNC_SEND_RV
|
||||
@ -1176,13 +1197,9 @@ void XMLHttpRequestMainThread::GetAllResponseHeaders(
|
||||
|
||||
// Even non-http channels supply content type.
|
||||
nsAutoCString value;
|
||||
if (NS_SUCCEEDED(mChannel->GetContentType(value))) {
|
||||
if (GetContentType(value)) {
|
||||
aResponseHeaders.AppendLiteral("Content-Type: ");
|
||||
aResponseHeaders.Append(value);
|
||||
if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && !value.IsEmpty()) {
|
||||
aResponseHeaders.AppendLiteral(";charset=");
|
||||
aResponseHeaders.Append(value);
|
||||
}
|
||||
aResponseHeaders.AppendLiteral("\r\n");
|
||||
}
|
||||
|
||||
@ -1240,18 +1257,11 @@ void XMLHttpRequestMainThread::GetResponseHeader(const nsACString& header,
|
||||
|
||||
// Content Type:
|
||||
if (header.LowerCaseEqualsASCII("content-type")) {
|
||||
if (NS_FAILED(mChannel->GetContentType(_retval))) {
|
||||
if (!GetContentType(_retval)) {
|
||||
// Means no content type
|
||||
_retval.SetIsVoid(true);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCString value;
|
||||
if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
|
||||
!value.IsEmpty()) {
|
||||
_retval.AppendLiteral(";charset=");
|
||||
_retval.Append(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Content Length:
|
||||
|
@ -477,6 +477,9 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
|
||||
// set to "text/xml".
|
||||
void EnsureChannelContentType();
|
||||
|
||||
// Gets the value of the final content-type header from the channel.
|
||||
bool GetContentType(nsACString& aValue) const;
|
||||
|
||||
already_AddRefed<nsIHttpChannel> GetCurrentHttpChannel();
|
||||
already_AddRefed<nsIJARChannel> GetCurrentJARChannel();
|
||||
|
||||
|
@ -903,6 +903,30 @@ bool NS_IsValidHTTPToken(const nsACString& aToken);
|
||||
*/
|
||||
void NS_TrimHTTPWhitespace(const nsACString& aSource, nsACString& aDest);
|
||||
|
||||
template <typename Char>
|
||||
constexpr bool NS_IsHTTPTokenPoint(Char aChar) {
|
||||
using UnsignedChar = typename mozilla::detail::MakeUnsignedChar<Char>::Type;
|
||||
auto c = static_cast<UnsignedChar>(aChar);
|
||||
return c == '!' || c == '#' || c == '$' || c == '%' || c == '&' ||
|
||||
c == '\'' || c == '*' || c == '+' || c == '-' || c == '.' ||
|
||||
c == '^' || c == '_' || c == '`' || c == '|' || c == '~' ||
|
||||
mozilla::IsAsciiAlphanumeric(c);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
constexpr bool NS_IsHTTPQuotedStringTokenPoint(Char aChar) {
|
||||
using UnsignedChar = typename mozilla::detail::MakeUnsignedChar<Char>::Type;
|
||||
auto c = static_cast<UnsignedChar>(aChar);
|
||||
return c == 0x9 || (c >= ' ' && c <= '~') || mozilla::IsNonAsciiLatin1(c);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
constexpr bool NS_IsHTTPWhitespace(Char aChar) {
|
||||
using UnsignedChar = typename mozilla::detail::MakeUnsignedChar<Char>::Type;
|
||||
auto c = static_cast<UnsignedChar>(aChar);
|
||||
return c == 0x9 || c == 0xA || c == 0xD || c == 0x20;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given request must be upgraded to HTTPS.
|
||||
* If |aResultCallback| is provided and the storage is not ready to read, the
|
||||
|
@ -59,7 +59,7 @@ nsresult nsDataChannel::OpenContentStream(bool async, nsIInputStream** result,
|
||||
nsDependentCSubstring dataRange;
|
||||
bool lBase64;
|
||||
rv = nsDataHandler::ParsePathWithoutRef(path, contentType, &contentCharset,
|
||||
lBase64, &dataRange);
|
||||
lBase64, &dataRange, &mMimeType);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// This will avoid a copy if nothing needs to be unescaped.
|
||||
|
@ -16,9 +16,13 @@ class nsDataChannel : public nsBaseChannel {
|
||||
public:
|
||||
explicit nsDataChannel(nsIURI* uri) { SetURI(uri); }
|
||||
|
||||
const nsACString& MimeType() const { return mMimeType; }
|
||||
|
||||
protected:
|
||||
[[nodiscard]] virtual nsresult OpenContentStream(
|
||||
bool async, nsIInputStream** result, nsIChannel** channel) override;
|
||||
|
||||
nsCString mMimeType;
|
||||
};
|
||||
|
||||
#endif /* nsDataChannel_h___ */
|
||||
|
@ -9,7 +9,9 @@
|
||||
#include "nsError.h"
|
||||
#include "nsIOService.h"
|
||||
#include "DataChannelChild.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsSimpleURI.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "mozilla/dom/MimeType.h"
|
||||
#include "mozilla/StaticPrefs_network.h"
|
||||
|
||||
@ -92,37 +94,68 @@ nsDataHandler::AllowPort(int32_t port, const char* scheme, bool* _retval) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper that performs a case insensitive match to find the offset of a given
|
||||
* pattern in a nsACString.
|
||||
* The search is performed starting from the end of the string; if the string
|
||||
* contains more than one match, the rightmost (last) match will be returned.
|
||||
*/
|
||||
static bool FindOffsetOf(const nsACString& aPattern, const nsACString& aSrc,
|
||||
nsACString::size_type& aOffset) {
|
||||
nsACString::const_iterator begin, end;
|
||||
aSrc.BeginReading(begin);
|
||||
aSrc.EndReading(end);
|
||||
if (!RFindInReadable(aPattern, begin, end,
|
||||
nsCaseInsensitiveCStringComparator)) {
|
||||
namespace {
|
||||
|
||||
constexpr bool TrimSpacesAndBase64(nsACString& aMimeType) {
|
||||
const char* beg = aMimeType.BeginReading();
|
||||
const char* end = aMimeType.EndReading();
|
||||
|
||||
// trim leading and trailing spaces
|
||||
while (beg < end && NS_IsHTTPWhitespace(*beg)) {
|
||||
++beg;
|
||||
}
|
||||
if (beg == end) {
|
||||
aMimeType.Truncate();
|
||||
return false;
|
||||
}
|
||||
while (end > beg && NS_IsHTTPWhitespace(*(end - 1))) {
|
||||
--end;
|
||||
}
|
||||
if (beg == end) {
|
||||
aMimeType.Truncate();
|
||||
return false;
|
||||
}
|
||||
|
||||
// FindInReadable updates |begin| and |end| to the match coordinates.
|
||||
aOffset = nsACString::size_type(begin.get() - aSrc.Data());
|
||||
return true;
|
||||
// trim trailing `; base64` (if any) and remember it
|
||||
const char* pos = end - 1;
|
||||
bool foundBase64 = false;
|
||||
if (pos > beg && *pos == '4' && --pos > beg && *pos == '6' && --pos > beg &&
|
||||
ToLowerCaseASCII(*pos) == 'e' && --pos > beg &&
|
||||
ToLowerCaseASCII(*pos) == 's' && --pos > beg &&
|
||||
ToLowerCaseASCII(*pos) == 'a' && --pos > beg &&
|
||||
ToLowerCaseASCII(*pos) == 'b') {
|
||||
while (--pos > beg && NS_IsHTTPWhitespace(*pos)) {
|
||||
}
|
||||
if (pos >= beg && *pos == ';') {
|
||||
end = pos;
|
||||
foundBase64 = true;
|
||||
}
|
||||
}
|
||||
|
||||
// actually trim off the spaces and trailing base64, returning if we found it.
|
||||
const char* s = aMimeType.BeginReading();
|
||||
aMimeType.Assign(Substring(aMimeType, beg - s, end - s));
|
||||
return foundBase64;
|
||||
}
|
||||
|
||||
nsresult nsDataHandler::ParsePathWithoutRef(
|
||||
const nsACString& aPath, nsCString& aContentType,
|
||||
nsCString* aContentCharset, bool& aIsBase64,
|
||||
nsDependentCSubstring* aDataBuffer) {
|
||||
static constexpr auto kBase64 = "base64"_ns;
|
||||
} // namespace
|
||||
|
||||
nsresult nsDataHandler::ParsePathWithoutRef(const nsACString& aPath,
|
||||
nsCString& aContentType,
|
||||
nsCString* aContentCharset,
|
||||
bool& aIsBase64,
|
||||
nsDependentCSubstring* aDataBuffer,
|
||||
nsCString* aMimeType) {
|
||||
static constexpr auto kCharset = "charset"_ns;
|
||||
|
||||
// This implements https://fetch.spec.whatwg.org/#data-url-processor
|
||||
// It also returns the full mimeType in aMimeType so fetch/XHR may access it
|
||||
// for content-length headers. The contentType and charset parameters retain
|
||||
// our legacy behavior, as much Gecko code generally expects GetContentType
|
||||
// to yield only the MimeType's essence, not its full value with parameters.
|
||||
|
||||
aIsBase64 = false;
|
||||
|
||||
// First, find the start of the data
|
||||
int32_t commaIdx = aPath.FindChar(',');
|
||||
|
||||
// This is a hack! When creating a URL using the DOM API we want to ignore
|
||||
@ -132,70 +165,45 @@ nsresult nsDataHandler::ParsePathWithoutRef(
|
||||
if (aContentCharset && commaIdx == kNotFound) {
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
if (commaIdx == 0 || commaIdx == kNotFound) {
|
||||
// Nothing but data.
|
||||
|
||||
// "Let mimeType be the result of collecting a sequence of code points that
|
||||
// are not equal to U+002C (,), given position."
|
||||
nsCString mimeType(Substring(aPath, 0, commaIdx));
|
||||
|
||||
// "Strip leading and trailing ASCII whitespace from mimeType."
|
||||
// "If mimeType ends with U+003B (;), followed by zero or more U+0020 SPACE,
|
||||
// followed by an ASCII case-insensitive match for "base64", then ..."
|
||||
aIsBase64 = TrimSpacesAndBase64(mimeType);
|
||||
|
||||
// "If mimeType starts with ";", then prepend "text/plain" to mimeType."
|
||||
if (mimeType.Length() > 0 && mimeType.CharAt(0) == ';') {
|
||||
mimeType = "text/plain"_ns + mimeType;
|
||||
}
|
||||
|
||||
// "Let mimeTypeRecord be the result of parsing mimeType."
|
||||
// This also checks for instances of ;base64 in the middle of the MimeType.
|
||||
// This is against the current spec, but we're doing it because we have
|
||||
// historically seen webcompat issues relying on this (see bug 781693).
|
||||
if (mozilla::UniquePtr<CMimeType> parsed = CMimeType::Parse(mimeType)) {
|
||||
parsed->GetFullType(aContentType);
|
||||
if (aContentCharset) {
|
||||
parsed->GetParameterValue(kCharset, *aContentCharset);
|
||||
}
|
||||
if (aMimeType) {
|
||||
parsed->Serialize(*aMimeType);
|
||||
}
|
||||
if (parsed->IsBase64()) {
|
||||
aIsBase64 = true;
|
||||
}
|
||||
} else {
|
||||
// "If mimeTypeRecord is failure, then set mimeTypeRecord to
|
||||
// text/plain;charset=US-ASCII."
|
||||
aContentType.AssignLiteral("text/plain");
|
||||
if (aContentCharset) {
|
||||
aContentCharset->AssignLiteral("US-ASCII");
|
||||
}
|
||||
} else {
|
||||
auto mediaType = Substring(aPath, 0, commaIdx);
|
||||
|
||||
// Determine if the data is base64 encoded.
|
||||
nsACString::size_type base64;
|
||||
if (FindOffsetOf(kBase64, mediaType, base64) && base64 > 0) {
|
||||
nsACString::size_type offset = base64 + kBase64.Length();
|
||||
// Per the RFC 2397 grammar, "base64" MUST be at the end of the
|
||||
// non-data part.
|
||||
//
|
||||
// But we also allow it in between parameters so a subsequent ";"
|
||||
// is ok as well (this deals with *broken* data URIs, see bug
|
||||
// 781693 for an example). Anything after "base64" in the non-data
|
||||
// part will be discarded in this case, however.
|
||||
if (offset == mediaType.Length() || mediaType[offset] == ';' ||
|
||||
mediaType[offset] == ' ') {
|
||||
MOZ_DIAGNOSTIC_ASSERT(base64 > 0, "Did someone remove the check?");
|
||||
// Index is on the first character of matched "base64" so we
|
||||
// move to the preceding character
|
||||
base64--;
|
||||
// Skip any preceding spaces, searching for a semicolon
|
||||
while (base64 > 0 && mediaType[base64] == ' ') {
|
||||
base64--;
|
||||
}
|
||||
if (mediaType[base64] == ';') {
|
||||
aIsBase64 = true;
|
||||
// Trim the base64 part off.
|
||||
mediaType.Rebind(aPath, 0, base64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip any leading spaces
|
||||
nsACString::size_type startIndex = 0;
|
||||
while (startIndex < mediaType.Length() && mediaType[startIndex] == ' ') {
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
nsAutoCString mediaTypeBuf;
|
||||
// If the mimetype starts with ';' we assume text/plain
|
||||
if (startIndex < mediaType.Length() && mediaType[startIndex] == ';') {
|
||||
mediaTypeBuf.AssignLiteral("text/plain");
|
||||
mediaTypeBuf.Append(mediaType);
|
||||
mediaType.Rebind(mediaTypeBuf, 0, mediaTypeBuf.Length());
|
||||
}
|
||||
|
||||
// Everything else is content type.
|
||||
if (mozilla::UniquePtr<CMimeType> parsed = CMimeType::Parse(mediaType)) {
|
||||
parsed->GetFullType(aContentType);
|
||||
if (aContentCharset) {
|
||||
parsed->GetParameterValue(kCharset, *aContentCharset);
|
||||
}
|
||||
} else {
|
||||
// Mime Type parsing failed
|
||||
aContentType.AssignLiteral("text/plain");
|
||||
if (aContentCharset) {
|
||||
aContentCharset->AssignLiteral("US-ASCII");
|
||||
}
|
||||
if (aMimeType) {
|
||||
aMimeType->AssignLiteral("text/plain;charset=US-ASCII");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ class nsDataHandler : public nsIProtocolHandler,
|
||||
[[nodiscard]] static nsresult ParsePathWithoutRef(
|
||||
const nsACString& aPath, nsCString& aContentType,
|
||||
nsCString* aContentCharset, bool& aIsBase64,
|
||||
nsDependentCSubstring* aDataBuffer);
|
||||
nsDependentCSubstring* aDataBuffer, nsCString* aMimeType = nullptr);
|
||||
};
|
||||
|
||||
#endif /* nsDataHandler_h___ */
|
||||
|
@ -4,30 +4,12 @@
|
||||
["data://test:test/,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:;x=x;charset=x,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:;x=x,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:IMAGE/gif;hi=x,%C2%B1"]
|
||||
expected: FAIL
|
||||
|
||||
["data:text/plain;a=%2C,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:x/x;base64;charset=x,WA"]
|
||||
expected: FAIL
|
||||
|
||||
["data:x;base64;x,WA"]
|
||||
expected: FAIL
|
||||
|
||||
["data:;base64;,WA"]
|
||||
expected: FAIL
|
||||
|
||||
["data:text/plain;a=\\",\\",X"]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[processing.any.worker.html]
|
||||
expected:
|
||||
@ -35,55 +17,19 @@
|
||||
["data://test:test/,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:;x=x;charset=x,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:;x=x,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:IMAGE/gif;hi=x,%C2%B1"]
|
||||
expected: FAIL
|
||||
|
||||
["data:text/plain;a=%2C,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:x/x;base64;charset=x,WA"]
|
||||
expected: FAIL
|
||||
|
||||
["data:x;base64;x,WA"]
|
||||
expected: FAIL
|
||||
|
||||
["data:;base64;,WA"]
|
||||
expected: FAIL
|
||||
|
||||
["data:text/plain;a=\\",\\",X"]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[processing.any.serviceworker.html]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
["data:;x=x;charset=x,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:text/plain;a=\\",\\",X"]
|
||||
expected: FAIL
|
||||
|
||||
["data://test:test/,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:;x=x,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:text/plain;a=%2C,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:x;base64;x,WA"]
|
||||
expected: FAIL
|
||||
|
||||
["data:IMAGE/gif;hi=x,%C2%B1"]
|
||||
expected: FAIL
|
||||
|
||||
["data:x/x;base64;charset=x,WA"]
|
||||
expected: FAIL
|
||||
|
||||
@ -94,27 +40,9 @@
|
||||
[processing.any.sharedworker.html]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
["data:;x=x;charset=x,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:text/plain;a=\\",\\",X"]
|
||||
expected: FAIL
|
||||
|
||||
["data://test:test/,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:;x=x,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:text/plain;a=%2C,X"]
|
||||
expected: FAIL
|
||||
|
||||
["data:x;base64;x,WA"]
|
||||
expected: FAIL
|
||||
|
||||
["data:IMAGE/gif;hi=x,%C2%B1"]
|
||||
expected: FAIL
|
||||
|
||||
["data:x/x;base64;charset=x,WA"]
|
||||
expected: FAIL
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user