summaryrefslogtreecommitdiff
path: root/chromium/content/browser/blob_storage/blob_dispatcher_host.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/blob_storage/blob_dispatcher_host.cc')
-rw-r--r--chromium/content/browser/blob_storage/blob_dispatcher_host.cc370
1 files changed, 370 insertions, 0 deletions
diff --git a/chromium/content/browser/blob_storage/blob_dispatcher_host.cc b/chromium/content/browser/blob_storage/blob_dispatcher_host.cc
new file mode 100644
index 00000000000..d61653cdaf5
--- /dev/null
+++ b/chromium/content/browser/blob_storage/blob_dispatcher_host.cc
@@ -0,0 +1,370 @@
+// 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 "content/browser/blob_storage/blob_dispatcher_host.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "content/browser/bad_message.h"
+#include "content/browser/fileapi/chrome_blob_storage_context.h"
+#include "content/common/fileapi/webblob_messages.h"
+#include "ipc/ipc_platform_file.h"
+#include "storage/browser/blob/blob_storage_context.h"
+#include "storage/browser/blob/blob_transport_result.h"
+#include "storage/common/blob_storage/blob_item_bytes_request.h"
+#include "storage/common/blob_storage/blob_item_bytes_response.h"
+#include "storage/common/data_element.h"
+#include "url/gurl.h"
+
+using storage::BlobStorageContext;
+using storage::BlobStorageRegistry;
+using storage::BlobTransportResult;
+using storage::IPCBlobCreationCancelCode;
+
+namespace content {
+namespace {
+
+// These are used for UMA stats, don't change.
+enum RefcountOperation {
+ BDH_DECREMENT = 0,
+ BDH_INCREMENT,
+ BDH_TRACING_ENUM_LAST
+};
+
+} // namespace
+
+BlobDispatcherHost::BlobDispatcherHost(
+ ChromeBlobStorageContext* blob_storage_context)
+ : BrowserMessageFilter(BlobMsgStart),
+ blob_storage_context_(blob_storage_context) {}
+
+BlobDispatcherHost::~BlobDispatcherHost() {
+ ClearHostFromBlobStorageContext();
+}
+
+void BlobDispatcherHost::OnChannelClosing() {
+ ClearHostFromBlobStorageContext();
+ public_blob_urls_.clear();
+ blobs_inuse_map_.clear();
+}
+
+bool BlobDispatcherHost::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(BlobDispatcherHost, message)
+ IPC_MESSAGE_HANDLER(BlobStorageMsg_RegisterBlobUUID, OnRegisterBlobUUID)
+ IPC_MESSAGE_HANDLER(BlobStorageMsg_StartBuildingBlob, OnStartBuildingBlob)
+ IPC_MESSAGE_HANDLER(BlobStorageMsg_MemoryItemResponse, OnMemoryItemResponse)
+ IPC_MESSAGE_HANDLER(BlobStorageMsg_CancelBuildingBlob, OnCancelBuildingBlob)
+ IPC_MESSAGE_HANDLER(BlobHostMsg_IncrementRefCount, OnIncrementBlobRefCount)
+ IPC_MESSAGE_HANDLER(BlobHostMsg_DecrementRefCount, OnDecrementBlobRefCount)
+ IPC_MESSAGE_HANDLER(BlobHostMsg_RegisterPublicURL, OnRegisterPublicBlobURL)
+ IPC_MESSAGE_HANDLER(BlobHostMsg_RevokePublicURL, OnRevokePublicBlobURL)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void BlobDispatcherHost::OnRegisterBlobUUID(
+ const std::string& uuid,
+ const std::string& content_type,
+ const std::string& content_disposition,
+ const std::set<std::string>& referenced_blob_uuids) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ BlobStorageContext* context = this->context();
+ if (uuid.empty() || context->registry().HasEntry(uuid) ||
+ async_builder_.IsBeingBuilt(uuid)) {
+ bad_message::ReceivedBadMessage(this, bad_message::BDH_UUID_REGISTERED);
+ return;
+ }
+ blobs_inuse_map_[uuid] = 1;
+ BlobTransportResult result = async_builder_.RegisterBlobUUID(
+ uuid, content_type, content_disposition, referenced_blob_uuids, context);
+ switch (result) {
+ case BlobTransportResult::BAD_IPC:
+ blobs_inuse_map_.erase(uuid);
+ bad_message::ReceivedBadMessage(this,
+ bad_message::BDH_CONSTRUCTION_FAILED);
+ break;
+ case BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN:
+ // The async builder builds the blob as broken, and we just need to send
+ // the cancel message back to the renderer.
+ Send(new BlobStorageMsg_CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN));
+ break;
+ case BlobTransportResult::DONE:
+ break;
+ case BlobTransportResult::CANCEL_MEMORY_FULL:
+ case BlobTransportResult::CANCEL_FILE_ERROR:
+ case BlobTransportResult::CANCEL_UNKNOWN:
+ case BlobTransportResult::PENDING_RESPONSES:
+ NOTREACHED();
+ break;
+ }
+}
+
+void BlobDispatcherHost::OnStartBuildingBlob(
+ const std::string& uuid,
+ const std::vector<storage::DataElement>& descriptions) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (uuid.empty()) {
+ SendIPCResponse(uuid, BlobTransportResult::BAD_IPC);
+ return;
+ }
+ BlobStorageContext* context = this->context();
+ const BlobStorageRegistry::Entry* entry = context->registry().GetEntry(uuid);
+ if (!entry || entry->state == BlobStorageRegistry::BlobState::BROKEN) {
+ // We ignore messages for blobs that don't exist to handle the case where
+ // the renderer de-refs a blob that we're still constructing, and there are
+ // no references to that blob. We ignore broken as well, in the case where
+ // we decided to break a blob after RegisterBlobUUID is called.
+ // Second, if the last dereference of the blob happened on a different host,
+ // then we still haven't gotten rid of the 'building' state in the original
+ // host. So we call cancel, and send the message just in case that happens.
+ if (async_builder_.IsBeingBuilt(uuid)) {
+ async_builder_.CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING,
+ context);
+ Send(new BlobStorageMsg_CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING));
+ }
+ return;
+ }
+ if (!async_builder_.IsBeingBuilt(uuid)) {
+ SendIPCResponse(uuid, BlobTransportResult::BAD_IPC);
+ return;
+ }
+ // |this| owns async_builder_ so using base::Unretained(this) is safe.
+ BlobTransportResult result = async_builder_.StartBuildingBlob(
+ uuid, descriptions, context->memory_available(), context,
+ base::Bind(&BlobDispatcherHost::SendMemoryRequest, base::Unretained(this),
+ uuid));
+ SendIPCResponse(uuid, result);
+}
+
+void BlobDispatcherHost::OnMemoryItemResponse(
+ const std::string& uuid,
+ const std::vector<storage::BlobItemBytesResponse>& responses) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (uuid.empty()) {
+ SendIPCResponse(uuid, BlobTransportResult::BAD_IPC);
+ return;
+ }
+ BlobStorageContext* context = this->context();
+ const BlobStorageRegistry::Entry* entry = context->registry().GetEntry(uuid);
+ if (!entry || entry->state == BlobStorageRegistry::BlobState::BROKEN) {
+ // We ignore messages for blobs that don't exist to handle the case where
+ // the renderer de-refs a blob that we're still constructing, and there are
+ // no references to that blob. We ignore broken as well, in the case where
+ // we decided to break a blob after sending the memory request.
+ // Note: if a blob is broken, then it can't be in the async_builder.
+ // Second, if the last dereference of the blob happened on a different host,
+ // then we still haven't gotten rid of the 'building' state in the original
+ // host. So we call cancel, and send the message just in case that happens.
+ if (async_builder_.IsBeingBuilt(uuid)) {
+ async_builder_.CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING,
+ context);
+ Send(new BlobStorageMsg_CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING));
+ }
+ return;
+ }
+ if (!async_builder_.IsBeingBuilt(uuid)) {
+ SendIPCResponse(uuid, BlobTransportResult::BAD_IPC);
+ return;
+ }
+ BlobTransportResult result =
+ async_builder_.OnMemoryResponses(uuid, responses, context);
+ SendIPCResponse(uuid, result);
+}
+
+void BlobDispatcherHost::OnCancelBuildingBlob(
+ const std::string& uuid,
+ const storage::IPCBlobCreationCancelCode code) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (uuid.empty()) {
+ SendIPCResponse(uuid, BlobTransportResult::BAD_IPC);
+ return;
+ }
+ BlobStorageContext* context = this->context();
+ const BlobStorageRegistry::Entry* entry = context->registry().GetEntry(uuid);
+ if (!entry || entry->state == BlobStorageRegistry::BlobState::BROKEN) {
+ // We ignore messages for blobs that don't exist to handle the case where
+ // the renderer de-refs a blob that we're still constructing, and there are
+ // no references to that blob. We ignore broken as well, in the case where
+ // we decided to break a blob and the renderer also decided to cancel.
+ // Note: if a blob is broken, then it can't be in the async_builder.
+ // Second, if the last dereference of the blob happened on a different host,
+ // then we still haven't gotten rid of the 'building' state in the original
+ // host. So we call cancel just in case this happens.
+ if (async_builder_.IsBeingBuilt(uuid)) {
+ async_builder_.CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING,
+ context);
+ }
+ return;
+ }
+ if (!async_builder_.IsBeingBuilt(uuid)) {
+ SendIPCResponse(uuid, BlobTransportResult::BAD_IPC);
+ return;
+ }
+ VLOG(1) << "Blob construction of " << uuid << " cancelled by renderer. "
+ << " Reason: " << static_cast<int>(code) << ".";
+ async_builder_.CancelBuildingBlob(uuid, code, context);
+}
+
+void BlobDispatcherHost::OnIncrementBlobRefCount(const std::string& uuid) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ BlobStorageContext* context = this->context();
+ if (uuid.empty()) {
+ bad_message::ReceivedBadMessage(
+ this, bad_message::BDH_INVALID_REFCOUNT_OPERATION);
+ return;
+ }
+ if (!context->registry().HasEntry(uuid)) {
+ UMA_HISTOGRAM_ENUMERATION("Storage.Blob.InvalidReference", BDH_INCREMENT,
+ BDH_TRACING_ENUM_LAST);
+ return;
+ }
+ context->IncrementBlobRefCount(uuid);
+ blobs_inuse_map_[uuid] += 1;
+}
+
+void BlobDispatcherHost::OnDecrementBlobRefCount(const std::string& uuid) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (uuid.empty()) {
+ bad_message::ReceivedBadMessage(
+ this, bad_message::BDH_INVALID_REFCOUNT_OPERATION);
+ return;
+ }
+ if (!IsInUseInHost(uuid)) {
+ UMA_HISTOGRAM_ENUMERATION("Storage.Blob.InvalidReference", BDH_DECREMENT,
+ BDH_TRACING_ENUM_LAST);
+ return;
+ }
+ BlobStorageContext* context = this->context();
+ context->DecrementBlobRefCount(uuid);
+ blobs_inuse_map_[uuid] -= 1;
+ if (blobs_inuse_map_[uuid] == 0) {
+ blobs_inuse_map_.erase(uuid);
+ // If the blob has been deleted in the context and we're still building it,
+ // this means we have no references waiting to read it. Clear the building
+ // state and send a cancel message to the renderer.
+ if (async_builder_.IsBeingBuilt(uuid) &&
+ !context->registry().HasEntry(uuid)) {
+ async_builder_.CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING,
+ context);
+ Send(new BlobStorageMsg_CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING));
+ }
+ }
+}
+
+void BlobDispatcherHost::OnRegisterPublicBlobURL(const GURL& public_url,
+ const std::string& uuid) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ BlobStorageContext* context = this->context();
+ if (uuid.empty()) {
+ bad_message::ReceivedBadMessage(this,
+ bad_message::BDH_INVALID_URL_OPERATION);
+ return;
+ }
+ if (!IsInUseInHost(uuid) || context->registry().IsURLMapped(public_url)) {
+ UMA_HISTOGRAM_ENUMERATION("Storage.Blob.InvalidURLRegister", BDH_INCREMENT,
+ BDH_TRACING_ENUM_LAST);
+ return;
+ }
+ context->RegisterPublicBlobURL(public_url, uuid);
+ public_blob_urls_.insert(public_url);
+}
+
+void BlobDispatcherHost::OnRevokePublicBlobURL(const GURL& public_url) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (!public_url.is_valid()) {
+ bad_message::ReceivedBadMessage(this,
+ bad_message::BDH_INVALID_URL_OPERATION);
+ return;
+ }
+ if (!IsUrlRegisteredInHost(public_url)) {
+ UMA_HISTOGRAM_ENUMERATION("Storage.Blob.InvalidURLRegister", BDH_DECREMENT,
+ BDH_TRACING_ENUM_LAST);
+ return;
+ }
+ context()->RevokePublicBlobURL(public_url);
+ public_blob_urls_.erase(public_url);
+}
+
+storage::BlobStorageContext* BlobDispatcherHost::context() {
+ return blob_storage_context_->context();
+}
+
+void BlobDispatcherHost::SendMemoryRequest(
+ const std::string& uuid,
+ scoped_ptr<std::vector<storage::BlobItemBytesRequest>> requests,
+ scoped_ptr<std::vector<base::SharedMemoryHandle>> memory_handles,
+ scoped_ptr<std::vector<base::File>> files) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ std::vector<IPC::PlatformFileForTransit> file_handles;
+ // TODO(dmurph): Support file-backed blob transportation.
+ DCHECK(files->empty());
+ Send(new BlobStorageMsg_RequestMemoryItem(uuid, *requests, *memory_handles,
+ file_handles));
+}
+
+void BlobDispatcherHost::SendIPCResponse(const std::string& uuid,
+ storage::BlobTransportResult result) {
+ switch (result) {
+ case BlobTransportResult::BAD_IPC:
+ bad_message::ReceivedBadMessage(this,
+ bad_message::BDH_CONSTRUCTION_FAILED);
+ return;
+ case BlobTransportResult::CANCEL_MEMORY_FULL:
+ Send(new BlobStorageMsg_CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY));
+ return;
+ case BlobTransportResult::CANCEL_FILE_ERROR:
+ Send(new BlobStorageMsg_CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::FILE_WRITE_FAILED));
+ return;
+ case BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN:
+ Send(new BlobStorageMsg_CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN));
+ return;
+ case BlobTransportResult::CANCEL_UNKNOWN:
+ Send(new BlobStorageMsg_CancelBuildingBlob(
+ uuid, IPCBlobCreationCancelCode::UNKNOWN));
+ return;
+ case BlobTransportResult::PENDING_RESPONSES:
+ return;
+ case BlobTransportResult::DONE:
+ Send(new BlobStorageMsg_DoneBuildingBlob(uuid));
+ return;
+ }
+ NOTREACHED();
+}
+
+bool BlobDispatcherHost::IsInUseInHost(const std::string& uuid) {
+ return blobs_inuse_map_.find(uuid) != blobs_inuse_map_.end();
+}
+
+bool BlobDispatcherHost::IsUrlRegisteredInHost(const GURL& blob_url) {
+ return public_blob_urls_.find(blob_url) != public_blob_urls_.end();
+}
+
+void BlobDispatcherHost::ClearHostFromBlobStorageContext() {
+ BlobStorageContext* context = this->context();
+ for (const auto& url : public_blob_urls_) {
+ context->RevokePublicBlobURL(url);
+ }
+ for (const auto& uuid_refnum_pair : blobs_inuse_map_) {
+ for (int i = 0; i < uuid_refnum_pair.second; ++i)
+ context->DecrementBlobRefCount(uuid_refnum_pair.first);
+ }
+ async_builder_.CancelAll(context);
+}
+
+} // namespace content