Bug 1784880 - Support range requests on blob URLs in fetch/XMLHttpRequest; r=dlrobertson,necko-reviewers,jesup
Differential Revision: https://phabricator.services.mozilla.com/D184281
This commit is contained in:
@@ -811,6 +811,28 @@ bool XMLHttpRequestMainThread::IsDeniedCrossSiteCORSRequest() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<nsBaseChannel::ContentRange>
|
||||
XMLHttpRequestMainThread::GetRequestedContentRange() const {
|
||||
MOZ_ASSERT(mChannel);
|
||||
if (!IsBlobURI(mRequestURL)) {
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
nsBaseChannel* baseChan = static_cast<nsBaseChannel*>(mChannel.get());
|
||||
if (!baseChan) {
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
return baseChan->GetContentRange();
|
||||
}
|
||||
|
||||
void XMLHttpRequestMainThread::GetContentRangeHeader(nsACString& out) const {
|
||||
Maybe<nsBaseChannel::ContentRange> range = GetRequestedContentRange();
|
||||
if (range.isSome()) {
|
||||
range->AsHeader(out);
|
||||
} else {
|
||||
out.SetIsVoid(true);
|
||||
}
|
||||
}
|
||||
|
||||
void XMLHttpRequestMainThread::GetResponseURL(nsAString& aUrl) {
|
||||
aUrl.Truncate();
|
||||
|
||||
@@ -867,8 +889,8 @@ uint32_t XMLHttpRequestMainThread::GetStatus(ErrorResult& aRv) {
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
|
||||
if (!httpChannel) {
|
||||
// Pretend like we got a 200 response, since our load was successful
|
||||
return 200;
|
||||
// Pretend like we got a 200/206 response, since our load was successful
|
||||
return GetRequestedContentRange().isSome() ? 206 : 200;
|
||||
}
|
||||
|
||||
uint32_t status;
|
||||
@@ -1175,6 +1197,17 @@ void XMLHttpRequestMainThread::GetAllResponseHeaders(
|
||||
aResponseHeaders.AppendLiteral("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Should set a Content-Range header for blob scheme.
|
||||
// From https://fetch.spec.whatwg.org/#scheme-fetch 3.blob.9.20:
|
||||
// "Set response’s header list to «(`Content-Length`, serializedSlicedLength),
|
||||
// (`Content-Type`, type), (`Content-Range`, contentRange)»."
|
||||
GetContentRangeHeader(value);
|
||||
if (!value.IsVoid()) {
|
||||
aResponseHeaders.AppendLiteral("Content-Range: ");
|
||||
aResponseHeaders.Append(value);
|
||||
aResponseHeaders.AppendLiteral("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
void XMLHttpRequestMainThread::GetResponseHeader(const nsACString& header,
|
||||
@@ -1229,6 +1262,11 @@ void XMLHttpRequestMainThread::GetResponseHeader(const nsACString& header,
|
||||
}
|
||||
}
|
||||
|
||||
// Content Range:
|
||||
else if (header.LowerCaseEqualsASCII("content-range")) {
|
||||
GetContentRangeHeader(_retval);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1865,6 +1903,13 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest* request) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we were asked for a bad range on a blob URL, but we're async,
|
||||
// we should throw now in order to fire an error progress event.
|
||||
if (GetRequestedContentRange().isNothing() &&
|
||||
mAuthorRequestHeaders.Has("range")) {
|
||||
return NS_ERROR_NET_PARTIAL_TRANSFER;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
|
||||
NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
|
||||
|
||||
@@ -1905,11 +1950,13 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest* request) {
|
||||
channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
|
||||
}
|
||||
|
||||
// Fallback to 'application/octet-stream'
|
||||
nsAutoCString type;
|
||||
channel->GetContentType(type);
|
||||
if (type.IsEmpty() || type.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
|
||||
channel->SetContentType(nsLiteralCString(APPLICATION_OCTET_STREAM));
|
||||
// Fallback to 'application/octet-stream' (leaving data URLs alone)
|
||||
if (!IsBlobURI(mRequestURL)) {
|
||||
nsAutoCString type;
|
||||
channel->GetContentType(type);
|
||||
if (type.IsEmpty() || type.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
|
||||
channel->SetContentType(nsLiteralCString(APPLICATION_OCTET_STREAM));
|
||||
}
|
||||
}
|
||||
|
||||
DetectCharset();
|
||||
@@ -2665,6 +2712,21 @@ nsresult XMLHttpRequestMainThread::InitiateFetch(
|
||||
}
|
||||
}
|
||||
|
||||
// Should set a Content-Range header for blob scheme, and also slice the
|
||||
// blob appropriately, so we process the Range header here for later use.
|
||||
if (IsBlobURI(mRequestURL)) {
|
||||
nsAutoCString range;
|
||||
mAuthorRequestHeaders.Get("range", range);
|
||||
if (!range.IsVoid()) {
|
||||
rv = NS_SetChannelContentRangeForBlobURI(mChannel, mRequestURL, range);
|
||||
if (mFlagSynchronous && NS_FAILED(rv)) {
|
||||
// We later fire an error progress event for non-sync
|
||||
mState = XMLHttpRequest_Binding::DONE;
|
||||
return NS_ERROR_DOM_NETWORK_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Due to the chrome-only XHR.channel API, we need a hacky way to set the
|
||||
// SEC_COOKIES_INCLUDE *after* the channel has been has been created, since
|
||||
// .withCredentials can be called after open() is called.
|
||||
@@ -2813,6 +2875,11 @@ already_AddRefed<PreloaderBase> XMLHttpRequestMainThread::FindPreload() {
|
||||
void XMLHttpRequestMainThread::EnsureChannelContentType() {
|
||||
MOZ_ASSERT(mChannel);
|
||||
|
||||
// We don't mess with the content type of a blob URL.
|
||||
if (IsBlobURI(mRequestURL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Since we expect XML data, set the type hint accordingly
|
||||
// if the channel doesn't know any content type.
|
||||
// This means that we always try to parse local files as XML
|
||||
|
||||
Reference in New Issue
Block a user