Index: content/browser/blob_storage/blob_dispatcher_host.cc |
diff --git a/content/browser/blob_storage/blob_dispatcher_host.cc b/content/browser/blob_storage/blob_dispatcher_host.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..00ef8b40f71f04826f09ac935af3516c0b318f23 |
--- /dev/null |
+++ b/content/browser/blob_storage/blob_dispatcher_host.cc |
@@ -0,0 +1,333 @@ |
+// 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 "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 { |
+ |
+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)) { |
+ 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_INVALID_OPERATION); |
+ 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. |
+ 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() || !context->registry().HasEntry(uuid)) { |
+ bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION); |
+ return; |
+ } |
+ context->IncrementBlobRefCount(uuid); |
+ blobs_inuse_map_[uuid] += 1; |
+} |
+ |
+void BlobDispatcherHost::OnDecrementBlobRefCount(const std::string& uuid) { |
+ DCHECK_CURRENTLY_ON(BrowserThread::IO); |
+ if (uuid.empty() || !IsInUseInHost(uuid)) { |
+ bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION); |
+ 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() || !IsInUseInHost(uuid) || |
+ context->registry().IsURLMapped(public_url)) { |
+ bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION); |
+ 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 (!IsUrlRegisteredInHost(public_url)) { |
+ bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION); |
+ 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 |