Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / renderer_host / pepper / pepper_file_system_browser_host.cc
blob0284ba405ec81d1468173abd94f442a6d57cd83e
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/browser/renderer_host/pepper/pepper_file_system_browser_host.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "content/browser/renderer_host/pepper/pepper_file_io_host.h"
10 #include "content/browser/renderer_host/pepper/quota_reservation.h"
11 #include "content/public/browser/browser_ppapi_host.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/plugin_service.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/storage_partition.h"
16 #include "content/public/common/pepper_plugin_info.h"
17 #include "net/base/mime_util.h"
18 #include "ppapi/c/pp_errors.h"
19 #include "ppapi/host/dispatch_host_message.h"
20 #include "ppapi/host/ppapi_host.h"
21 #include "ppapi/proxy/ppapi_messages.h"
22 #include "ppapi/shared_impl/file_system_util.h"
23 #include "ppapi/shared_impl/file_type_conversion.h"
24 #include "webkit/browser/fileapi/file_system_operation_runner.h"
25 #include "webkit/browser/fileapi/isolated_context.h"
26 #include "webkit/browser/quota/quota_manager_proxy.h"
27 #include "webkit/common/fileapi/file_system_util.h"
28 #include "webkit/common/quota/quota_types.h"
30 namespace content {
32 namespace {
34 // This is the minimum amount of quota we reserve per file system.
35 const int64_t kMinimumQuotaReservationSize = 1024 * 1024; // 1 MB
37 scoped_refptr<fileapi::FileSystemContext> GetFileSystemContextFromRenderId(
38 int render_process_id) {
39 DCHECK_CURRENTLY_ON(BrowserThread::UI);
40 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
41 if (!host)
42 return NULL;
43 StoragePartition* storage_partition = host->GetStoragePartition();
44 if (!storage_partition)
45 return NULL;
46 return storage_partition->GetFileSystemContext();
49 } // namespace
51 PepperFileSystemBrowserHost::PepperFileSystemBrowserHost(BrowserPpapiHost* host,
52 PP_Instance instance,
53 PP_Resource resource,
54 PP_FileSystemType type)
55 : ResourceHost(host->GetPpapiHost(), instance, resource),
56 browser_ppapi_host_(host),
57 type_(type),
58 called_open_(false),
59 opened_(false),
60 file_system_context_(NULL),
61 reserved_quota_(0),
62 reserving_quota_(false),
63 weak_factory_(this) {}
65 PepperFileSystemBrowserHost::~PepperFileSystemBrowserHost() {
66 // If |files_| is not empty, the plugin failed to close some files. It must
67 // have crashed.
68 if (!files_.empty()) {
69 file_system_context_->default_file_task_runner()->PostTask(
70 FROM_HERE,
71 base::Bind(&QuotaReservation::OnClientCrash, quota_reservation_));
74 // All FileRefs and FileIOs that reference us must have been destroyed. Cancel
75 // all pending file system operations.
76 if (file_system_operation_runner_)
77 file_system_operation_runner_->Shutdown();
80 void PepperFileSystemBrowserHost::OpenExisting(const GURL& root_url,
81 const base::Closure& callback) {
82 root_url_ = root_url;
83 int render_process_id = 0;
84 int unused;
85 if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
86 pp_instance(), &render_process_id, &unused)) {
87 NOTREACHED();
89 called_open_ = true;
90 // Get the file system context asynchronously, and then complete the Open
91 // operation by calling |callback|.
92 BrowserThread::PostTaskAndReplyWithResult(
93 BrowserThread::UI,
94 FROM_HERE,
95 base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
96 base::Bind(&PepperFileSystemBrowserHost::OpenExistingFileSystem,
97 weak_factory_.GetWeakPtr(),
98 callback));
101 int32_t PepperFileSystemBrowserHost::OnResourceMessageReceived(
102 const IPC::Message& msg,
103 ppapi::host::HostMessageContext* context) {
104 IPC_BEGIN_MESSAGE_MAP(PepperFileSystemBrowserHost, msg)
105 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileSystem_Open, OnHostMsgOpen)
106 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
107 PpapiHostMsg_FileSystem_InitIsolatedFileSystem,
108 OnHostMsgInitIsolatedFileSystem)
109 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileSystem_ReserveQuota,
110 OnHostMsgReserveQuota)
111 IPC_END_MESSAGE_MAP()
112 return PP_ERROR_FAILED;
115 bool PepperFileSystemBrowserHost::IsFileSystemHost() { return true; }
117 void PepperFileSystemBrowserHost::OpenQuotaFile(
118 PepperFileIOHost* file_io_host,
119 const fileapi::FileSystemURL& url,
120 const OpenQuotaFileCallback& callback) {
121 int32_t id = file_io_host->pp_resource();
122 std::pair<FileMap::iterator, bool> insert_result =
123 files_.insert(std::make_pair(id, file_io_host));
124 if (insert_result.second) {
125 base::PostTaskAndReplyWithResult(
126 file_system_context_->default_file_task_runner(),
127 FROM_HERE,
128 base::Bind(&QuotaReservation::OpenFile, quota_reservation_, id, url),
129 callback);
130 } else {
131 NOTREACHED();
135 void PepperFileSystemBrowserHost::CloseQuotaFile(
136 PepperFileIOHost* file_io_host,
137 const ppapi::FileGrowth& file_growth) {
138 int32_t id = file_io_host->pp_resource();
139 FileMap::iterator it = files_.find(id);
140 if (it != files_.end()) {
141 files_.erase(it);
142 } else {
143 NOTREACHED();
144 return;
147 file_system_context_->default_file_task_runner()->PostTask(
148 FROM_HERE,
149 base::Bind(
150 &QuotaReservation::CloseFile, quota_reservation_, id, file_growth));
153 int32_t PepperFileSystemBrowserHost::OnHostMsgOpen(
154 ppapi::host::HostMessageContext* context,
155 int64_t /* unused */) {
156 // TODO(raymes): The file system size is now unused by FileSystemDispatcher.
157 // Figure out why. Why is the file system size signed?
159 // Not allow multiple opens.
160 if (called_open_)
161 return PP_ERROR_INPROGRESS;
162 called_open_ = true;
164 fileapi::FileSystemType file_system_type =
165 ppapi::PepperFileSystemTypeToFileSystemType(type_);
166 if (file_system_type == fileapi::kFileSystemTypeUnknown)
167 return PP_ERROR_FAILED;
169 int render_process_id = 0;
170 int unused;
171 if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
172 pp_instance(), &render_process_id, &unused)) {
173 return PP_ERROR_FAILED;
176 BrowserThread::PostTaskAndReplyWithResult(
177 BrowserThread::UI,
178 FROM_HERE,
179 base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
180 base::Bind(&PepperFileSystemBrowserHost::OpenFileSystem,
181 weak_factory_.GetWeakPtr(),
182 context->MakeReplyMessageContext(),
183 file_system_type));
184 return PP_OK_COMPLETIONPENDING;
187 void PepperFileSystemBrowserHost::OpenExistingFileSystem(
188 const base::Closure& callback,
189 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
190 if (file_system_context.get()) {
191 opened_ = true;
192 } else {
193 // If there is no file system context, we log a warning and continue with an
194 // invalid resource (which will produce errors when used), since we have no
195 // way to communicate the error to the caller.
196 LOG(WARNING) << "Could not retrieve file system context.";
198 SetFileSystemContext(file_system_context);
200 if (ShouldCreateQuotaReservation())
201 CreateQuotaReservation(callback);
202 else
203 callback.Run();
206 void PepperFileSystemBrowserHost::OpenFileSystem(
207 ppapi::host::ReplyMessageContext reply_context,
208 fileapi::FileSystemType file_system_type,
209 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
210 if (!file_system_context.get()) {
211 OpenFileSystemComplete(
212 reply_context, GURL(), std::string(), base::File::FILE_ERROR_FAILED);
213 return;
216 SetFileSystemContext(file_system_context);
218 GURL origin =
219 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin();
220 file_system_context_->OpenFileSystem(
221 origin,
222 file_system_type,
223 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
224 base::Bind(&PepperFileSystemBrowserHost::OpenFileSystemComplete,
225 weak_factory_.GetWeakPtr(),
226 reply_context));
229 void PepperFileSystemBrowserHost::OpenFileSystemComplete(
230 ppapi::host::ReplyMessageContext reply_context,
231 const GURL& root,
232 const std::string& /* unused */,
233 base::File::Error error) {
234 int32 pp_error = ppapi::FileErrorToPepperError(error);
235 if (pp_error == PP_OK) {
236 opened_ = true;
237 root_url_ = root;
239 if (ShouldCreateQuotaReservation()) {
240 CreateQuotaReservation(
241 base::Bind(&PepperFileSystemBrowserHost::SendReplyForFileSystem,
242 weak_factory_.GetWeakPtr(),
243 reply_context,
244 static_cast<int32_t>(PP_OK)));
245 return;
248 SendReplyForFileSystem(reply_context, pp_error);
251 void PepperFileSystemBrowserHost::OpenIsolatedFileSystem(
252 ppapi::host::ReplyMessageContext reply_context,
253 const std::string& fsid,
254 PP_IsolatedFileSystemType_Private type,
255 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
256 if (!file_system_context.get()) {
257 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
258 return;
260 SetFileSystemContext(file_system_context);
262 root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
263 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
264 fsid,
265 ppapi::IsolatedFileSystemTypeToRootName(type)));
266 if (!root_url_.is_valid()) {
267 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
268 return;
271 switch (type) {
272 case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_CRX:
273 opened_ = true;
274 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_OK);
275 return;
276 case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE:
277 OpenPluginPrivateFileSystem(reply_context, fsid, file_system_context_);
278 return;
279 default:
280 NOTREACHED();
281 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
282 return;
286 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystem(
287 ppapi::host::ReplyMessageContext reply_context,
288 const std::string& fsid,
289 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
290 GURL origin =
291 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin();
292 if (!origin.is_valid()) {
293 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
294 return;
297 const std::string& plugin_id = GeneratePluginId(GetPluginMimeType());
298 if (plugin_id.empty()) {
299 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
300 return;
303 file_system_context->OpenPluginPrivateFileSystem(
304 origin,
305 fileapi::kFileSystemTypePluginPrivate,
306 fsid,
307 plugin_id,
308 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
309 base::Bind(
310 &PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete,
311 weak_factory_.GetWeakPtr(),
312 reply_context,
313 fsid));
316 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete(
317 ppapi::host::ReplyMessageContext reply_context,
318 const std::string& fsid,
319 base::File::Error error) {
320 int32 pp_error = ppapi::FileErrorToPepperError(error);
321 if (pp_error == PP_OK)
322 opened_ = true;
323 SendReplyForIsolatedFileSystem(reply_context, fsid, pp_error);
326 int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem(
327 ppapi::host::HostMessageContext* context,
328 const std::string& fsid,
329 PP_IsolatedFileSystemType_Private type) {
330 // Do not allow multiple opens.
331 if (called_open_)
332 return PP_ERROR_INPROGRESS;
333 called_open_ = true;
335 // Do a sanity check.
336 if (!fileapi::ValidateIsolatedFileSystemId(fsid))
337 return PP_ERROR_BADARGUMENT;
339 int render_process_id = 0;
340 int unused;
341 if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
342 pp_instance(), &render_process_id, &unused)) {
343 fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
344 return PP_ERROR_FAILED;
347 root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
348 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
349 fsid,
350 ppapi::IsolatedFileSystemTypeToRootName(type)));
352 BrowserThread::PostTaskAndReplyWithResult(
353 BrowserThread::UI,
354 FROM_HERE,
355 base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
356 base::Bind(&PepperFileSystemBrowserHost::OpenIsolatedFileSystem,
357 weak_factory_.GetWeakPtr(),
358 context->MakeReplyMessageContext(),
359 fsid,
360 type));
361 return PP_OK_COMPLETIONPENDING;
364 int32_t PepperFileSystemBrowserHost::OnHostMsgReserveQuota(
365 ppapi::host::HostMessageContext* context,
366 int64_t amount,
367 const ppapi::FileGrowthMap& file_growths) {
368 DCHECK(ChecksQuota());
369 DCHECK_GT(amount, 0);
371 if (reserving_quota_)
372 return PP_ERROR_INPROGRESS;
373 reserving_quota_ = true;
375 int64_t reservation_amount =
376 std::max<int64_t>(kMinimumQuotaReservationSize, amount);
377 file_system_context_->default_file_task_runner()->PostTask(
378 FROM_HERE,
379 base::Bind(&QuotaReservation::ReserveQuota,
380 quota_reservation_,
381 reservation_amount,
382 file_growths,
383 base::Bind(&PepperFileSystemBrowserHost::GotReservedQuota,
384 weak_factory_.GetWeakPtr(),
385 context->MakeReplyMessageContext())));
387 return PP_OK_COMPLETIONPENDING;
390 void PepperFileSystemBrowserHost::SendReplyForFileSystem(
391 ppapi::host::ReplyMessageContext reply_context,
392 int32_t pp_error) {
393 reply_context.params.set_result(pp_error);
394 host()->SendReply(reply_context, PpapiPluginMsg_FileSystem_OpenReply());
397 void PepperFileSystemBrowserHost::SendReplyForIsolatedFileSystem(
398 ppapi::host::ReplyMessageContext reply_context,
399 const std::string& fsid,
400 int32_t error) {
401 if (error != PP_OK)
402 fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
403 reply_context.params.set_result(error);
404 host()->SendReply(reply_context,
405 PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply());
408 void PepperFileSystemBrowserHost::SetFileSystemContext(
409 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
410 file_system_context_ = file_system_context;
411 if (type_ != PP_FILESYSTEMTYPE_EXTERNAL || root_url_.is_valid()) {
412 file_system_operation_runner_ =
413 file_system_context_->CreateFileSystemOperationRunner();
417 bool PepperFileSystemBrowserHost::ShouldCreateQuotaReservation() const {
418 // Some file system types don't have quota.
419 if (!ppapi::FileSystemTypeHasQuota(type_))
420 return false;
422 // For file system types with quota, some origins have unlimited storage.
423 quota::QuotaManagerProxy* quota_manager_proxy =
424 file_system_context_->quota_manager_proxy();
425 CHECK(quota_manager_proxy);
426 CHECK(quota_manager_proxy->quota_manager());
427 fileapi::FileSystemType file_system_type =
428 ppapi::PepperFileSystemTypeToFileSystemType(type_);
429 return !quota_manager_proxy->quota_manager()->IsStorageUnlimited(
430 root_url_.GetOrigin(),
431 fileapi::FileSystemTypeToQuotaStorageType(file_system_type));
434 void PepperFileSystemBrowserHost::CreateQuotaReservation(
435 const base::Closure& callback) {
436 DCHECK(root_url_.is_valid());
437 base::PostTaskAndReplyWithResult(
438 file_system_context_->default_file_task_runner(),
439 FROM_HERE,
440 base::Bind(&QuotaReservation::Create,
441 file_system_context_,
442 root_url_.GetOrigin(),
443 ppapi::PepperFileSystemTypeToFileSystemType(type_)),
444 base::Bind(&PepperFileSystemBrowserHost::GotQuotaReservation,
445 weak_factory_.GetWeakPtr(),
446 callback));
449 void PepperFileSystemBrowserHost::GotQuotaReservation(
450 const base::Closure& callback,
451 scoped_refptr<QuotaReservation> quota_reservation) {
452 quota_reservation_ = quota_reservation;
453 callback.Run();
456 void PepperFileSystemBrowserHost::GotReservedQuota(
457 ppapi::host::ReplyMessageContext reply_context,
458 int64_t amount,
459 const ppapi::FileSizeMap& file_sizes) {
460 DCHECK(reserving_quota_);
461 reserving_quota_ = false;
462 reserved_quota_ = amount;
464 reply_context.params.set_result(PP_OK);
465 host()->SendReply(
466 reply_context,
467 PpapiPluginMsg_FileSystem_ReserveQuotaReply(amount, file_sizes));
470 std::string PepperFileSystemBrowserHost::GetPluginMimeType() const {
471 base::FilePath plugin_path = browser_ppapi_host_->GetPluginPath();
472 PepperPluginInfo* info =
473 PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(plugin_path);
474 if (!info || info->mime_types.empty())
475 return std::string();
476 // Use the first element in |info->mime_types| even if several elements exist.
477 return info->mime_types[0].mime_type;
480 std::string PepperFileSystemBrowserHost::GeneratePluginId(
481 const std::string& mime_type) const {
482 // TODO(nhiroki): This function is very specialized for specific plugins (MIME
483 // types). If we bring this API to stable, we might have to make it more
484 // general.
486 if (!net::IsMimeType(mime_type))
487 return std::string();
488 std::string output = mime_type;
490 // Replace a slash used for type/subtype separator with an underscore.
491 // NOTE: This assumes there is only one slash in the MIME type.
492 ReplaceFirstSubstringAfterOffset(&output, 0, "/", "_");
494 // Verify |output| contains only alphabets, digits, or "._-".
495 for (std::string::const_iterator it = output.begin(); it != output.end();
496 ++it) {
497 if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) && *it != '.' && *it != '_' &&
498 *it != '-') {
499 LOG(WARNING) << "Failed to generate a plugin id.";
500 return std::string();
503 return output;
506 } // namespace content