Index: third_party/WebKit/Source/modules/fetch/BlobBytesConsumer.cpp |
diff --git a/third_party/WebKit/Source/modules/fetch/BlobBytesConsumer.cpp b/third_party/WebKit/Source/modules/fetch/BlobBytesConsumer.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..88cb7f479660490d66992482b11c55d62bfa48d7 |
--- /dev/null |
+++ b/third_party/WebKit/Source/modules/fetch/BlobBytesConsumer.cpp |
@@ -0,0 +1,326 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "modules/fetch/BlobBytesConsumer.h" |
+ |
+#include "core/fetch/FetchInitiatorTypeNames.h" |
+#include "core/loader/ThreadableLoader.h" |
+#include "modules/fetch/BytesConsumerForDataConsumerHandle.h" |
+#include "modules/fetch/DataConsumerHandleUtil.h" |
+#include "platform/blob/BlobData.h" |
+#include "platform/blob/BlobRegistry.h" |
+#include "platform/blob/BlobURL.h" |
+#include "platform/network/ResourceError.h" |
+#include "platform/network/ResourceRequest.h" |
+#include "platform/weborigin/KURL.h" |
+#include "platform/weborigin/SecurityOrigin.h" |
+ |
+namespace blink { |
+ |
+BlobBytesConsumer::BlobBytesConsumer(ExecutionContext* executionContext, PassRefPtr<BlobDataHandle> blobDataHandle, ThreadableLoader* loader) |
+ : ContextLifecycleObserver(executionContext) |
+ , m_blobDataHandle(blobDataHandle) |
+ , m_loader(loader) |
+{ |
+ ThreadState::current()->registerPreFinalizer(this); |
+} |
+ |
+BlobBytesConsumer::BlobBytesConsumer(ExecutionContext* executionContext, PassRefPtr<BlobDataHandle> blobDataHandle) |
+ : BlobBytesConsumer(executionContext, blobDataHandle, nullptr) |
+{ |
+} |
+ |
+BlobBytesConsumer::~BlobBytesConsumer() |
+{ |
+} |
+ |
+BytesConsumer::Result BlobBytesConsumer::beginRead(const char** buffer, size_t* available) |
+{ |
+ *buffer = nullptr; |
+ *available = 0; |
+ |
+ if (m_hasSeenEndOfData && m_hasFinishedLoading) { |
+ // It's possible that |cancel| has been called before the first |
+ // |beginRead| call. That's why we need to check this condition |
+ // before checking |m_hasStarted|. |
+ return Result::Done; |
+ } |
+ |
+ if (!m_hasStarted) { |
+ KURL m_blobURL = BlobURL::createPublicURL(getExecutionContext()->getSecurityOrigin()); |
+ if (m_blobURL.isEmpty()) { |
+ m_hasFailedToLoad = true; |
+ } else { |
+ BlobRegistry::registerPublicBlobURL(getExecutionContext()->getSecurityOrigin(), m_blobURL, m_blobDataHandle); |
+ |
+ // m_loader is non-null only in tests. |
+ if (!m_loader) |
+ m_loader = createLoader(); |
+ |
+ ResourceRequest request(m_blobURL); |
+ request.setRequestContext(WebURLRequest::RequestContextInternal); |
+ request.setUseStreamOnResponse(true); |
+ // We intentionally skip |
+ // 'setExternalRequestStateFromRequestorAddressSpace', as 'blob:' |
+ // can never be external. |
+ m_loader->start(request); |
+ } |
+ m_hasStarted = true; |
+ } |
+ |
+ if (m_hasFailedToLoad) |
+ return Result::Error; |
+ |
+ if (m_hasSeenEndOfData && m_hasFinishedLoading) { |
hiroshige
2016/09/15 05:54:51
This if statement is removed in Patch Set 12.
Do w
yhirano
2016/09/15 08:13:31
I found that this should not happen because m_hasS
|
+ // We need to check this condition again because the loader can |
+ // call a callback synchronously. |
+ return Result::Done; |
+ } |
+ |
+ if (!m_hasReceivedResponse) |
+ return Result::ShouldWait; |
+ |
+ auto result = m_body->beginRead(buffer, available); |
+ switch (result) { |
+ case Result::Ok: |
+ case Result::ShouldWait: |
+ break; |
+ case Result::Done: |
+ m_hasSeenEndOfData = true; |
+ clearIfNecessary(); |
+ return m_hasFinishedLoading ? Result::Done : Result::ShouldWait; |
+ case Result::Error: |
+ m_hasFinishedLoading = true; |
+ clearIfNecessary(); |
+ break; |
+ } |
+ return result; |
+} |
+ |
+BytesConsumer::Result BlobBytesConsumer::endRead(size_t read) |
+{ |
+ DCHECK(m_body); |
+ m_body->endRead(read); |
+ if (m_hasFailedToLoad || (m_hasSeenEndOfData && m_hasFinishedLoading)) |
+ m_body = nullptr; |
+ return Result::Ok; |
+} |
+ |
+PassRefPtr<BlobDataHandle> BlobBytesConsumer::drainAsBlobDataHandle(BlobSizePolicy policy) |
+{ |
+ if (m_hasStarted || m_hasFinishedLoading) |
+ return nullptr; |
+ DCHECK(m_blobDataHandle); |
+ if (policy == BlobSizePolicy::DisallowBlobWithInvalidSize && m_blobDataHandle->size() == UINT64_MAX) |
+ return nullptr; |
+ m_hasFinishedLoading = true; |
+ m_hasSeenEndOfData = true; |
+ return m_blobDataHandle.release(); |
+} |
+ |
+PassRefPtr<EncodedFormData> BlobBytesConsumer::drainAsFormData() |
+{ |
+ RefPtr<BlobDataHandle> handle = drainAsBlobDataHandle(BlobSizePolicy::AllowBlobWithInvalidSize); |
+ if (!handle) |
+ return nullptr; |
+ RefPtr<EncodedFormData> formData = EncodedFormData::create(); |
+ formData->appendBlob(handle->uuid(), handle); |
+ return formData.release(); |
+} |
+ |
+void BlobBytesConsumer::setClient(BytesConsumer::Client* client) |
+{ |
+ DCHECK(!m_client); |
+ DCHECK(client); |
+ m_client = client; |
+} |
+ |
+void BlobBytesConsumer::clearClient() |
+{ |
+ m_client = nullptr; |
+} |
+ |
+void BlobBytesConsumer::cancel() |
+{ |
+ m_hasSeenEndOfData = true; |
+ m_hasFinishedLoading = true; |
+ clearIfNecessary(); |
+ if (!m_blobURL.isEmpty()) { |
+ BlobRegistry::revokePublicBlobURL(m_blobURL); |
+ m_blobURL = KURL(); |
+ } |
+} |
+ |
+BytesConsumer::Error BlobBytesConsumer::getError() const |
+{ |
+ DCHECK_EQ(PublicState::Errored, getPublicState()); |
+ return Error("Failed to load a blob."); |
+} |
+ |
+BytesConsumer::PublicState BlobBytesConsumer::getPublicState() const |
+{ |
+ if (m_hasSeenEndOfData) |
+ return m_hasFinishedLoading ? PublicState::Closed : PublicState::ReadableOrWaiting; |
+ if (m_hasFailedToLoad) |
+ return PublicState::Errored; |
+ if (!m_hasReceivedResponse) |
+ return PublicState::ReadableOrWaiting; |
+ // As we monitor the state change at onStateChange, |m_body|'s state |
+ // must be ReadableOrWaiting. |
+ DCHECK_EQ(PublicState::ReadableOrWaiting, m_body->getPublicState()); |
+ return PublicState::ReadableOrWaiting; |
+} |
+ |
+void BlobBytesConsumer::contextDestroyed() |
+{ |
+ if (m_loader) { |
+ m_loader->cancel(); |
+ m_loader = nullptr; |
+ } |
+ |
+ if (getPublicState() != PublicState::ReadableOrWaiting) |
+ return; |
+ |
+ m_hasFailedToLoad = true; |
+ if (m_client) |
+ m_client->onStateChange(); |
+ clearIfNecessary(); |
+} |
+ |
+void BlobBytesConsumer::onStateChange() |
+{ |
+ DCHECK(!m_hasFailedToLoad); |
+ DCHECK(!m_hasSeenEndOfData); |
+ DCHECK(m_body); |
+ |
+ switch (m_body->getPublicState()) { |
+ case PublicState::ReadableOrWaiting: |
+ break; |
+ case PublicState::Closed: |
+ m_hasSeenEndOfData = true; |
+ break; |
+ case PublicState::Errored: |
+ m_hasFailedToLoad = true; |
+ break; |
+ } |
+ |
+ if (m_client) |
+ m_client->onStateChange(); |
+ clearIfNecessary(); |
+} |
+ |
+void BlobBytesConsumer::didReceiveResponse(unsigned long identifier, const ResourceResponse&, std::unique_ptr<WebDataConsumerHandle> handle) |
+{ |
+ DCHECK(handle); |
+ DCHECK(!m_hasReceivedResponse); |
+ |
+ m_hasReceivedResponse = true; |
+ m_body = new BytesConsumerForDataConsumerHandle(getExecutionContext(), createFetchDataConsumerHandleFromWebHandle(std::move(handle))); |
+ m_body->setClient(this); |
+ |
+ if (!m_hasStarted) { |
+ // This function is called synchronously in ThreadableLoader::start. |
+ return; |
+ } |
+ onStateChange(); |
+} |
+ |
+void BlobBytesConsumer::didFinishLoading(unsigned long identifier, double finishTime) |
+{ |
+ DCHECK(!m_hasFailedToLoad); |
+ DCHECK(!m_hasFinishedLoading); |
+ m_hasFinishedLoading = true; |
+ m_loader = nullptr; |
+ if (!m_hasStarted) { |
+ // This function is called synchronously in ThreadableLoader::start. |
+ return; |
+ } |
+ if (!m_hasSeenEndOfData) |
+ return; |
+ m_body = nullptr; |
+ if (m_client) { |
+ m_client->onStateChange(); |
+ m_client = nullptr; |
+ } |
+} |
+ |
+void BlobBytesConsumer::didFail(const ResourceError& error) |
+{ |
+ if (error.isCancellation()) { |
+ DCHECK(m_hasSeenEndOfData); |
+ DCHECK(m_hasFinishedLoading); |
+ return; |
+ } |
+ didFailInternal(); |
+} |
+ |
+void BlobBytesConsumer::didFailAccessControlCheck(const ResourceError&) |
+{ |
+ didFailInternal(); |
+} |
+ |
+void BlobBytesConsumer::didFailRedirectCheck() |
+{ |
+ didFailInternal(); |
hiroshige
2016/09/13 08:35:44
nit: Can we place NOTREACHED()?
(FetchBlobDCH's Bl
yhirano
2016/09/14 07:33:25
Done.
|
+} |
+ |
+DEFINE_TRACE(BlobBytesConsumer) |
+{ |
+ visitor->trace(m_body); |
+ visitor->trace(m_client); |
+ visitor->trace(m_loader); |
+ BytesConsumer::trace(visitor); |
+ BytesConsumer::Client::trace(visitor); |
+ ContextLifecycleObserver::trace(visitor); |
+} |
+ |
+BlobBytesConsumer* BlobBytesConsumer::createForTesting(ExecutionContext* executionContext, PassRefPtr<BlobDataHandle> blobDataHandle, ThreadableLoader* loader) |
+{ |
+ return new BlobBytesConsumer(executionContext, blobDataHandle, loader); |
+} |
+ |
+ThreadableLoader* BlobBytesConsumer::createLoader() |
+{ |
+ ThreadableLoaderOptions options; |
+ options.preflightPolicy = ConsiderPreflight; |
+ options.crossOriginRequestPolicy = DenyCrossOriginRequests; |
+ options.contentSecurityPolicyEnforcement = DoNotEnforceContentSecurityPolicy; |
+ options.initiator = FetchInitiatorTypeNames::internal; |
+ |
+ ResourceLoaderOptions resourceLoaderOptions; |
+ resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData; |
+ |
+ return ThreadableLoader::create(*getExecutionContext(), this, options, resourceLoaderOptions); |
+} |
+ |
+void BlobBytesConsumer::didFailInternal() |
+{ |
+ DCHECK(!m_hasFailedToLoad); |
+ DCHECK(!m_hasFinishedLoading); |
+ m_hasFailedToLoad = true; |
+ m_loader = nullptr; |
+ m_body = nullptr; |
+ if (!m_hasStarted) { |
+ // This function is called synchronously in ThreadableLoader::start. |
+ return; |
+ } |
+ if (m_client) { |
+ m_client->onStateChange(); |
+ m_client = nullptr; |
+ } |
+} |
+ |
+void BlobBytesConsumer::clearIfNecessary() |
+{ |
+ if (m_hasFailedToLoad || (m_hasFinishedLoading && m_hasSeenEndOfData)) { |
+ if (m_loader) { |
+ m_loader->cancel(); |
+ m_loader = nullptr; |
+ } |
+ m_body = nullptr; |
+ m_client = nullptr; |
+ } |
+} |
+ |
+} // namespace blink |