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.
4
5#include "content/child/webblobregistry_impl.h"
6
7#include "base/files/file_path.h"
8#include "base/guid.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 "content/child/child_thread.h"
13#include "content/child/thread_safe_sender.h"
14#include "content/common/fileapi/webblob_messages.h"
15#include "storage/common/blob/blob_data.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"
20
21using blink::WebBlobData;
22using blink::WebString;
23using blink::WebThreadSafeData;
24using blink::WebURL;
25
26namespace content {
27
28namespace {
29
30const size_t kLargeThresholdBytes = 250 * 1024;
31const size_t kMaxSharedMemoryBytes = 10 * 1024 * 1024;
32
33}  // namespace
34
35WebBlobRegistryImpl::WebBlobRegistryImpl(ThreadSafeSender* sender)
36    : sender_(sender) {
37}
38
39WebBlobRegistryImpl::~WebBlobRegistryImpl() {
40}
41
42void WebBlobRegistryImpl::registerBlobData(
43    const blink::WebString& uuid, const blink::WebBlobData& data) {
44  const std::string uuid_str(uuid.utf8());
45
46  sender_->Send(new BlobHostMsg_StartBuilding(uuid_str));
47  size_t i = 0;
48  WebBlobData::Item data_item;
49  while (data.itemAt(i++, data_item)) {
50    switch (data_item.type) {
51      case WebBlobData::Item::TypeData: {
52        // WebBlobData does not allow partial data items.
53        DCHECK(!data_item.offset && data_item.length == -1);
54        SendDataForBlob(uuid_str, data_item.data);
55        break;
56      }
57      case WebBlobData::Item::TypeFile:
58        if (data_item.length) {
59          storage::BlobData::Item item;
60          item.SetToFilePathRange(
61              base::FilePath::FromUTF16Unsafe(data_item.filePath),
62              static_cast<uint64>(data_item.offset),
63              static_cast<uint64>(data_item.length),
64              base::Time::FromDoubleT(data_item.expectedModificationTime));
65          sender_->Send(
66              new BlobHostMsg_AppendBlobDataItem(uuid_str, item));
67        }
68        break;
69      case WebBlobData::Item::TypeBlob:
70        if (data_item.length) {
71          storage::BlobData::Item item;
72          item.SetToBlobRange(
73              data_item.blobUUID.utf8(),
74              static_cast<uint64>(data_item.offset),
75              static_cast<uint64>(data_item.length));
76          sender_->Send(
77              new BlobHostMsg_AppendBlobDataItem(uuid_str, item));
78        }
79        break;
80      case WebBlobData::Item::TypeFileSystemURL:
81        if (data_item.length) {
82          // We only support filesystem URL as of now.
83          DCHECK(GURL(data_item.fileSystemURL).SchemeIsFileSystem());
84          storage::BlobData::Item item;
85          item.SetToFileSystemUrlRange(
86              data_item.fileSystemURL,
87              static_cast<uint64>(data_item.offset),
88              static_cast<uint64>(data_item.length),
89              base::Time::FromDoubleT(data_item.expectedModificationTime));
90          sender_->Send(
91              new BlobHostMsg_AppendBlobDataItem(uuid_str, item));
92        }
93        break;
94      default:
95        NOTREACHED();
96    }
97  }
98  sender_->Send(new BlobHostMsg_FinishBuilding(
99      uuid_str, data.contentType().utf8().data()));
100}
101
102void WebBlobRegistryImpl::addBlobDataRef(const WebString& uuid) {
103  sender_->Send(new BlobHostMsg_IncrementRefCount(uuid.utf8()));
104}
105
106void WebBlobRegistryImpl::removeBlobDataRef(const WebString& uuid) {
107  sender_->Send(new BlobHostMsg_DecrementRefCount(uuid.utf8()));
108}
109
110void WebBlobRegistryImpl::registerPublicBlobURL(
111    const WebURL& url, const WebString& uuid) {
112  sender_->Send(new BlobHostMsg_RegisterPublicURL(url, uuid.utf8()));
113}
114
115void WebBlobRegistryImpl::revokePublicBlobURL(const WebURL& url) {
116  sender_->Send(new BlobHostMsg_RevokePublicURL(url));
117}
118
119void WebBlobRegistryImpl::SendDataForBlob(const std::string& uuid_str,
120                                          const WebThreadSafeData& data) {
121
122  if (data.size() == 0)
123    return;
124  if (data.size() < kLargeThresholdBytes) {
125    storage::BlobData::Item item;
126    item.SetToBytes(data.data(), data.size());
127    sender_->Send(new BlobHostMsg_AppendBlobDataItem(uuid_str, item));
128  } else {
129    // We handle larger amounts of data via SharedMemory instead of
130    // writing it directly to the IPC channel.
131    size_t shared_memory_size = std::min(
132        data.size(), kMaxSharedMemoryBytes);
133    scoped_ptr<base::SharedMemory> shared_memory(
134        ChildThread::AllocateSharedMemory(shared_memory_size,
135                                          sender_.get()));
136    CHECK(shared_memory.get());
137
138    size_t data_size = data.size();
139    const char* data_ptr = data.data();
140    while (data_size) {
141      size_t chunk_size = std::min(data_size, shared_memory_size);
142      memcpy(shared_memory->memory(), data_ptr, chunk_size);
143      sender_->Send(new BlobHostMsg_SyncAppendSharedMemory(
144          uuid_str, shared_memory->handle(), chunk_size));
145      data_size -= chunk_size;
146      data_ptr += chunk_size;
147    }
148  }
149}
150
151// ------ streams stuff -----
152
153void WebBlobRegistryImpl::registerStreamURL(
154    const WebURL& url, const WebString& content_type) {
155  DCHECK(ChildThread::current());
156  sender_->Send(new StreamHostMsg_StartBuilding(url, content_type.utf8()));
157}
158
159void WebBlobRegistryImpl::registerStreamURL(
160    const WebURL& url, const WebURL& src_url) {
161  DCHECK(ChildThread::current());
162  sender_->Send(new StreamHostMsg_Clone(url, src_url));
163}
164
165void WebBlobRegistryImpl::addDataToStream(const WebURL& url,
166                                          WebThreadSafeData& data) {
167  DCHECK(ChildThread::current());
168  if (data.size() == 0)
169    return;
170  if (data.size() < kLargeThresholdBytes) {
171    storage::BlobData::Item item;
172    item.SetToBytes(data.data(), data.size());
173    sender_->Send(new StreamHostMsg_AppendBlobDataItem(url, item));
174  } else {
175    // We handle larger amounts of data via SharedMemory instead of
176    // writing it directly to the IPC channel.
177    size_t shared_memory_size = std::min(
178        data.size(), kMaxSharedMemoryBytes);
179    scoped_ptr<base::SharedMemory> shared_memory(
180        ChildThread::AllocateSharedMemory(shared_memory_size,
181                                          sender_.get()));
182    CHECK(shared_memory.get());
183
184    size_t data_size = data.size();
185    const char* data_ptr = data.data();
186    while (data_size) {
187      size_t chunk_size = std::min(data_size, shared_memory_size);
188      memcpy(shared_memory->memory(), data_ptr, chunk_size);
189      sender_->Send(new StreamHostMsg_SyncAppendSharedMemory(
190          url, shared_memory->handle(), chunk_size));
191      data_size -= chunk_size;
192      data_ptr += chunk_size;
193    }
194  }
195}
196
197void WebBlobRegistryImpl::finalizeStream(const WebURL& url) {
198  DCHECK(ChildThread::current());
199  sender_->Send(new StreamHostMsg_FinishBuilding(url));
200}
201
202void WebBlobRegistryImpl::abortStream(const WebURL& url) {
203  DCHECK(ChildThread::current());
204  sender_->Send(new StreamHostMsg_AbortBuilding(url));
205}
206
207void WebBlobRegistryImpl::unregisterStreamURL(const WebURL& url) {
208  DCHECK(ChildThread::current());
209  sender_->Send(new StreamHostMsg_Remove(url));
210}
211
212}  // namespace content
213