forked from mirrors/gecko-dev
		
	Bug 1771423 - Set Content-Type to an empty string for an blob with no type. r=kershaw,smaug,Gijs
Set the Content-Type to an empty string for an blob when the type was not given. Ensure that the channel Content-Type is not overriden in the case that it was previously set and the blob type is not valid. Differential Revision: https://phabricator.services.mozilla.com/D147481
This commit is contained in:
		
							parent
							
								
									680803f101
								
							
						
					
					
						commit
						d349734f89
					
				
					 10 changed files with 55 additions and 70 deletions
				
			
		|  | @ -1057,13 +1057,13 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest) { | |||
|       if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) { | ||||
|         contentType += ";charset="_ns + contentCharset; | ||||
|       } | ||||
| 
 | ||||
|       IgnoredErrorResult result; | ||||
|       response->Headers()->Append("Content-Type"_ns, contentType, result); | ||||
|       MOZ_ASSERT(!result.Failed()); | ||||
|     } | ||||
| 
 | ||||
|     if (contentLength > 0) { | ||||
|     IgnoredErrorResult result; | ||||
|     response->Headers()->Append("Content-Type"_ns, contentType, result); | ||||
|     MOZ_ASSERT(!result.Failed()); | ||||
| 
 | ||||
|     if (contentLength >= 0) { | ||||
|       nsAutoCString contentLenStr; | ||||
|       contentLenStr.AppendInt(contentLength); | ||||
| 
 | ||||
|  |  | |||
|  | @ -26,6 +26,18 @@ BlobURLChannel::BlobURLChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo) | |||
| 
 | ||||
| BlobURLChannel::~BlobURLChannel() = default; | ||||
| 
 | ||||
| NS_IMETHODIMP | ||||
| BlobURLChannel::SetContentType(const nsACString& aContentType) { | ||||
|   // If the blob type is empty, set the content type of the channel to the
 | ||||
|   // empty string.
 | ||||
|   if (aContentType.IsEmpty()) { | ||||
|     mContentType.Truncate(); | ||||
|     return NS_OK; | ||||
|   } | ||||
| 
 | ||||
|   return nsBaseChannel::SetContentType(aContentType); | ||||
| } | ||||
| 
 | ||||
| nsresult BlobURLChannel::OpenContentStream(bool aAsync, | ||||
|                                            nsIInputStream** aResult, | ||||
|                                            nsIChannel** aChannel) { | ||||
|  |  | |||
|  | @ -21,6 +21,8 @@ class BlobURLChannel final : public nsBaseChannel { | |||
|  public: | ||||
|   BlobURLChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo); | ||||
| 
 | ||||
|   NS_IMETHOD SetContentType(const nsACString& aContentType) override; | ||||
| 
 | ||||
|  private: | ||||
|   ~BlobURLChannel() override; | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
| #include "mozilla/dom/ContentChild.h" | ||||
| #include "mozilla/dom/IPCBlobUtils.h" | ||||
| #include "nsStreamUtils.h" | ||||
| #include "nsMimeTypes.h" | ||||
| 
 | ||||
| namespace mozilla::dom { | ||||
| 
 | ||||
|  | @ -461,9 +462,21 @@ nsresult BlobURLInputStream::StoreBlobImplStream( | |||
|     already_AddRefed<BlobImpl> aBlobImpl, const MutexAutoLock& aProofOfLock) { | ||||
|   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread"); | ||||
|   const RefPtr<BlobImpl> blobImpl = aBlobImpl; | ||||
|   nsAutoString contentType; | ||||
|   blobImpl->GetType(contentType); | ||||
|   mChannel->SetContentType(NS_ConvertUTF16toUTF8(contentType)); | ||||
|   nsAutoString blobContentType; | ||||
|   nsAutoCString channelContentType; | ||||
| 
 | ||||
|   blobImpl->GetType(blobContentType); | ||||
|   mChannel->GetContentType(channelContentType); | ||||
|   // A empty content type is the correct channel content type in the case of a
 | ||||
|   // fetch of a blob where the type was not set. It is invalid in others cases
 | ||||
|   // such as a XHR (See https://xhr.spec.whatwg.org/#response-mime-type). The
 | ||||
|   // XMLHttpRequestMainThread will set the channel content type to the correct
 | ||||
|   // fallback value before this point, so we need to be careful to only override
 | ||||
|   // it when the blob type is valid.
 | ||||
|   if (!blobContentType.IsEmpty() || | ||||
|       channelContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) { | ||||
|     mChannel->SetContentType(NS_ConvertUTF16toUTF8(blobContentType)); | ||||
|   } | ||||
| 
 | ||||
|   auto cleanupOnExit = MakeScopeExit([&] { mChannel = nullptr; }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1889,7 +1889,7 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest* request) { | |||
|   // Fallback to 'application/octet-stream'
 | ||||
|   nsAutoCString type; | ||||
|   channel->GetContentType(type); | ||||
|   if (type.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) { | ||||
|   if (type.IsEmpty() || type.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) { | ||||
|     channel->SetContentType(nsLiteralCString(APPLICATION_OCTET_STREAM)); | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -292,8 +292,6 @@ class nsBaseChannel | |||
|   nsCOMPtr<nsISupports> mOwner; | ||||
|   nsCOMPtr<nsITransportSecurityInfo> mSecurityInfo; | ||||
|   nsCOMPtr<nsIChannel> mRedirectChannel; | ||||
|   nsCString mContentType; | ||||
|   nsCString mContentCharset; | ||||
|   uint32_t mLoadFlags{LOAD_NORMAL}; | ||||
|   bool mQueriedProgressSink{true}; | ||||
|   bool mSynthProgressEvents{false}; | ||||
|  | @ -303,6 +301,8 @@ class nsBaseChannel | |||
|   uint32_t mRedirectFlags{0}; | ||||
| 
 | ||||
|  protected: | ||||
|   nsCString mContentType; | ||||
|   nsCString mContentCharset; | ||||
|   nsCOMPtr<nsIURI> mURI; | ||||
|   nsCOMPtr<nsILoadGroup> mLoadGroup; | ||||
|   nsCOMPtr<nsILoadInfo> mLoadInfo; | ||||
|  |  | |||
|  | @ -246,7 +246,8 @@ class ParentProcessDocumentOpenInfo final : public nsDocumentOpenInfo, | |||
|     // The one exception is nsUnknownDecoder, which works in the parent
 | ||||
|     // (and we need to know what the content type is before we can
 | ||||
|     // decide if it will be handled in the parent), so we run that here.
 | ||||
|     if (mContentType.LowerCaseEqualsASCII(UNKNOWN_CONTENT_TYPE)) { | ||||
|     if (mContentType.LowerCaseEqualsASCII(UNKNOWN_CONTENT_TYPE) || | ||||
|         mContentType.IsEmpty()) { | ||||
|       return nsDocumentOpenInfo::TryStreamConversion(aChannel); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -471,7 +471,8 @@ nsViewSourceChannel::GetContentType(nsACString& aContentType) { | |||
|     // content decoder will then kick in automatically, and it
 | ||||
|     // will call our SetOriginalContentType method instead of our
 | ||||
|     // SetContentType method to set the type it determines.
 | ||||
|     if (!contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) { | ||||
|     if (!contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) && | ||||
|         !contentType.IsEmpty()) { | ||||
|       contentType = VIEWSOURCE_CONTENT_TYPE; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,54 +0,0 @@ | |||
| [scheme-blob.sub.any.worker.html] | ||||
|   expected: | ||||
|     if (os == "android") and fission: [OK, TIMEOUT] | ||||
|   [Fetching URL.createObjectURL(empty_blob) is OK] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Fetching URL.createObjectURL(empty_type_blob) is OK] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Fetching URL.createObjectURL(empty_data_blob) is OK] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Blob content is not sniffed for a content type [image/png\]] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Blob content is not sniffed for a content type [text/xml\]] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Set content type to the empty string for slice with invalid content type] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Set content type to the empty string for slice with no content type ] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Blob.slice should not sniff the content for a content type] | ||||
|     expected: FAIL | ||||
| 
 | ||||
| 
 | ||||
| [scheme-blob.sub.any.html] | ||||
|   expected: | ||||
|     if (os == "android") and fission: [OK, TIMEOUT] | ||||
|   [Fetching URL.createObjectURL(empty_blob) is OK] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Fetching URL.createObjectURL(empty_type_blob) is OK] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Fetching URL.createObjectURL(empty_data_blob) is OK] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Blob content is not sniffed for a content type [image/png\]] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Blob content is not sniffed for a content type [text/xml\]] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Set content type to the empty string for slice with invalid content type] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Set content type to the empty string for slice with no content type ] | ||||
|     expected: FAIL | ||||
| 
 | ||||
