Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(877)

Unified Diff: third_party/WebKit/Source/modules/fetch/BlobBytesConsumer.cpp

Issue 2287323002: Implement BlobBytesConsumer (Closed)
Patch Set: fix Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698