Bug 1312410 - Fetch API should use MutableBlobStorage to store big Blobs on disk, r=qdot

This commit is contained in:
Andrea Marchesini
2016-10-26 09:07:25 +02:00
parent 87820057a6
commit 0ce4aeb4a5
10 changed files with 383 additions and 73 deletions

View File

@@ -28,6 +28,7 @@
#include "mozilla/dom/File.h"
#include "mozilla/dom/FormData.h"
#include "mozilla/dom/Headers.h"
#include "mozilla/dom/MutableBlobStreamListener.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/dom/Request.h"
@@ -170,10 +171,7 @@ public:
// ...but release it before calling Fetch, because mResolver's callback can
// be called synchronously and they want the mutex, too.
fetch->Fetch(mResolver);
// FetchDriver::Fetch never directly fails
return NS_OK;
return fetch->Fetch(mResolver);
}
};
@@ -709,6 +707,36 @@ public:
}
};
/*
* Called on successfully reading the complete stream for Blob.
*/
template <class Derived>
class ContinueConsumeBlobBodyRunnable final : public MainThreadWorkerRunnable
{
// This has been addrefed before this runnable is dispatched,
// released in WorkerRun().
FetchBody<Derived>* mFetchBody;
RefPtr<BlobImpl> mBlobImpl;
public:
ContinueConsumeBlobBodyRunnable(FetchBody<Derived>* aFetchBody,
BlobImpl* aBlobImpl)
: MainThreadWorkerRunnable(aFetchBody->mWorkerPrivate)
, mFetchBody(aFetchBody)
, mBlobImpl(aBlobImpl)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mBlobImpl);
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
mFetchBody->ContinueConsumeBlobBody(mBlobImpl);
return true;
}
};
// OnStreamComplete always adopts the buffer, utility class to release it in
// a couple of places.
class MOZ_STACK_CLASS AutoFreeBuffer final {
@@ -789,6 +817,7 @@ public:
template <class Derived>
class ConsumeBodyDoneObserver : public nsIStreamLoaderObserver
, public MutableBlobStorageCallback
{
FetchBody<Derived>* mFetchBody;
@@ -836,6 +865,31 @@ public:
return NS_SUCCESS_ADOPTED_DATA;
}
virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
Blob* aBlob,
nsresult aRv) override
{
// On error.
if (NS_FAILED(aRv)) {
OnStreamComplete(nullptr, nullptr, aRv, 0, nullptr);
return;
}
MOZ_ASSERT(aBlob);
if (mFetchBody->mWorkerPrivate) {
RefPtr<ContinueConsumeBlobBodyRunnable<Derived>> r =
new ContinueConsumeBlobBodyRunnable<Derived>(mFetchBody, aBlob->Impl());
if (!r->Dispatch()) {
NS_WARNING("Could not dispatch ConsumeBlobBodyRunnable");
return;
}
} else {
mFetchBody->ContinueConsumeBlobBody(aBlob->Impl());
}
}
private:
virtual ~ConsumeBodyDoneObserver()
{ }
@@ -1074,13 +1128,35 @@ FetchBody<Derived>::BeginConsumeBodyMainThread()
}
RefPtr<ConsumeBodyDoneObserver<Derived>> p = new ConsumeBodyDoneObserver<Derived>(this);
nsCOMPtr<nsIStreamLoader> loader;
rv = NS_NewStreamLoader(getter_AddRefs(loader), p);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
nsCOMPtr<nsIStreamListener> listener;
if (mConsumeType == CONSUME_BLOB) {
MutableBlobStorage::MutableBlobStorageType type =
MutableBlobStorage::eOnlyInMemory;
const mozilla::UniquePtr<mozilla::ipc::PrincipalInfo>& principalInfo =
DerivedClass()->GetPrincipalInfo();
// We support temporary file for blobs only if the principal is known and
// it's system or content not in private Browsing.
if (principalInfo &&
(principalInfo->type() == mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo ||
(principalInfo->type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
principalInfo->get_ContentPrincipalInfo().attrs().mPrivateBrowsingId == 0))) {
type = MutableBlobStorage::eCouldBeInTemporaryFile;
}
listener = new MutableBlobStreamListener(type, nullptr, mMimeType, p);
} else {
nsCOMPtr<nsIStreamLoader> loader;
rv = NS_NewStreamLoader(getter_AddRefs(loader), p);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
listener = loader;
}
rv = pump->AsyncRead(loader, nullptr);
rv = pump->AsyncRead(listener, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
@@ -1195,14 +1271,7 @@ FetchBody<Derived>::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength
break;
}
case CONSUME_BLOB: {
RefPtr<dom::Blob> blob = BodyUtil::ConsumeBlob(
derivedClass->GetParentObject(), NS_ConvertUTF8toUTF16(mMimeType),
aResultLength, aResult, error);
if (!error.Failed()) {
localPromise->MaybeResolve(blob);
// File takes over ownership.
autoFree.Reset();
}
MOZ_CRASH("This should not happen.");
break;
}
case CONSUME_FORMDATA: {
@@ -1245,6 +1314,38 @@ FetchBody<Derived>::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength
}
}
template <class Derived>
void
FetchBody<Derived>::ContinueConsumeBlobBody(BlobImpl* aBlobImpl)
{
AssertIsOnTargetThread();
// Just a precaution to ensure ContinueConsumeBody is not called out of
// sync with a body read.
MOZ_ASSERT(mBodyUsed);
MOZ_ASSERT(!mReadDone);
MOZ_ASSERT(mConsumeType == CONSUME_BLOB);
MOZ_ASSERT_IF(mWorkerPrivate, mWorkerHolder);
#ifdef DEBUG
mReadDone = true;
#endif
MOZ_ASSERT(mConsumePromise);
RefPtr<Promise> localPromise = mConsumePromise.forget();
RefPtr<Derived> derivedClass = DerivedClass();
ReleaseObject();
// Release the pump and then early exit if there was an error.
// Uses NS_ProxyRelease internally, so this is safe.
mConsumeBodyPump = nullptr;
RefPtr<dom::Blob> blob =
dom::Blob::Create(derivedClass->GetParentObject(), aBlobImpl);
MOZ_ASSERT(blob);
localPromise->MaybeResolve(blob);
}
template <class Derived>
already_AddRefed<Promise>
FetchBody<Derived>::ConsumeBody(ConsumeType aType, ErrorResult& aRv)