| 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 | 
|---|