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

Side by Side Diff: content/browser/blob_storage/blob_dispatcher_host.cc

Issue 1234813004: [BlobAsync] Asynchronous Blob Construction Final Patch (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@blob-protocol-change
Patch Set: comments Created 4 years, 9 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 unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698