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"
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
;
26 using blink::WebBlobRegistry
;
27 using storage::DataElement
;
33 const size_t kLargeThresholdBytes
= 250 * 1024;
34 const size_t kMaxSharedMemoryBytes
= 10 * 1024 * 1024;
38 WebBlobRegistryImpl::WebBlobRegistryImpl(ThreadSafeSender
* 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
62 WebBlobData::Item data_item
;
63 while (data
.itemAt(i
++, data_item
)) {
64 if (data_item
.length
== 0) {
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
);
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
);
80 case WebBlobData::Item::TypeBlob
:
81 builder
->appendBlob(data_item
.blobUUID
, data_item
.offset
,
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
);
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
,
133 DCHECK(ChildThreadImpl::current());
136 if (length
< kLargeThresholdBytes
) {
138 item
.SetToBytes(data
, length
);
139 sender_
->Send(new StreamHostMsg_AppendBlobDataItem(url
, item
));
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
,
147 CHECK(shared_memory
.get());
148 if (!shared_memory
->Map(shared_memory_size
))
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
,
202 consolidation_
.AddBlobItem(uuid
.utf8(), offset
, length
);
205 void WebBlobRegistryImpl::BuilderImpl::appendFile(
206 const WebString
& path
,
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
,
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
];
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".
244 case DataElement::TYPE_BYTES
:
245 if (item
.length
> kLargeThresholdBytes
) {
246 SendOversizedDataForBlob(i
);
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
));
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
));
263 case DataElement::TYPE_BLOB
:
264 element
.SetToBlobRange(item
.blob_uuid
, item
.offset
, item
.length
);
265 sender_
->Send(new BlobHostMsg_AppendBlobDataItem(uuid_
, element
));
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
));
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.";
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