OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "content/browser/blob_storage/blob_dispatcher_host.h" |
| 6 |
| 7 #include <algorithm> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "content/browser/bad_message.h" |
| 11 #include "content/browser/fileapi/chrome_blob_storage_context.h" |
| 12 #include "content/common/fileapi/webblob_messages.h" |
| 13 #include "ipc/ipc_platform_file.h" |
| 14 #include "storage/browser/blob/blob_storage_context.h" |
| 15 #include "storage/browser/blob/blob_transport_result.h" |
| 16 #include "storage/common/blob_storage/blob_item_bytes_request.h" |
| 17 #include "storage/common/blob_storage/blob_item_bytes_response.h" |
| 18 #include "storage/common/data_element.h" |
| 19 #include "url/gurl.h" |
| 20 |
| 21 using storage::BlobStorageContext; |
| 22 using storage::BlobStorageRegistry; |
| 23 using storage::BlobTransportResult; |
| 24 using storage::IPCBlobCreationCancelCode; |
| 25 |
| 26 namespace content { |
| 27 |
| 28 BlobDispatcherHost::BlobDispatcherHost( |
| 29 ChromeBlobStorageContext* blob_storage_context) |
| 30 : BrowserMessageFilter(BlobMsgStart), |
| 31 blob_storage_context_(blob_storage_context) {} |
| 32 |
| 33 BlobDispatcherHost::~BlobDispatcherHost() { |
| 34 ClearHostFromBlobStorageContext(); |
| 35 } |
| 36 |
| 37 void BlobDispatcherHost::OnChannelClosing() { |
| 38 ClearHostFromBlobStorageContext(); |
| 39 public_blob_urls_.clear(); |
| 40 blobs_inuse_map_.clear(); |
| 41 } |
| 42 |
| 43 bool BlobDispatcherHost::OnMessageReceived(const IPC::Message& message) { |
| 44 bool handled = true; |
| 45 IPC_BEGIN_MESSAGE_MAP(BlobDispatcherHost, message) |
| 46 IPC_MESSAGE_HANDLER(BlobStorageMsg_RegisterBlobUUID, OnRegisterBlobUUID) |
| 47 IPC_MESSAGE_HANDLER(BlobStorageMsg_StartBuildingBlob, OnStartBuildingBlob) |
| 48 IPC_MESSAGE_HANDLER(BlobStorageMsg_MemoryItemResponse, OnMemoryItemResponse) |
| 49 IPC_MESSAGE_HANDLER(BlobStorageMsg_CancelBuildingBlob, OnCancelBuildingBlob) |
| 50 IPC_MESSAGE_HANDLER(BlobHostMsg_IncrementRefCount, OnIncrementBlobRefCount) |
| 51 IPC_MESSAGE_HANDLER(BlobHostMsg_DecrementRefCount, OnDecrementBlobRefCount) |
| 52 IPC_MESSAGE_HANDLER(BlobHostMsg_RegisterPublicURL, OnRegisterPublicBlobURL) |
| 53 IPC_MESSAGE_HANDLER(BlobHostMsg_RevokePublicURL, OnRevokePublicBlobURL) |
| 54 IPC_MESSAGE_UNHANDLED(handled = false) |
| 55 IPC_END_MESSAGE_MAP() |
| 56 return handled; |
| 57 } |
| 58 |
| 59 void BlobDispatcherHost::OnRegisterBlobUUID( |
| 60 const std::string& uuid, |
| 61 const std::string& content_type, |
| 62 const std::string& content_disposition, |
| 63 const std::set<std::string>& referenced_blob_uuids) { |
| 64 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 65 BlobStorageContext* context = this->context(); |
| 66 if (uuid.empty() || context->registry().HasEntry(uuid)) { |
| 67 bad_message::ReceivedBadMessage(this, bad_message::BDH_UUID_REGISTERED); |
| 68 return; |
| 69 } |
| 70 blobs_inuse_map_[uuid] = 1; |
| 71 BlobTransportResult result = async_builder_.RegisterBlobUUID( |
| 72 uuid, content_type, content_disposition, referenced_blob_uuids, context); |
| 73 switch (result) { |
| 74 case BlobTransportResult::BAD_IPC: |
| 75 blobs_inuse_map_.erase(uuid); |
| 76 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION); |
| 77 break; |
| 78 ; |
| 79 case BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN: |
| 80 // The async builder builds the blob as broken, and we just need to send |
| 81 // the cancel message back to the renderer. |
| 82 Send(new BlobStorageMsg_CancelBuildingBlob( |
| 83 uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN)); |
| 84 break; |
| 85 case BlobTransportResult::DONE: |
| 86 break; |
| 87 case BlobTransportResult::CANCEL_MEMORY_FULL: |
| 88 case BlobTransportResult::CANCEL_FILE_ERROR: |
| 89 case BlobTransportResult::CANCEL_UNKNOWN: |
| 90 case BlobTransportResult::PENDING_RESPONSES: |
| 91 NOTREACHED(); |
| 92 break; |
| 93 } |
| 94 } |
| 95 |
| 96 void BlobDispatcherHost::OnStartBuildingBlob( |
| 97 const std::string& uuid, |
| 98 const std::vector<storage::DataElement>& descriptions) { |
| 99 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 100 if (uuid.empty()) { |
| 101 SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); |
| 102 return; |
| 103 } |
| 104 BlobStorageContext* context = this->context(); |
| 105 const BlobStorageRegistry::Entry* entry = context->registry().GetEntry(uuid); |
| 106 if (!entry || entry->state == BlobStorageRegistry::BlobState::BROKEN) { |
| 107 // We ignore messages for blobs that don't exist to handle the case where |
| 108 // the renderer de-refs a blob that we're still constructing, and there are |
| 109 // no references to that blob. We ignore broken as well, in the case where |
| 110 // we decided to break a blob after RegisterBlobUUID is called. |
| 111 // Second, if the last dereference of the blob happened on a different host, |
| 112 // then we still haven't gotten rid of the 'building' state in the original |
| 113 // host. So we call cancel, and send the message just in case that happens. |
| 114 if (async_builder_.IsBeingBuilt(uuid)) { |
| 115 async_builder_.CancelBuildingBlob( |
| 116 uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING, |
| 117 context); |
| 118 Send(new BlobStorageMsg_CancelBuildingBlob( |
| 119 uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING)); |
| 120 } |
| 121 return; |
| 122 } |
| 123 if (!async_builder_.IsBeingBuilt(uuid)) { |
| 124 SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); |
| 125 return; |
| 126 } |
| 127 // |this| owns async_builder_ so using base::Unretained(this) is safe. |
| 128 BlobTransportResult result = async_builder_.StartBuildingBlob( |
| 129 uuid, descriptions, context->memory_available(), context, |
| 130 base::Bind(&BlobDispatcherHost::SendMemoryRequest, base::Unretained(this), |
| 131 uuid)); |
| 132 SendIPCResponse(uuid, result); |
| 133 } |
| 134 |
| 135 void BlobDispatcherHost::OnMemoryItemResponse( |
| 136 const std::string& uuid, |
| 137 const std::vector<storage::BlobItemBytesResponse>& responses) { |
| 138 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 139 if (uuid.empty()) { |
| 140 SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); |
| 141 return; |
| 142 } |
| 143 BlobStorageContext* context = this->context(); |
| 144 const BlobStorageRegistry::Entry* entry = context->registry().GetEntry(uuid); |
| 145 if (!entry || entry->state == BlobStorageRegistry::BlobState::BROKEN) { |
| 146 // We ignore messages for blobs that don't exist to handle the case where |
| 147 // the renderer de-refs a blob that we're still constructing, and there are |
| 148 // no references to that blob. We ignore broken as well, in the case where |
| 149 // we decided to break a blob after sending the memory request. |
| 150 // Note: if a blob is broken, then it can't be in the async_builder. |
| 151 // Second, if the last dereference of the blob happened on a different host, |
| 152 // then we still haven't gotten rid of the 'building' state in the original |
| 153 // host. So we call cancel, and send the message just in case that happens. |
| 154 if (async_builder_.IsBeingBuilt(uuid)) { |
| 155 async_builder_.CancelBuildingBlob( |
| 156 uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING, |
| 157 context); |
| 158 Send(new BlobStorageMsg_CancelBuildingBlob( |
| 159 uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING)); |
| 160 } |
| 161 return; |
| 162 } |
| 163 if (!async_builder_.IsBeingBuilt(uuid)) { |
| 164 SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); |
| 165 return; |
| 166 } |
| 167 BlobTransportResult result = |
| 168 async_builder_.OnMemoryResponses(uuid, responses, context); |
| 169 SendIPCResponse(uuid, result); |
| 170 } |
| 171 |
| 172 void BlobDispatcherHost::OnCancelBuildingBlob( |
| 173 const std::string& uuid, |
| 174 const storage::IPCBlobCreationCancelCode code) { |
| 175 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 176 if (uuid.empty()) { |
| 177 SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); |
| 178 return; |
| 179 } |
| 180 BlobStorageContext* context = this->context(); |
| 181 const BlobStorageRegistry::Entry* entry = context->registry().GetEntry(uuid); |
| 182 if (!entry || entry->state == BlobStorageRegistry::BlobState::BROKEN) { |
| 183 // We ignore messages for blobs that don't exist to handle the case where |
| 184 // the renderer de-refs a blob that we're still constructing, and there are |
| 185 // no references to that blob. We ignore broken as well, in the case where |
| 186 // we decided to break a blob and the renderer also decided to cancel. |
| 187 // Note: if a blob is broken, then it can't be in the async_builder. |
| 188 // Second, if the last dereference of the blob happened on a different host, |
| 189 // then we still haven't gotten rid of the 'building' state in the original |
| 190 // host. So we call cancel just in case this happens. |
| 191 async_builder_.CancelBuildingBlob( |
| 192 uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING, |
| 193 context); |
| 194 return; |
| 195 } |
| 196 if (!async_builder_.IsBeingBuilt(uuid)) { |
| 197 SendIPCResponse(uuid, BlobTransportResult::BAD_IPC); |
| 198 return; |
| 199 } |
| 200 VLOG(1) << "Blob construction of " << uuid << " cancelled by renderer. " |
| 201 << " Reason: " << static_cast<int>(code) << "."; |
| 202 async_builder_.CancelBuildingBlob(uuid, code, context); |
| 203 } |
| 204 |
| 205 void BlobDispatcherHost::OnIncrementBlobRefCount(const std::string& uuid) { |
| 206 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 207 BlobStorageContext* context = this->context(); |
| 208 if (uuid.empty() || !context->registry().HasEntry(uuid)) { |
| 209 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION); |
| 210 return; |
| 211 } |
| 212 context->IncrementBlobRefCount(uuid); |
| 213 blobs_inuse_map_[uuid] += 1; |
| 214 } |
| 215 |
| 216 void BlobDispatcherHost::OnDecrementBlobRefCount(const std::string& uuid) { |
| 217 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 218 if (uuid.empty() || !IsInUseInHost(uuid)) { |
| 219 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION); |
| 220 return; |
| 221 } |
| 222 BlobStorageContext* context = this->context(); |
| 223 context->DecrementBlobRefCount(uuid); |
| 224 blobs_inuse_map_[uuid] -= 1; |
| 225 if (blobs_inuse_map_[uuid] == 0) { |
| 226 blobs_inuse_map_.erase(uuid); |
| 227 // If the blob has been deleted in the context and we're still building it, |
| 228 // this means we have no references waiting to read it. Clear the building |
| 229 // state and send a cancel message to the renderer. |
| 230 if (async_builder_.IsBeingBuilt(uuid) && |
| 231 !context->registry().HasEntry(uuid)) { |
| 232 async_builder_.CancelBuildingBlob( |
| 233 uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING, |
| 234 context); |
| 235 Send(new BlobStorageMsg_CancelBuildingBlob( |
| 236 uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING)); |
| 237 } |
| 238 } |
| 239 } |
| 240 |
| 241 void BlobDispatcherHost::OnRegisterPublicBlobURL(const GURL& public_url, |
| 242 const std::string& uuid) { |
| 243 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 244 BlobStorageContext* context = this->context(); |
| 245 if (uuid.empty() || !IsInUseInHost(uuid) || |
| 246 context->registry().IsURLMapped(public_url)) { |
| 247 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION); |
| 248 return; |
| 249 } |
| 250 context->RegisterPublicBlobURL(public_url, uuid); |
| 251 public_blob_urls_.insert(public_url); |
| 252 } |
| 253 |
| 254 void BlobDispatcherHost::OnRevokePublicBlobURL(const GURL& public_url) { |
| 255 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 256 if (!IsUrlRegisteredInHost(public_url)) { |
| 257 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION); |
| 258 return; |
| 259 } |
| 260 context()->RevokePublicBlobURL(public_url); |
| 261 public_blob_urls_.erase(public_url); |
| 262 } |
| 263 |
| 264 storage::BlobStorageContext* BlobDispatcherHost::context() { |
| 265 return blob_storage_context_->context(); |
| 266 } |
| 267 |
| 268 void BlobDispatcherHost::SendMemoryRequest( |
| 269 const std::string& uuid, |
| 270 scoped_ptr<std::vector<storage::BlobItemBytesRequest>> requests, |
| 271 scoped_ptr<std::vector<base::SharedMemoryHandle>> memory_handles, |
| 272 scoped_ptr<std::vector<base::File>> files) { |
| 273 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 274 std::vector<IPC::PlatformFileForTransit> file_handles; |
| 275 // TODO(dmurph): Support file-backed blob transportation. |
| 276 DCHECK(files->empty()); |
| 277 Send(new BlobStorageMsg_RequestMemoryItem(uuid, *requests, *memory_handles, |
| 278 file_handles)); |
| 279 } |
| 280 |
| 281 void BlobDispatcherHost::SendIPCResponse(const std::string& uuid, |
| 282 storage::BlobTransportResult result) { |
| 283 switch (result) { |
| 284 case BlobTransportResult::BAD_IPC: |
| 285 bad_message::ReceivedBadMessage(this, |
| 286 bad_message::BDH_CONSTRUCTION_FAILED); |
| 287 return; |
| 288 case BlobTransportResult::CANCEL_MEMORY_FULL: |
| 289 Send(new BlobStorageMsg_CancelBuildingBlob( |
| 290 uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY)); |
| 291 return; |
| 292 case BlobTransportResult::CANCEL_FILE_ERROR: |
| 293 Send(new BlobStorageMsg_CancelBuildingBlob( |
| 294 uuid, IPCBlobCreationCancelCode::FILE_WRITE_FAILED)); |
| 295 return; |
| 296 case BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN: |
| 297 Send(new BlobStorageMsg_CancelBuildingBlob( |
| 298 uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN)); |
| 299 return; |
| 300 case BlobTransportResult::CANCEL_UNKNOWN: |
| 301 Send(new BlobStorageMsg_CancelBuildingBlob( |
| 302 uuid, IPCBlobCreationCancelCode::UNKNOWN)); |
| 303 return; |
| 304 case BlobTransportResult::PENDING_RESPONSES: |
| 305 return; |
| 306 case BlobTransportResult::DONE: |
| 307 Send(new BlobStorageMsg_DoneBuildingBlob(uuid)); |
| 308 return; |
| 309 } |
| 310 NOTREACHED(); |
| 311 } |
| 312 |
| 313 bool BlobDispatcherHost::IsInUseInHost(const std::string& uuid) { |
| 314 return blobs_inuse_map_.find(uuid) != blobs_inuse_map_.end(); |
| 315 } |
| 316 |
| 317 bool BlobDispatcherHost::IsUrlRegisteredInHost(const GURL& blob_url) { |
| 318 return public_blob_urls_.find(blob_url) != public_blob_urls_.end(); |
| 319 } |
| 320 |
| 321 void BlobDispatcherHost::ClearHostFromBlobStorageContext() { |
| 322 BlobStorageContext* context = this->context(); |
| 323 for (const auto& url : public_blob_urls_) { |
| 324 context->RevokePublicBlobURL(url); |
| 325 } |
| 326 for (const auto& uuid_refnum_pair : blobs_inuse_map_) { |
| 327 for (int i = 0; i < uuid_refnum_pair.second; ++i) |
| 328 context->DecrementBlobRefCount(uuid_refnum_pair.first); |
| 329 } |
| 330 async_builder_.CancelAll(context); |
| 331 } |
| 332 |
| 333 } // namespace content |
OLD | NEW |