Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / child / webblobregistry_impl.cc
blob1967c7b151a0e551cf6d0cddb17c1ff5ef0567aa
1 // Copyright 2013 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.
5 #include "content/child/webblobregistry_impl.h"
7 #include "base/files/file_path.h"
8 #include "base/guid.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/memory/shared_memory.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/trace_event/trace_event.h"
14 #include "content/child/child_thread_impl.h"
15 #include "content/child/thread_safe_sender.h"
16 #include "content/common/fileapi/webblob_messages.h"
17 #include "third_party/WebKit/public/platform/WebBlobData.h"
18 #include "third_party/WebKit/public/platform/WebString.h"
19 #include "third_party/WebKit/public/platform/WebThreadSafeData.h"
20 #include "third_party/WebKit/public/platform/WebURL.h"
22 using blink::WebBlobData;
23 using blink::WebString;
24 using blink::WebThreadSafeData;
25 using blink::WebURL;
26 using blink::WebBlobRegistry;
27 using storage::DataElement;
29 namespace content {
31 namespace {
33 const size_t kLargeThresholdBytes = 250 * 1024;
34 const size_t kMaxSharedMemoryBytes = 10 * 1024 * 1024;
36 } // namespace
38 WebBlobRegistryImpl::WebBlobRegistryImpl(ThreadSafeSender* sender)
39 : sender_(sender) {
40 // Record a dummy trace event on startup so the 'Storage' category shows up
41 // in the chrome://tracing viewer.
42 TRACE_EVENT0("Blob", "Init");
45 WebBlobRegistryImpl::~WebBlobRegistryImpl() {
48 blink::WebBlobRegistry::Builder* WebBlobRegistryImpl::createBuilder(
49 const blink::WebString& uuid,
50 const blink::WebString& contentType) {
51 return new BuilderImpl(uuid, contentType, sender_.get());
54 void WebBlobRegistryImpl::registerBlobData(const blink::WebString& uuid,
55 const blink::WebBlobData& data) {
56 TRACE_EVENT0("Blob", "Registry::RegisterBlob");
57 scoped_ptr<Builder> builder(createBuilder(uuid, data.contentType()));
59 // This is temporary until we move to createBuilder() as our blob creation
60 // method.
61 size_t i = 0;
62 WebBlobData::Item data_item;
63 while (data.itemAt(i++, data_item)) {
64 if (data_item.length == 0) {
65 continue;
67 switch (data_item.type) {
68 case WebBlobData::Item::TypeData: {
69 // WebBlobData does not allow partial data items.
70 DCHECK(!data_item.offset && data_item.length == -1);
71 builder->appendData(data_item.data);
72 break;
74 case WebBlobData::Item::TypeFile:
75 builder->appendFile(data_item.filePath,
76 static_cast<uint64_t>(data_item.offset),
77 static_cast<uint64_t>(data_item.length),
78 data_item.expectedModificationTime);
79 break;
80 case WebBlobData::Item::TypeBlob:
81 builder->appendBlob(data_item.blobUUID, data_item.offset,
82 data_item.length);
83 break;
84 case WebBlobData::Item::TypeFileSystemURL:
85 // We only support filesystem URL as of now.
86 DCHECK(GURL(data_item.fileSystemURL).SchemeIsFileSystem());
87 builder->appendFileSystemURL(data_item.fileSystemURL,
88 static_cast<uint64>(data_item.offset),
89 static_cast<uint64>(data_item.length),
90 data_item.expectedModificationTime);
91 break;
92 default:
93 NOTREACHED();
96 builder->build();
99 void WebBlobRegistryImpl::addBlobDataRef(const WebString& uuid) {
100 sender_->Send(new BlobHostMsg_IncrementRefCount(uuid.utf8()));
103 void WebBlobRegistryImpl::removeBlobDataRef(const WebString& uuid) {
104 sender_->Send(new BlobHostMsg_DecrementRefCount(uuid.utf8()));
107 void WebBlobRegistryImpl::registerPublicBlobURL(const WebURL& url,
108 const WebString& uuid) {
109 sender_->Send(new BlobHostMsg_RegisterPublicURL(url, uuid.utf8()));
112 void WebBlobRegistryImpl::revokePublicBlobURL(const WebURL& url) {
113 sender_->Send(new BlobHostMsg_RevokePublicURL(url));
116 // ------ streams stuff -----
118 void WebBlobRegistryImpl::registerStreamURL(const WebURL& url,
119 const WebString& content_type) {
120 DCHECK(ChildThreadImpl::current());
121 sender_->Send(new StreamHostMsg_StartBuilding(url, content_type.utf8()));
124 void WebBlobRegistryImpl::registerStreamURL(const WebURL& url,
125 const WebURL& src_url) {
126 DCHECK(ChildThreadImpl::current());
127 sender_->Send(new StreamHostMsg_Clone(url, src_url));
130 void WebBlobRegistryImpl::addDataToStream(const WebURL& url,
131 const char* data,
132 size_t length) {
133 DCHECK(ChildThreadImpl::current());
134 if (length == 0)
135 return;
136 if (length < kLargeThresholdBytes) {
137 DataElement item;
138 item.SetToBytes(data, length);
139 sender_->Send(new StreamHostMsg_AppendBlobDataItem(url, item));
140 } else {
141 // We handle larger amounts of data via SharedMemory instead of
142 // writing it directly to the IPC channel.
143 size_t shared_memory_size = std::min(length, kMaxSharedMemoryBytes);
144 scoped_ptr<base::SharedMemory> shared_memory(
145 ChildThreadImpl::AllocateSharedMemory(shared_memory_size,
146 sender_.get()));
147 CHECK(shared_memory.get());
148 if (!shared_memory->Map(shared_memory_size))
149 CHECK(false);
151 size_t remaining_bytes = length;
152 const char* current_ptr = data;
153 while (remaining_bytes) {
154 size_t chunk_size = std::min(remaining_bytes, shared_memory_size);
155 memcpy(shared_memory->memory(), current_ptr, chunk_size);
156 sender_->Send(new StreamHostMsg_SyncAppendSharedMemory(
157 url, shared_memory->handle(), chunk_size));
158 remaining_bytes -= chunk_size;
159 current_ptr += chunk_size;
164 void WebBlobRegistryImpl::flushStream(const WebURL& url) {
165 DCHECK(ChildThreadImpl::current());
166 sender_->Send(new StreamHostMsg_Flush(url));
169 void WebBlobRegistryImpl::finalizeStream(const WebURL& url) {
170 DCHECK(ChildThreadImpl::current());
171 sender_->Send(new StreamHostMsg_FinishBuilding(url));
174 void WebBlobRegistryImpl::abortStream(const WebURL& url) {
175 DCHECK(ChildThreadImpl::current());
176 sender_->Send(new StreamHostMsg_AbortBuilding(url));
179 void WebBlobRegistryImpl::unregisterStreamURL(const WebURL& url) {
180 DCHECK(ChildThreadImpl::current());
181 sender_->Send(new StreamHostMsg_Remove(url));
184 WebBlobRegistryImpl::BuilderImpl::BuilderImpl(
185 const blink::WebString& uuid,
186 const blink::WebString& content_type,
187 ThreadSafeSender* sender)
188 : uuid_(uuid.utf8()), content_type_(content_type.utf8()), sender_(sender) {
191 WebBlobRegistryImpl::BuilderImpl::~BuilderImpl() {
194 void WebBlobRegistryImpl::BuilderImpl::appendData(
195 const WebThreadSafeData& data) {
196 consolidation_.AddDataItem(data);
199 void WebBlobRegistryImpl::BuilderImpl::appendBlob(const WebString& uuid,
200 uint64_t offset,
201 uint64_t length) {
202 consolidation_.AddBlobItem(uuid.utf8(), offset, length);
205 void WebBlobRegistryImpl::BuilderImpl::appendFile(
206 const WebString& path,
207 uint64_t offset,
208 uint64_t length,
209 double expected_modification_time) {
210 consolidation_.AddFileItem(
211 base::FilePath::FromUTF16Unsafe(base::string16(path)), offset, length,
212 expected_modification_time);
215 void WebBlobRegistryImpl::BuilderImpl::appendFileSystemURL(
216 const WebURL& fileSystemURL,
217 uint64_t offset,
218 uint64_t length,
219 double expected_modification_time) {
220 DCHECK(GURL(fileSystemURL).SchemeIsFileSystem());
221 consolidation_.AddFileSystemItem(GURL(fileSystemURL), offset, length,
222 expected_modification_time);
225 void WebBlobRegistryImpl::BuilderImpl::build() {
226 sender_->Send(new BlobHostMsg_StartBuilding(uuid_));
227 const auto& items = consolidation_.consolidated_items();
229 // We still need a buffer to hold the continuous block of data so the
230 // DataElement can hold it.
231 size_t buffer_size = 0;
232 scoped_ptr<char[]> buffer;
233 for (size_t i = 0; i < items.size(); i++) {
234 const BlobConsolidation::ConsolidatedItem& item = items[i];
235 DataElement element;
236 // NOTE: length == -1 when we want to use the whole file. This
237 // only happens when we are creating a file object in Blink, and the file
238 // object is the only item in the 'blob'. If we use that file blob to
239 // create another blob, it is sent here as a 'file' item and not a blob,
240 // and the correct size is populated.
241 // static_cast<uint64>(-1) == kuint64max, which is what DataElement uses
242 // to specificy "use the whole file".
243 switch (item.type) {
244 case DataElement::TYPE_BYTES:
245 if (item.length > kLargeThresholdBytes) {
246 SendOversizedDataForBlob(i);
247 break;
249 if (buffer_size < item.length) {
250 buffer.reset(new char[item.length]);
251 buffer_size = item.length;
253 consolidation_.ReadMemory(i, 0, item.length, buffer.get());
254 element.SetToSharedBytes(buffer.get(), item.length);
255 sender_->Send(new BlobHostMsg_AppendBlobDataItem(uuid_, element));
256 break;
257 case DataElement::TYPE_FILE:
258 element.SetToFilePathRange(
259 item.path, item.offset, item.length,
260 base::Time::FromDoubleT(item.expected_modification_time));
261 sender_->Send(new BlobHostMsg_AppendBlobDataItem(uuid_, element));
262 break;
263 case DataElement::TYPE_BLOB:
264 element.SetToBlobRange(item.blob_uuid, item.offset, item.length);
265 sender_->Send(new BlobHostMsg_AppendBlobDataItem(uuid_, element));
266 break;
267 case DataElement::TYPE_FILE_FILESYSTEM:
268 element.SetToFileSystemUrlRange(
269 item.filesystem_url, item.offset, item.length,
270 base::Time::FromDoubleT(item.expected_modification_time));
271 sender_->Send(new BlobHostMsg_AppendBlobDataItem(uuid_, element));
272 break;
273 default:
274 NOTREACHED();
277 sender_->Send(new BlobHostMsg_FinishBuilding(uuid_, content_type_));
280 void WebBlobRegistryImpl::BuilderImpl::SendOversizedDataForBlob(
281 size_t consolidated_item_index) {
282 TRACE_EVENT0("Blob", "Registry::SendOversizedBlobData");
283 const BlobConsolidation::ConsolidatedItem& item =
284 consolidation_.consolidated_items()[consolidated_item_index];
285 // We handle larger amounts of data via SharedMemory instead of
286 // writing it directly to the IPC channel.
288 size_t data_size = item.length;
289 size_t shared_memory_size = std::min(data_size, kMaxSharedMemoryBytes);
290 scoped_ptr<base::SharedMemory> shared_memory(
291 ChildThreadImpl::AllocateSharedMemory(shared_memory_size, sender_.get()));
292 CHECK(shared_memory.get());
293 const bool mapped = shared_memory->Map(shared_memory_size);
294 CHECK(mapped) << "Unable to map shared memory.";
296 size_t offset = 0;
297 while (data_size) {
298 TRACE_EVENT0("Blob", "Registry::SendOversizedBlobItem");
299 size_t chunk_size = std::min(data_size, shared_memory_size);
300 consolidation_.ReadMemory(consolidated_item_index, offset, chunk_size,
301 shared_memory->memory());
302 sender_->Send(new BlobHostMsg_SyncAppendSharedMemory(
303 uuid_, shared_memory->handle(), chunk_size));
304 data_size -= chunk_size;
305 offset += chunk_size;
309 } // namespace content