|   [Blob.slice should not sniff the content for a content type] | ||||
|     expected: FAIL | ||||
|  | @ -273,7 +273,9 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request) { | |||
|     // Check if this is a PDF which should be opened internally. We also handle
 | ||||
|     // octet-streams that look like they might be PDFs based on their extension.
 | ||||
|     bool isPDF = mContentType.LowerCaseEqualsASCII(APPLICATION_PDF); | ||||
|     if (!isPDF && mContentType.LowerCaseEqualsASCII(APPLICATION_OCTET_STREAM)) { | ||||
|     if (!isPDF && | ||||
|         (mContentType.LowerCaseEqualsASCII(APPLICATION_OCTET_STREAM) || | ||||
|          mContentType.IsEmpty())) { | ||||
|       nsAutoString flname; | ||||
|       aChannel->GetContentDispositionFilename(flname); | ||||
|       isPDF = StringEndsWith(flname, u".pdf"_ns); | ||||
|  | @ -475,7 +477,7 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request) { | |||
|     request->SetLoadFlags(loadFlags | nsIChannel::LOAD_RETARGETED_DOCUMENT_URI | | ||||
|                           nsIChannel::LOAD_TARGETED); | ||||
| 
 | ||||
|     if (isGuessFromExt) { | ||||
|     if (isGuessFromExt || mContentType.IsEmpty()) { | ||||
|       mContentType = APPLICATION_GUESS_FROM_EXT; | ||||
|       aChannel->SetContentType(nsLiteralCString(APPLICATION_GUESS_FROM_EXT)); | ||||
|     } | ||||
|  | @ -568,7 +570,15 @@ nsresult nsDocumentOpenInfo::ConvertData(nsIRequest* request, | |||
| 
 | ||||
| nsresult nsDocumentOpenInfo::TryStreamConversion(nsIChannel* aChannel) { | ||||
|   constexpr auto anyType = "*/*"_ns; | ||||
|   nsresult rv = ConvertData(aChannel, m_contentListener, mContentType, anyType); | ||||
| 
 | ||||
|   // A empty content type should be treated like the unknown content type.
 | ||||
|   nsCString srcContentType(mContentType); | ||||
|   if (srcContentType.IsEmpty()) { | ||||
|     srcContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); | ||||
|   } | ||||
| 
 | ||||
|   nsresult rv = | ||||
|       ConvertData(aChannel, m_contentListener, srcContentType, anyType); | ||||
|   if (NS_FAILED(rv)) { | ||||
|     m_targetStreamListener = nullptr; | ||||
|   } else if (m_targetStreamListener) { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Dan Robertson
						Dan Robertson