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

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: added shared memory test, and fixed memory leak Created 4 years, 10 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 if (referenced_blob_uuids.find(uuid) != referenced_blob_uuids.end()) {
71 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION);
72 return;
73 }
74 blobs_inuse_map_[uuid] = 1;
75 if (!async_builder_.RegisterBlobUUID(uuid, content_type, content_disposition,
76 referenced_blob_uuids, context)) {
77 // The async builder builds the blob as broken, and we just need to send the
78 // cancel message back to the renderer.
79 Send(new BlobStorageMsg_CancelBuildingBlob(
80 uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN));
81 }
82 }
83
84 void BlobDispatcherHost::OnStartBuildingBlob(
85 const std::string& uuid,
86 const std::vector<storage::DataElement>& descriptions) {
87 DCHECK_CURRENTLY_ON(BrowserThread::IO);
88 BlobTransportResult result = BlobTransportResult::BAD_IPC;
89 if (uuid.empty()) {
90 SendIPCResponse(uuid, result);
91 return;
92 }
93 BlobStorageContext* context = this->context();
94 const BlobStorageRegistry::Entry* entry = context->registry().GetEntry(uuid);
95 if (!entry || entry->state == BlobStorageRegistry::BlobState::BROKEN) {
96 // We ignore messages for blobs that don't exist to handle the case where
97 // the renderer de-refs a blob that we're still constructing, and there are
98 // no references to that blob. We ignore broken as well, in the case where
99 // we decided to break a blob after RegisterBlobUUID is called.
100 return;
101 }
102 if (context->IsBeingBuilt(uuid)) {
103 // We use base::Unretained(this) because the async_builder_ stores a copy
104 // of the callback, creating a circular dependency. Since we're storing the
105 // async builder by value, we're guarenteed that the lifetime of this object
106 // will be greater than the builder.
107 result = async_builder_.StartBuildingBlob(
108 uuid, descriptions, context->memory_available(), context,
109 base::Bind(&BlobDispatcherHost::SendMemoryRequest,
110 base::Unretained(this), uuid));
111 }
112 SendIPCResponse(uuid, result);
113 }
114
115 void BlobDispatcherHost::OnMemoryItemResponse(
116 const std::string& uuid,
117 const std::vector<storage::BlobItemBytesResponse>& responses) {
118 DCHECK_CURRENTLY_ON(BrowserThread::IO);
119 BlobTransportResult result = BlobTransportResult::BAD_IPC;
120 if (uuid.empty()) {
121 SendIPCResponse(uuid, result);
122 return;
123 }
124 BlobStorageContext* context = this->context();
125 const BlobStorageRegistry::Entry* entry = context->registry().GetEntry(uuid);
126 if (!entry || entry->state == BlobStorageRegistry::BlobState::BROKEN) {
127 // We ignore messages for blobs that don't exist to handle the case where
128 // the renderer de-refs a blob that we're still constructing, and there are
129 // no references to that blob. We ignore broken as well, in the case where
130 // we decided to break a blob after sending the memory request.
131 return;
132 }
133 if (IsBeingBuiltInHost(uuid)) {
134 result = async_builder_.OnMemoryResponses(uuid, responses, context);
135 }
136 SendIPCResponse(uuid, result);
137 }
138
139 void BlobDispatcherHost::OnCancelBuildingBlob(
140 const std::string& uuid,
141 const storage::IPCBlobCreationCancelCode code) {
142 DCHECK_CURRENTLY_ON(BrowserThread::IO);
143 if (uuid.empty() || !IsBeingBuiltInHost(uuid)) {
144 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION);
145 return;
146 }
147 VLOG(1) << "Blob construction of " << uuid << " cancelled by renderer. "
148 << " Reason: " << static_cast<int>(code) << ".";
149 async_builder_.CancelBuildingBlob(uuid, code, context());
150 }
151
152 void BlobDispatcherHost::OnIncrementBlobRefCount(const std::string& uuid) {
153 DCHECK_CURRENTLY_ON(BrowserThread::IO);
154 BlobStorageContext* context = this->context();
155 if (uuid.empty() || !context->registry().HasEntry(uuid)) {
156 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION);
157 return;
158 }
159 context->IncrementBlobRefCount(uuid);
160 blobs_inuse_map_[uuid] += 1;
161 }
162
163 void BlobDispatcherHost::OnDecrementBlobRefCount(const std::string& uuid) {
164 DCHECK_CURRENTLY_ON(BrowserThread::IO);
165 if (uuid.empty() || !IsInUseInHost(uuid)) {
166 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION);
167 return;
168 }
169 BlobStorageContext* context = this->context();
170 context->DecrementBlobRefCount(uuid);
171 blobs_inuse_map_[uuid] -= 1;
172 if (blobs_inuse_map_[uuid] == 0) {
173 blobs_inuse_map_.erase(uuid);
174 // If the blob has been deleted in the context and we're still building it,
175 // this means we have no references waiting to read it. Clear the building
176 // state and send a cancel message to the renderer.
177 if (IsBeingBuiltInHost(uuid) && !context->IsBeingBuilt(uuid)) {
178 async_builder_.StopBuildingBlob(uuid);
179 Send(new BlobStorageMsg_CancelBuildingBlob(
180 uuid, IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING));
181 }
182 }
183 }
184
185 void BlobDispatcherHost::OnRegisterPublicBlobURL(const GURL& public_url,
186 const std::string& uuid) {
187 DCHECK_CURRENTLY_ON(BrowserThread::IO);
188 BlobStorageContext* context = this->context();
189 if (uuid.empty() || !IsInUseInHost(uuid) ||
190 context->registry().IsURLMapped(public_url)) {
191 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION);
192 return;
193 }
194 context->RegisterPublicBlobURL(public_url, uuid);
195 public_blob_urls_.insert(public_url);
196 }
197
198 void BlobDispatcherHost::OnRevokePublicBlobURL(const GURL& public_url) {
199 DCHECK_CURRENTLY_ON(BrowserThread::IO);
200 if (!IsUrlRegisteredInHost(public_url)) {
201 bad_message::ReceivedBadMessage(this, bad_message::BDH_INVALID_OPERATION);
202 return;
203 }
204 context()->RevokePublicBlobURL(public_url);
205 public_blob_urls_.erase(public_url);
206 }
207
208 storage::BlobStorageContext* BlobDispatcherHost::context() {
209 return blob_storage_context_->context();
210 }
211
212 void BlobDispatcherHost::SendMemoryRequest(
213 const std::string& uuid,
214 scoped_ptr<std::vector<storage::BlobItemBytesRequest>> requests,
215 scoped_ptr<std::vector<base::SharedMemoryHandle>> memory_handles,
216 scoped_ptr<std::vector<base::File>> files) {
217 DCHECK_CURRENTLY_ON(BrowserThread::IO);
218 std::vector<IPC::PlatformFileForTransit> file_handles;
219 // TODO(dmurph): Support file-backed blob transportation.
220 DCHECK(files->empty());
221 Send(new BlobStorageMsg_RequestMemoryItem(uuid, *requests, *memory_handles,
222 file_handles));
223 }
224
225 void BlobDispatcherHost::SendIPCResponse(const std::string& uuid,
226 storage::BlobTransportResult result) {
227 switch (result) {
228 case BlobTransportResult::BAD_IPC:
229 bad_message::ReceivedBadMessage(this,
230 bad_message::BDH_CONSTRUCTION_FAILED);
231 return;
232 case BlobTransportResult::CANCEL_MEMORY_FULL:
233 Send(new BlobStorageMsg_CancelBuildingBlob(
234 uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY));
235 return;
236 case BlobTransportResult::CANCEL_FILE_ERROR:
237 Send(new BlobStorageMsg_CancelBuildingBlob(
238 uuid, IPCBlobCreationCancelCode::FILE_WRITE_FAILED));
239 return;
240 case BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN:
241 Send(new BlobStorageMsg_CancelBuildingBlob(
242 uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN));
243 return;
244 case BlobTransportResult::CANCEL_UNKNOWN:
245 Send(new BlobStorageMsg_CancelBuildingBlob(
246 uuid, IPCBlobCreationCancelCode::UNKNOWN));
247 return;
248 case BlobTransportResult::PENDING_RESPONSES:
249 return;
250 case BlobTransportResult::DONE:
251 Send(new BlobStorageMsg_DoneBuildingBlob(uuid));
252 return;
253 }
254 NOTREACHED();
255 }
256
257 bool BlobDispatcherHost::IsInUseInHost(const std::string& uuid) {
258 return blobs_inuse_map_.find(uuid) != blobs_inuse_map_.end();
259 }
260
261 bool BlobDispatcherHost::IsBeingBuiltInHost(const std::string& uuid) {
262 return async_builder_.IsBeingBuilt(uuid);
263 }
264
265 bool BlobDispatcherHost::IsUrlRegisteredInHost(const GURL& blob_url) {
266 return public_blob_urls_.find(blob_url) != public_blob_urls_.end();
267 }
268
269 void BlobDispatcherHost::ClearHostFromBlobStorageContext() {
270 BlobStorageContext* context = this->context();
271 for (const auto& url : public_blob_urls_) {
272 context->RevokePublicBlobURL(url);
273 }
274 for (const auto& uuid_refnum_pair : blobs_inuse_map_) {
275 for (int i = 0; i < uuid_refnum_pair.second; ++i)
276 context->DecrementBlobRefCount(uuid_refnum_pair.first);
277 }
278 async_builder_.CancelAll(context);
279 }
280
281 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698