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"
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/common/fileapi/file_system_util.h"
32 // This is the minimum amount of quota we reserve per file system.
33 const int64_t kMinimumQuotaReservationSize
= 1024 * 1024; // 1 MB
35 scoped_refptr
<fileapi::FileSystemContext
>
36 GetFileSystemContextFromRenderId(int render_process_id
) {
37 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
38 RenderProcessHost
* host
= RenderProcessHost::FromID(render_process_id
);
41 StoragePartition
* storage_partition
= host
->GetStoragePartition();
42 if (!storage_partition
)
44 return storage_partition
->GetFileSystemContext();
49 PepperFileSystemBrowserHost::QuotaRequest::QuotaRequest(
51 const RequestQuotaCallback
& callback_arg
)
53 callback(callback_arg
) {
56 PepperFileSystemBrowserHost::QuotaRequest::~QuotaRequest() {
59 PepperFileSystemBrowserHost::PepperFileSystemBrowserHost(BrowserPpapiHost
* host
,
62 PP_FileSystemType type
)
63 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
64 browser_ppapi_host_(host
),
68 file_system_context_(NULL
),
70 reserving_quota_(false),
74 PepperFileSystemBrowserHost::~PepperFileSystemBrowserHost() {
75 // All FileRefs and FileIOs that reference us must have been destroyed. Cancel
76 // all pending file system operations.
77 if (file_system_operation_runner_
)
78 file_system_operation_runner_
->Shutdown();
81 void PepperFileSystemBrowserHost::OpenExisting(const GURL
& root_url
,
82 const base::Closure
& callback
) {
84 int render_process_id
= 0;
86 if (!browser_ppapi_host_
->GetRenderViewIDsForInstance(
87 pp_instance(), &render_process_id
, &unused
)) {
91 // Get the file system context asynchronously, and then complete the Open
92 // operation by calling |callback|.
93 BrowserThread::PostTaskAndReplyWithResult(
96 base::Bind(&GetFileSystemContextFromRenderId
, render_process_id
),
97 base::Bind(&PepperFileSystemBrowserHost::OpenExistingFileSystem
,
98 weak_factory_
.GetWeakPtr(), 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(
106 PpapiHostMsg_FileSystem_Open
,
108 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
109 PpapiHostMsg_FileSystem_InitIsolatedFileSystem
,
110 OnHostMsgInitIsolatedFileSystem
)
111 IPC_END_MESSAGE_MAP()
112 return PP_ERROR_FAILED
;
115 bool PepperFileSystemBrowserHost::IsFileSystemHost() {
119 void PepperFileSystemBrowserHost::OpenQuotaFile(
120 PepperFileIOHost
* file_io_host
,
121 const base::FilePath
& file_path
,
122 const OpenQuotaFileCallback
& callback
) {
123 int32_t id
= file_io_host
->pp_resource();
124 std::pair
<FileMap::iterator
, bool> insert_result
=
125 files_
.insert(std::make_pair(id
, file_io_host
));
126 if (insert_result
.second
) {
127 base::PostTaskAndReplyWithResult(
128 file_system_context_
->default_file_task_runner(),
130 base::Bind(&QuotaReservation::OpenFile
,
140 void PepperFileSystemBrowserHost::CloseQuotaFile(
141 PepperFileIOHost
* file_io_host
) {
142 int32_t id
= file_io_host
->pp_resource();
143 int64_t max_written_offset
= 0;
144 FileMap::iterator it
= files_
.find(id
);
145 if (it
!= files_
.end()) {
146 max_written_offset
= file_io_host
->max_written_offset();
153 file_system_context_
->default_file_task_runner()->PostTask(
155 base::Bind(&QuotaReservation::CloseFile
,
158 max_written_offset
));
161 int32_t PepperFileSystemBrowserHost::RequestQuota(
163 const RequestQuotaCallback
& callback
) {
165 if (!reserving_quota_
&& reserved_quota_
>= amount
) {
166 reserved_quota_
-= amount
;
170 // Queue up a pending quota request.
171 pending_quota_requests_
.push(QuotaRequest(amount
, callback
));
173 // Reserve more quota if we haven't already.
174 if (!reserving_quota_
)
175 ReserveQuota(amount
);
177 return PP_OK_COMPLETIONPENDING
;
180 int32_t PepperFileSystemBrowserHost::OnHostMsgOpen(
181 ppapi::host::HostMessageContext
* context
,
182 int64_t /* unused */) {
183 // TODO(raymes): The file system size is now unused by FileSystemDispatcher.
184 // Figure out why. Why is the file system size signed?
186 // Not allow multiple opens.
188 return PP_ERROR_INPROGRESS
;
191 fileapi::FileSystemType file_system_type
=
192 ppapi::PepperFileSystemTypeToFileSystemType(type_
);
193 if (file_system_type
== fileapi::kFileSystemTypeUnknown
)
194 return PP_ERROR_FAILED
;
196 int render_process_id
= 0;
198 if (!browser_ppapi_host_
->GetRenderViewIDsForInstance(pp_instance(),
201 return PP_ERROR_FAILED
;
204 BrowserThread::PostTaskAndReplyWithResult(
207 base::Bind(&GetFileSystemContextFromRenderId
, render_process_id
),
208 base::Bind(&PepperFileSystemBrowserHost::OpenFileSystem
,
209 weak_factory_
.GetWeakPtr(),
210 context
->MakeReplyMessageContext(),
212 return PP_OK_COMPLETIONPENDING
;
215 void PepperFileSystemBrowserHost::OpenExistingFileSystem(
216 const base::Closure
& callback
,
217 scoped_refptr
<fileapi::FileSystemContext
> file_system_context
) {
218 if (file_system_context
.get()) {
221 // If there is no file system context, we log a warning and continue with an
222 // invalid resource (which will produce errors when used), since we have no
223 // way to communicate the error to the caller.
224 LOG(WARNING
) << "Could not retrieve file system context.";
226 SetFileSystemContext(file_system_context
);
228 int32_t pp_error
= CreateQuotaReservation(callback
);
229 if (pp_error
== PP_OK_COMPLETIONPENDING
)
235 void PepperFileSystemBrowserHost::OpenFileSystem(
236 ppapi::host::ReplyMessageContext reply_context
,
237 fileapi::FileSystemType file_system_type
,
238 scoped_refptr
<fileapi::FileSystemContext
> file_system_context
) {
239 if (!file_system_context
.get()) {
240 OpenFileSystemComplete(
241 reply_context
, GURL(), std::string(), base::PLATFORM_FILE_ERROR_FAILED
);
245 SetFileSystemContext(file_system_context
);
247 GURL origin
= browser_ppapi_host_
->GetDocumentURLForInstance(
248 pp_instance()).GetOrigin();
249 file_system_context_
->OpenFileSystem(origin
, file_system_type
,
250 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
251 base::Bind(&PepperFileSystemBrowserHost::OpenFileSystemComplete
,
252 weak_factory_
.GetWeakPtr(),
256 void PepperFileSystemBrowserHost::OpenFileSystemComplete(
257 ppapi::host::ReplyMessageContext reply_context
,
259 const std::string
& /* unused */,
260 base::PlatformFileError error
) {
261 int32 pp_error
= ppapi::PlatformFileErrorToPepperError(error
);
262 if (pp_error
== PP_OK
) {
266 pp_error
= CreateQuotaReservation(
267 base::Bind(&PepperFileSystemBrowserHost::SendReplyForFileSystem
,
268 weak_factory_
.GetWeakPtr(),
270 static_cast<int32_t>(PP_OK
)));
271 if (pp_error
== PP_OK_COMPLETIONPENDING
)
273 // For PP_OK and all other error codes, we can send the reply now.
275 SendReplyForFileSystem(reply_context
, pp_error
);
278 void PepperFileSystemBrowserHost::OpenIsolatedFileSystem(
279 ppapi::host::ReplyMessageContext reply_context
,
280 const std::string
& fsid
,
281 PP_IsolatedFileSystemType_Private type
,
282 scoped_refptr
<fileapi::FileSystemContext
> file_system_context
) {
283 if (!file_system_context
.get()) {
284 SendReplyForIsolatedFileSystem(reply_context
, fsid
, PP_ERROR_FAILED
);
287 SetFileSystemContext(file_system_context
);
289 root_url_
= GURL(fileapi::GetIsolatedFileSystemRootURIString(
290 browser_ppapi_host_
->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
291 fsid
, ppapi::IsolatedFileSystemTypeToRootName(type
)));
292 if (!root_url_
.is_valid()) {
293 SendReplyForIsolatedFileSystem(reply_context
, fsid
, PP_ERROR_FAILED
);
298 case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_CRX
:
300 SendReplyForIsolatedFileSystem(reply_context
, fsid
, PP_OK
);
302 case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE
:
303 OpenPluginPrivateFileSystem(reply_context
, fsid
, file_system_context_
);
307 SendReplyForIsolatedFileSystem(reply_context
, fsid
, PP_ERROR_BADARGUMENT
);
312 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystem(
313 ppapi::host::ReplyMessageContext reply_context
,
314 const std::string
& fsid
,
315 scoped_refptr
<fileapi::FileSystemContext
> file_system_context
) {
316 GURL origin
= browser_ppapi_host_
->GetDocumentURLForInstance(
317 pp_instance()).GetOrigin();
318 if (!origin
.is_valid()) {
319 SendReplyForIsolatedFileSystem(reply_context
, fsid
, PP_ERROR_FAILED
);
323 const std::string
& plugin_id
= GeneratePluginId(GetPluginMimeType());
324 if (plugin_id
.empty()) {
325 SendReplyForIsolatedFileSystem(reply_context
, fsid
, PP_ERROR_BADARGUMENT
);
329 file_system_context
->OpenPluginPrivateFileSystem(
330 origin
, fileapi::kFileSystemTypePluginPrivate
, fsid
, plugin_id
,
331 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
333 &PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete
,
334 weak_factory_
.GetWeakPtr(), reply_context
, fsid
));
337 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete(
338 ppapi::host::ReplyMessageContext reply_context
,
339 const std::string
& fsid
,
340 base::PlatformFileError error
) {
341 int32 pp_error
= ppapi::PlatformFileErrorToPepperError(error
);
342 if (pp_error
== PP_OK
)
344 SendReplyForIsolatedFileSystem(reply_context
, fsid
, pp_error
);
347 int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem(
348 ppapi::host::HostMessageContext
* context
,
349 const std::string
& fsid
,
350 PP_IsolatedFileSystemType_Private type
) {
351 // Do not allow multiple opens.
353 return PP_ERROR_INPROGRESS
;
356 // Do a sanity check.
357 if (!fileapi::ValidateIsolatedFileSystemId(fsid
))
358 return PP_ERROR_BADARGUMENT
;
360 int render_process_id
= 0;
362 if (!browser_ppapi_host_
->GetRenderViewIDsForInstance(pp_instance(),
365 fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid
);
366 return PP_ERROR_FAILED
;
369 root_url_
= GURL(fileapi::GetIsolatedFileSystemRootURIString(
370 browser_ppapi_host_
->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
371 fsid
, ppapi::IsolatedFileSystemTypeToRootName(type
)));
373 BrowserThread::PostTaskAndReplyWithResult(
376 base::Bind(&GetFileSystemContextFromRenderId
, render_process_id
),
377 base::Bind(&PepperFileSystemBrowserHost::OpenIsolatedFileSystem
,
378 weak_factory_
.GetWeakPtr(),
379 context
->MakeReplyMessageContext(), fsid
, type
));
380 return PP_OK_COMPLETIONPENDING
;
383 void PepperFileSystemBrowserHost::SendReplyForFileSystem(
384 ppapi::host::ReplyMessageContext reply_context
,
386 reply_context
.params
.set_result(pp_error
);
387 host()->SendReply(reply_context
, PpapiPluginMsg_FileSystem_OpenReply());
390 void PepperFileSystemBrowserHost::SendReplyForIsolatedFileSystem(
391 ppapi::host::ReplyMessageContext reply_context
,
392 const std::string
& fsid
,
395 fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid
);
396 reply_context
.params
.set_result(error
);
397 host()->SendReply(reply_context
,
398 PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply());
401 void PepperFileSystemBrowserHost::SetFileSystemContext(
402 scoped_refptr
<fileapi::FileSystemContext
> file_system_context
) {
403 file_system_context_
= file_system_context
;
404 if (type_
!= PP_FILESYSTEMTYPE_EXTERNAL
) {
405 file_system_operation_runner_
=
406 file_system_context_
->CreateFileSystemOperationRunner();
410 int32_t PepperFileSystemBrowserHost::CreateQuotaReservation(
411 const base::Closure
& callback
) {
412 if (!ppapi::FileSystemTypeHasQuota(type_
))
415 DCHECK(root_url_
.is_valid());
416 base::PostTaskAndReplyWithResult(
417 file_system_context_
->default_file_task_runner(),
419 base::Bind(&QuotaReservation::Create
,
420 file_system_context_
,
421 root_url_
.GetOrigin(),
422 ppapi::PepperFileSystemTypeToFileSystemType(type_
)),
423 base::Bind(&PepperFileSystemBrowserHost::GotQuotaReservation
,
424 weak_factory_
.GetWeakPtr(),
426 return PP_OK_COMPLETIONPENDING
;
429 void PepperFileSystemBrowserHost::GotQuotaReservation(
430 const base::Closure
& callback
,
431 scoped_refptr
<QuotaReservation
> quota_reservation
) {
432 quota_reservation_
= quota_reservation
;
436 void PepperFileSystemBrowserHost::ReserveQuota(int32_t amount
) {
437 DCHECK(!reserving_quota_
);
438 reserving_quota_
= true;
440 // Get the max_written_offset for each open file.
441 QuotaReservation::OffsetMap max_written_offsets
;
442 for (FileMap::iterator it
= files_
.begin(); it
!= files_
.end(); ++ it
) {
443 max_written_offsets
.insert(
444 std::make_pair(it
->first
, it
->second
->max_written_offset()));
447 int64_t reservation_amount
= std::max
<int64_t>(kMinimumQuotaReservationSize
,
449 file_system_context_
->default_file_task_runner()->PostTask(
451 base::Bind(&QuotaReservation::ReserveQuota
,
455 base::Bind(&PepperFileSystemBrowserHost::GotReservedQuota
,
456 weak_factory_
.GetWeakPtr())));
459 void PepperFileSystemBrowserHost::GotReservedQuota(
461 const QuotaReservation::OffsetMap
& max_written_offsets
) {
462 DCHECK(reserving_quota_
);
463 reserving_quota_
= false;
464 reserved_quota_
= amount
;
466 // Update open files with their new base sizes. This won't write over any
467 // updates since the files are waiting for quota and can't write.
468 for (FileMap::iterator it
= files_
.begin(); it
!= files_
.end(); ++ it
) {
469 QuotaReservation::OffsetMap::const_iterator offset_it
=
470 max_written_offsets
.find(it
->first
);
471 if (offset_it
!= max_written_offsets
.end())
472 it
->second
->set_max_written_offset(offset_it
->second
);
477 DCHECK(!pending_quota_requests_
.empty());
478 // If we can't grant the first request after refreshing reserved_quota_, then
479 // fail all pending quota requests to avoid an infinite refresh/fail loop.
480 bool fail_all
= reserved_quota_
< pending_quota_requests_
.front().amount
;
481 while (!pending_quota_requests_
.empty()) {
482 QuotaRequest
& request
= pending_quota_requests_
.front();
484 request
.callback
.Run(0);
485 pending_quota_requests_
.pop();
486 } else if (reserved_quota_
>= request
.amount
) {
487 reserved_quota_
-= request
.amount
;
488 request
.callback
.Run(request
.amount
);
489 pending_quota_requests_
.pop();
491 // Refresh the quota reservation for the first pending request that we
493 ReserveQuota(request
.amount
);
499 std::string
PepperFileSystemBrowserHost::GetPluginMimeType() const {
500 base::FilePath plugin_path
= browser_ppapi_host_
->GetPluginPath();
501 PepperPluginInfo
* info
=
502 PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(plugin_path
);
503 if (!info
|| info
->mime_types
.empty())
504 return std::string();
505 // Use the first element in |info->mime_types| even if several elements exist.
506 return info
->mime_types
[0].mime_type
;
509 std::string
PepperFileSystemBrowserHost::GeneratePluginId(
510 const std::string
& mime_type
) const {
511 // TODO(nhiroki): This function is very specialized for specific plugins (MIME
512 // types). If we bring this API to stable, we might have to make it more
515 if (!net::IsMimeType(mime_type
))
516 return std::string();
517 std::string output
= mime_type
;
519 // Replace a slash used for type/subtype separator with an underscore.
520 // NOTE: This assumes there is only one slash in the MIME type.
521 ReplaceFirstSubstringAfterOffset(&output
, 0, "/", "_");
523 // Verify |output| contains only alphabets, digits, or "._-".
524 for (std::string::const_iterator it
= output
.begin();
525 it
!= output
.end(); ++it
) {
526 if (!IsAsciiAlpha(*it
) && !IsAsciiDigit(*it
) &&
527 *it
!= '.' && *it
!= '_' && *it
!= '-') {
528 LOG(WARNING
) << "Failed to generate a plugin id.";
529 return std::string();
535 } // namespace content