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/shared_memory.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/trace_event/trace_event.h"
13 #include "content/child/child_thread_impl.h"
14 #include "content/child/thread_safe_sender.h"
15 #include "content/common/fileapi/webblob_messages.h"
16 #include "third_party/WebKit/public/platform/WebBlobData.h"
17 #include "third_party/WebKit/public/platform/WebString.h"
18 #include "third_party/WebKit/public/platform/WebThreadSafeData.h"
19 #include "third_party/WebKit/public/platform/WebURL.h"
21 using blink::WebBlobData
;
22 using blink::WebString
;
23 using blink::WebThreadSafeData
;
30 const size_t kLargeThresholdBytes
= 250 * 1024;
31 const size_t kMaxSharedMemoryBytes
= 10 * 1024 * 1024;
35 WebBlobRegistryImpl::WebBlobRegistryImpl(ThreadSafeSender
* sender
)
37 // Record a dummy trace event on startup so the 'Storage' category shows up
38 // in the chrome://tracing viewer.
39 TRACE_EVENT0("Blob", "Init");
42 WebBlobRegistryImpl::~WebBlobRegistryImpl() {
45 void WebBlobRegistryImpl::registerBlobData(
46 const blink::WebString
& uuid
, const blink::WebBlobData
& data
) {
47 TRACE_EVENT0("Blob", "Registry::RegisterBlob");
48 const std::string
uuid_str(uuid
.utf8());
50 storage::DataElement data_buffer
;
51 data_buffer
.SetToEmptyBytes();
53 sender_
->Send(new BlobHostMsg_StartBuilding(uuid_str
));
55 WebBlobData::Item data_item
;
56 while (data
.itemAt(i
++, data_item
)) {
57 // NOTE: data_item.length == -1 when we want to use the whole file. This
58 // only happens when we are creating a file object in Blink, and the file
59 // object is the only item in the 'blob'. If we use that file blob to
60 // create another blob, it is sent here as a 'file' item and not a blob,
61 // and the correct size is populated.
62 // static_cast<uint64>(-1) == kuint64max, which is what DataElement uses
63 // to specificy "use the whole file".
64 if (data_item
.length
== 0) {
67 if (data_item
.type
!= WebBlobData::Item::TypeData
&&
68 data_buffer
.length() != 0) {
69 FlushBlobItemBuffer(uuid_str
, &data_buffer
);
71 storage::DataElement item
;
72 switch (data_item
.type
) {
73 case WebBlobData::Item::TypeData
: {
74 // WebBlobData does not allow partial data items.
75 DCHECK(!data_item
.offset
&& data_item
.length
== -1);
76 if (data_item
.data
.size() == 0) {
79 BufferBlobData(uuid_str
, data_item
.data
, &data_buffer
);
82 case WebBlobData::Item::TypeFile
:
83 item
.SetToFilePathRange(
84 base::FilePath::FromUTF16Unsafe(data_item
.filePath
),
85 static_cast<uint64
>(data_item
.offset
),
86 static_cast<uint64
>(data_item
.length
),
87 base::Time::FromDoubleT(data_item
.expectedModificationTime
));
88 sender_
->Send(new BlobHostMsg_AppendBlobDataItem(uuid_str
, item
));
90 case WebBlobData::Item::TypeBlob
:
92 data_item
.blobUUID
.utf8(),
93 static_cast<uint64
>(data_item
.offset
),
94 static_cast<uint64
>(data_item
.length
));
96 new BlobHostMsg_AppendBlobDataItem(uuid_str
, item
));
98 case WebBlobData::Item::TypeFileSystemURL
:
99 // We only support filesystem URL as of now.
100 DCHECK(GURL(data_item
.fileSystemURL
).SchemeIsFileSystem());
101 item
.SetToFileSystemUrlRange(
102 data_item
.fileSystemURL
,
103 static_cast<uint64
>(data_item
.offset
),
104 static_cast<uint64
>(data_item
.length
),
105 base::Time::FromDoubleT(data_item
.expectedModificationTime
));
107 new BlobHostMsg_AppendBlobDataItem(uuid_str
, item
));
113 if (data_buffer
.length() != 0) {
114 FlushBlobItemBuffer(uuid_str
, &data_buffer
);
116 sender_
->Send(new BlobHostMsg_FinishBuilding(
117 uuid_str
, data
.contentType().utf8().data()));
120 void WebBlobRegistryImpl::addBlobDataRef(const WebString
& uuid
) {
121 sender_
->Send(new BlobHostMsg_IncrementRefCount(uuid
.utf8()));
124 void WebBlobRegistryImpl::removeBlobDataRef(const WebString
& uuid
) {
125 sender_
->Send(new BlobHostMsg_DecrementRefCount(uuid
.utf8()));
128 void WebBlobRegistryImpl::registerPublicBlobURL(
129 const WebURL
& url
, const WebString
& uuid
) {
130 sender_
->Send(new BlobHostMsg_RegisterPublicURL(url
, uuid
.utf8()));
133 void WebBlobRegistryImpl::revokePublicBlobURL(const WebURL
& url
) {
134 sender_
->Send(new BlobHostMsg_RevokePublicURL(url
));
137 void WebBlobRegistryImpl::FlushBlobItemBuffer(
138 const std::string
& uuid_str
,
139 storage::DataElement
* data_buffer
) const {
140 DCHECK_NE(data_buffer
->length(), 0ul);
141 DCHECK_LT(data_buffer
->length(), kLargeThresholdBytes
);
142 sender_
->Send(new BlobHostMsg_AppendBlobDataItem(uuid_str
, *data_buffer
));
143 data_buffer
->SetToEmptyBytes();
146 void WebBlobRegistryImpl::BufferBlobData(const std::string
& uuid_str
,
147 const blink::WebThreadSafeData
& data
,
148 storage::DataElement
* data_buffer
) {
149 size_t buffer_size
= data_buffer
->length();
150 size_t data_size
= data
.size();
151 DCHECK_NE(data_size
, 0ul);
152 if (buffer_size
!= 0 && buffer_size
+ data_size
>= kLargeThresholdBytes
) {
153 FlushBlobItemBuffer(uuid_str
, data_buffer
);
156 if (data_size
>= kLargeThresholdBytes
) {
157 TRACE_EVENT0("Blob", "Registry::SendOversizedBlobData");
158 SendOversizedDataForBlob(uuid_str
, data
);
160 DCHECK_LT(buffer_size
+ data_size
, kLargeThresholdBytes
);
161 data_buffer
->AppendBytes(data
.data(), data_size
);
165 void WebBlobRegistryImpl::SendOversizedDataForBlob(
166 const std::string
& uuid_str
,
167 const blink::WebThreadSafeData
& data
) {
168 DCHECK_GE(data
.size(), kLargeThresholdBytes
);
169 // We handle larger amounts of data via SharedMemory instead of
170 // writing it directly to the IPC channel.
171 size_t shared_memory_size
= std::min(data
.size(), kMaxSharedMemoryBytes
);
172 scoped_ptr
<base::SharedMemory
> shared_memory(
173 ChildThreadImpl::AllocateSharedMemory(shared_memory_size
, sender_
.get()));
174 CHECK(shared_memory
.get());
175 if (!shared_memory
->Map(shared_memory_size
))
178 size_t data_size
= data
.size();
179 const char* data_ptr
= data
.data();
181 TRACE_EVENT0("Blob", "Registry::SendOversizedBlobItem");
182 size_t chunk_size
= std::min(data_size
, shared_memory_size
);
183 memcpy(shared_memory
->memory(), data_ptr
, chunk_size
);
184 sender_
->Send(new BlobHostMsg_SyncAppendSharedMemory(
185 uuid_str
, shared_memory
->handle(), chunk_size
));
186 data_size
-= chunk_size
;
187 data_ptr
+= chunk_size
;
191 // ------ streams stuff -----
193 void WebBlobRegistryImpl::registerStreamURL(
194 const WebURL
& url
, const WebString
& content_type
) {
195 DCHECK(ChildThreadImpl::current());
196 sender_
->Send(new StreamHostMsg_StartBuilding(url
, content_type
.utf8()));
199 void WebBlobRegistryImpl::registerStreamURL(
200 const WebURL
& url
, const WebURL
& src_url
) {
201 DCHECK(ChildThreadImpl::current());
202 sender_
->Send(new StreamHostMsg_Clone(url
, src_url
));
205 void WebBlobRegistryImpl::addDataToStream(const WebURL
& url
,
206 const char* data
, size_t length
) {
207 DCHECK(ChildThreadImpl::current());
210 if (length
< kLargeThresholdBytes
) {
211 storage::DataElement item
;
212 item
.SetToBytes(data
, length
);
213 sender_
->Send(new StreamHostMsg_AppendBlobDataItem(url
, item
));
215 // We handle larger amounts of data via SharedMemory instead of
216 // writing it directly to the IPC channel.
217 size_t shared_memory_size
= std::min(
218 length
, kMaxSharedMemoryBytes
);
219 scoped_ptr
<base::SharedMemory
> shared_memory(
220 ChildThreadImpl::AllocateSharedMemory(shared_memory_size
,
222 CHECK(shared_memory
.get());
223 if (!shared_memory
->Map(shared_memory_size
))
226 size_t remaining_bytes
= length
;
227 const char* current_ptr
= data
;
228 while (remaining_bytes
) {
229 size_t chunk_size
= std::min(remaining_bytes
, shared_memory_size
);
230 memcpy(shared_memory
->memory(), current_ptr
, chunk_size
);
231 sender_
->Send(new StreamHostMsg_SyncAppendSharedMemory(
232 url
, shared_memory
->handle(), chunk_size
));
233 remaining_bytes
-= chunk_size
;
234 current_ptr
+= chunk_size
;
239 void WebBlobRegistryImpl::flushStream(const WebURL
& url
) {
240 DCHECK(ChildThreadImpl::current());
241 sender_
->Send(new StreamHostMsg_Flush(url
));
244 void WebBlobRegistryImpl::finalizeStream(const WebURL
& url
) {
245 DCHECK(ChildThreadImpl::current());
246 sender_
->Send(new StreamHostMsg_FinishBuilding(url
));
249 void WebBlobRegistryImpl::abortStream(const WebURL
& url
) {
250 DCHECK(ChildThreadImpl::current());
251 sender_
->Send(new StreamHostMsg_AbortBuilding(url
));
254 void WebBlobRegistryImpl::unregisterStreamURL(const WebURL
& url
) {
255 DCHECK(ChildThreadImpl::current());
256 sender_
->Send(new StreamHostMsg_Remove(url
));
259 } // namespace content