1 // Copyright (c) 2012 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/renderer/pepper/pepper_file_io_host.h"
8 #include "base/callback_helpers.h"
9 #include "base/files/file_util_proxy.h"
10 #include "ppapi/c/pp_errors.h"
11 #include "ppapi/host/dispatch_host_message.h"
12 #include "ppapi/host/ppapi_host.h"
13 #include "ppapi/proxy/ppapi_messages.h"
14 #include "ppapi/shared_impl/file_type_conversion.h"
15 #include "ppapi/shared_impl/time_conversion.h"
16 #include "ppapi/thunk/enter.h"
17 #include "webkit/fileapi/file_system_callback_dispatcher.h"
18 #include "webkit/plugins/ppapi/file_callbacks.h"
19 #include "webkit/plugins/ppapi/host_globals.h"
20 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
21 #include "webkit/plugins/ppapi/ppb_file_ref_impl.h"
22 #include "webkit/plugins/ppapi/quota_file_io.h"
26 using ppapi::FileIOStateManager
;
27 using ppapi::PPTimeToTime
;
28 using ppapi::TimeToPPTime
;
29 using ppapi::host::ReplyMessageContext
;
30 using ppapi::thunk::EnterResourceNoLock
;
31 using ppapi::thunk::PPB_FileRef_API
;
32 using webkit::ppapi::PPB_FileRef_Impl
;
33 using webkit::ppapi::PluginDelegate
;
37 // The maximum size we'll support reading in one chunk. The renderer process
38 // must allocate a buffer sized according to the request of the plugin. To
39 // keep things from getting out of control, we cap the read size to this value.
40 // This should generally be OK since the API specifies that it may perform a
42 static const int32_t kMaxReadSize
= 32 * 1024 * 1024; // 32MB
44 typedef base::Callback
<void (base::PlatformFileError
)> PlatformGeneralCallback
;
46 class PlatformGeneralCallbackTranslator
47 : public fileapi::FileSystemCallbackDispatcher
{
49 explicit PlatformGeneralCallbackTranslator(
50 const PlatformGeneralCallback
& callback
)
51 : callback_(callback
) {}
53 virtual ~PlatformGeneralCallbackTranslator() {}
55 virtual void DidSucceed() OVERRIDE
{
56 callback_
.Run(base::PLATFORM_FILE_OK
);
59 virtual void DidReadMetadata(const base::PlatformFileInfo
& file_info
,
60 const base::FilePath
& platform_path
) OVERRIDE
{
64 virtual void DidCreateSnapshotFile(
65 const base::PlatformFileInfo
& file_info
,
66 const base::FilePath
& platform_path
) OVERRIDE
{
70 virtual void DidReadDirectory(
71 const std::vector
<base::FileUtilProxy::Entry
>& entries
,
72 bool has_more
) OVERRIDE
{
76 virtual void DidOpenFileSystem(const std::string
& name
,
77 const GURL
& root
) OVERRIDE
{
81 virtual void DidFail(base::PlatformFileError error_code
) OVERRIDE
{
82 callback_
.Run(error_code
);
85 virtual void DidWrite(int64 bytes
, bool complete
) OVERRIDE
{
89 virtual void DidOpenFile(base::PlatformFile file
) OVERRIDE
{
94 PlatformGeneralCallback callback_
;
97 int32_t ErrorOrByteNumber(int32_t pp_error
, int32_t byte_number
) {
98 // On the plugin side, some callbacks expect a parameter that means different
99 // things depending on whether is negative or not. We translate for those
101 return pp_error
== PP_OK
? byte_number
: pp_error
;
106 PepperFileIOHost::PepperFileIOHost(RendererPpapiHost
* host
,
107 PP_Instance instance
,
108 PP_Resource resource
)
109 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
110 file_(base::kInvalidPlatformFileValue
),
111 file_system_type_(PP_FILESYSTEMTYPE_INVALID
),
112 is_running_in_process_(host
->IsRunningInProcess()),
113 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
114 // TODO(victorhsieh): eliminate plugin_delegate_ as it's no longer needed.
115 webkit::ppapi::PluginInstance
* plugin_instance
=
116 webkit::ppapi::HostGlobals::Get()->GetInstance(instance
);
117 plugin_delegate_
= plugin_instance
? plugin_instance
->delegate() : NULL
;
120 PepperFileIOHost::~PepperFileIOHost() {
121 OnHostMsgClose(NULL
);
124 int32_t PepperFileIOHost::OnResourceMessageReceived(
125 const IPC::Message
& msg
,
126 ppapi::host::HostMessageContext
* context
) {
127 IPC_BEGIN_MESSAGE_MAP(PepperFileIOHost
, msg
)
128 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Open
,
130 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Query
,
132 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Touch
,
134 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Read
,
136 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_Write
,
138 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_SetLength
,
140 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Flush
,
142 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_Close
,
144 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_WillWrite
,
146 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileIO_WillSetLength
,
147 OnHostMsgWillSetLength
)
148 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FileIO_GetOSFileDescriptor
,
149 OnHostMsgGetOSFileDescriptor
)
150 IPC_END_MESSAGE_MAP()
151 return PP_ERROR_FAILED
;
154 int32_t PepperFileIOHost::OnHostMsgOpen(
155 ppapi::host::HostMessageContext
* context
,
156 PP_Resource file_ref_resource
,
157 int32_t open_flags
) {
158 int32_t rv
= state_manager_
.CheckOperationState(
159 FileIOStateManager::OPERATION_EXCLUSIVE
, false);
164 if (!::ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags
, &flags
))
165 return PP_ERROR_BADARGUMENT
;
167 EnterResourceNoLock
<PPB_FileRef_API
> enter(file_ref_resource
, true);
169 return PP_ERROR_BADRESOURCE
;
171 PPB_FileRef_API
* file_ref_api
= enter
.object();
172 PP_FileSystemType type
= file_ref_api
->GetFileSystemType();
173 if (type
!= PP_FILESYSTEMTYPE_LOCALPERSISTENT
&&
174 type
!= PP_FILESYSTEMTYPE_LOCALTEMPORARY
&&
175 type
!= PP_FILESYSTEMTYPE_EXTERNAL
)
176 return PP_ERROR_FAILED
;
177 file_system_type_
= type
;
179 if (!plugin_delegate_
)
180 return PP_ERROR_FAILED
;
182 PPB_FileRef_Impl
* file_ref
= static_cast<PPB_FileRef_Impl
*>(file_ref_api
);
183 if (file_ref
->HasValidFileSystem()) {
184 file_system_url_
= file_ref
->GetFileSystemURL();
185 if (!plugin_delegate_
->AsyncOpenFileSystemURL(
186 file_system_url_
, flags
,
188 &PepperFileIOHost::ExecutePlatformOpenFileSystemURLCallback
,
189 weak_factory_
.GetWeakPtr(),
190 context
->MakeReplyMessageContext())))
191 return PP_ERROR_FAILED
;
193 if (file_system_type_
!= PP_FILESYSTEMTYPE_EXTERNAL
)
194 return PP_ERROR_FAILED
;
195 if (!plugin_delegate_
->AsyncOpenFile(
196 file_ref
->GetSystemPath(), flags
,
197 base::Bind(&PepperFileIOHost::ExecutePlatformOpenFileCallback
,
198 weak_factory_
.GetWeakPtr(),
199 context
->MakeReplyMessageContext())))
200 return PP_ERROR_FAILED
;
203 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
204 return PP_OK_COMPLETIONPENDING
;
207 int32_t PepperFileIOHost::OnHostMsgQuery(
208 ppapi::host::HostMessageContext
* context
) {
209 int32_t rv
= state_manager_
.CheckOperationState(
210 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
214 if (!plugin_delegate_
)
215 return PP_ERROR_FAILED
;
217 if (!base::FileUtilProxy::GetFileInfoFromPlatformFile(
218 plugin_delegate_
->GetFileThreadMessageLoopProxy(), file_
,
219 base::Bind(&PepperFileIOHost::ExecutePlatformQueryCallback
,
220 weak_factory_
.GetWeakPtr(),
221 context
->MakeReplyMessageContext())))
222 return PP_ERROR_FAILED
;
224 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
225 return PP_OK_COMPLETIONPENDING
;
228 int32_t PepperFileIOHost::OnHostMsgTouch(
229 ppapi::host::HostMessageContext
* context
,
230 PP_Time last_access_time
,
231 PP_Time last_modified_time
) {
232 int32_t rv
= state_manager_
.CheckOperationState(
233 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
237 if (!plugin_delegate_
)
238 return PP_ERROR_FAILED
;
240 if (file_system_type_
!= PP_FILESYSTEMTYPE_EXTERNAL
) {
241 if (!plugin_delegate_
->Touch(
243 PPTimeToTime(last_access_time
),
244 PPTimeToTime(last_modified_time
),
245 new PlatformGeneralCallbackTranslator(
246 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
247 weak_factory_
.GetWeakPtr(),
248 context
->MakeReplyMessageContext()))))
249 return PP_ERROR_FAILED
;
250 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
251 return PP_OK_COMPLETIONPENDING
;
254 // TODO(nhiroki): fix a failure of FileIO.Touch for an external filesystem on
255 // Mac and Linux due to sandbox restrictions (http://crbug.com/101128).
256 if (!base::FileUtilProxy::Touch(
257 plugin_delegate_
->GetFileThreadMessageLoopProxy(),
258 file_
, PPTimeToTime(last_access_time
),
259 PPTimeToTime(last_modified_time
),
260 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
261 weak_factory_
.GetWeakPtr(),
262 context
->MakeReplyMessageContext())))
263 return PP_ERROR_FAILED
;
265 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
266 return PP_OK_COMPLETIONPENDING
;
269 int32_t PepperFileIOHost::OnHostMsgRead(
270 ppapi::host::HostMessageContext
* context
,
272 int32_t max_read_length
) {
273 int32_t rv
= state_manager_
.CheckOperationState(
274 FileIOStateManager::OPERATION_READ
, true);
278 // Validate max_read_length before allocating below. This value is coming from
279 // the untrusted plugin.
280 if (max_read_length
< 0) {
281 ReplyMessageContext reply_context
= context
->MakeReplyMessageContext();
282 reply_context
.params
.set_result(PP_ERROR_FAILED
);
283 host()->SendReply(reply_context
,
284 PpapiPluginMsg_FileIO_ReadReply(std::string()));
285 return PP_OK_COMPLETIONPENDING
;
288 if (!plugin_delegate_
)
289 return PP_ERROR_FAILED
;
291 if (!base::FileUtilProxy::Read(
292 plugin_delegate_
->GetFileThreadMessageLoopProxy(), file_
, offset
,
294 base::Bind(&PepperFileIOHost::ExecutePlatformReadCallback
,
295 weak_factory_
.GetWeakPtr(),
296 context
->MakeReplyMessageContext())))
297 return PP_ERROR_FAILED
;
299 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_READ
);
300 return PP_OK_COMPLETIONPENDING
;
303 int32_t PepperFileIOHost::OnHostMsgWrite(
304 ppapi::host::HostMessageContext
* context
,
306 const std::string
& buffer
) {
307 int32_t rv
= state_manager_
.CheckOperationState(
308 FileIOStateManager::OPERATION_WRITE
, true);
312 if (quota_file_io_
.get()) {
313 if (!quota_file_io_
->Write(
314 offset
, buffer
.c_str(), buffer
.size(),
315 base::Bind(&PepperFileIOHost::ExecutePlatformWriteCallback
,
316 weak_factory_
.GetWeakPtr(),
317 context
->MakeReplyMessageContext())))
318 return PP_ERROR_FAILED
;
320 if (!plugin_delegate_
)
321 return PP_ERROR_FAILED
;
323 if (!base::FileUtilProxy::Write(
324 plugin_delegate_
->GetFileThreadMessageLoopProxy(), file_
, offset
,
325 buffer
.c_str(), buffer
.size(),
326 base::Bind(&PepperFileIOHost::ExecutePlatformWriteCallback
,
327 weak_factory_
.GetWeakPtr(),
328 context
->MakeReplyMessageContext())))
329 return PP_ERROR_FAILED
;
332 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_WRITE
);
333 return PP_OK_COMPLETIONPENDING
;
336 int32_t PepperFileIOHost::OnHostMsgSetLength(
337 ppapi::host::HostMessageContext
* context
,
339 int32_t rv
= state_manager_
.CheckOperationState(
340 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
344 if (!plugin_delegate_
)
345 return PP_ERROR_FAILED
;
347 if (file_system_type_
!= PP_FILESYSTEMTYPE_EXTERNAL
) {
348 if (!plugin_delegate_
->SetLength(
349 file_system_url_
, length
,
350 new PlatformGeneralCallbackTranslator(
351 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
352 weak_factory_
.GetWeakPtr(),
353 context
->MakeReplyMessageContext()))))
354 return PP_ERROR_FAILED
;
356 // TODO(nhiroki): fix a failure of FileIO.SetLength for an external
357 // filesystem on Mac due to sandbox restrictions (http://crbug.com/156077).
358 if (!base::FileUtilProxy::Truncate(
359 plugin_delegate_
->GetFileThreadMessageLoopProxy(), file_
, length
,
360 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
361 weak_factory_
.GetWeakPtr(),
362 context
->MakeReplyMessageContext())))
363 return PP_ERROR_FAILED
;
366 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
367 return PP_OK_COMPLETIONPENDING
;
370 int32_t PepperFileIOHost::OnHostMsgFlush(
371 ppapi::host::HostMessageContext
* context
) {
372 int32_t rv
= state_manager_
.CheckOperationState(
373 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
377 if (!plugin_delegate_
)
378 return PP_ERROR_FAILED
;
380 if (!base::FileUtilProxy::Flush(
381 plugin_delegate_
->GetFileThreadMessageLoopProxy(), file_
,
382 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
383 weak_factory_
.GetWeakPtr(),
384 context
->MakeReplyMessageContext())))
385 return PP_ERROR_FAILED
;
387 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
388 return PP_OK_COMPLETIONPENDING
;
391 int32_t PepperFileIOHost::OnHostMsgClose(
392 ppapi::host::HostMessageContext
* context
) {
393 if (file_
!= base::kInvalidPlatformFileValue
&& plugin_delegate_
) {
394 base::FileUtilProxy::Close(
395 plugin_delegate_
->GetFileThreadMessageLoopProxy(),
397 base::ResetAndReturn(¬ify_close_file_callback_
));
398 file_
= base::kInvalidPlatformFileValue
;
399 quota_file_io_
.reset();
404 int32_t PepperFileIOHost::OnHostMsgWillWrite(
405 ppapi::host::HostMessageContext
* context
,
407 int32_t bytes_to_write
) {
408 int32_t rv
= state_manager_
.CheckOperationState(
409 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
413 if (!quota_file_io_
.get())
416 if (!quota_file_io_
->WillWrite(
417 offset
, bytes_to_write
,
418 base::Bind(&PepperFileIOHost::ExecutePlatformWillWriteCallback
,
419 weak_factory_
.GetWeakPtr(),
420 context
->MakeReplyMessageContext())))
421 return PP_ERROR_FAILED
;
423 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
424 return PP_OK_COMPLETIONPENDING
;
427 int32_t PepperFileIOHost::OnHostMsgWillSetLength(
428 ppapi::host::HostMessageContext
* context
,
430 int32_t rv
= state_manager_
.CheckOperationState(
431 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
435 if (!quota_file_io_
.get())
438 if (!quota_file_io_
->WillSetLength(
440 base::Bind(&PepperFileIOHost::ExecutePlatformGeneralCallback
,
441 weak_factory_
.GetWeakPtr(),
442 context
->MakeReplyMessageContext())))
443 return PP_ERROR_FAILED
;
445 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
446 return PP_OK_COMPLETIONPENDING
;
449 int32_t PepperFileIOHost::OnHostMsgGetOSFileDescriptor(
450 ppapi::host::HostMessageContext
* context
) {
451 if (!is_running_in_process_
)
452 return PP_ERROR_FAILED
;
454 #if defined(OS_POSIX)
456 #elif defined(OS_WIN)
457 reinterpret_cast<uintptr_t>(file_
);
459 -1; // Platform not supported.
461 // TODO(victorhsieh): Pass the file handle in the reply params once this works
463 host()->SendReply(context
->MakeReplyMessageContext(),
464 PpapiPluginMsg_FileIO_GetOSFileDescriptorReply(fd
));
465 return PP_OK_COMPLETIONPENDING
;
468 void PepperFileIOHost::ExecutePlatformGeneralCallback(
469 ppapi::host::ReplyMessageContext reply_context
,
470 base::PlatformFileError error_code
) {
471 reply_context
.params
.set_result(
472 ::ppapi::PlatformFileErrorToPepperError(error_code
));
473 host()->SendReply(reply_context
, PpapiPluginMsg_FileIO_GeneralReply());
474 state_manager_
.SetOperationFinished();
477 void PepperFileIOHost::ExecutePlatformOpenFileCallback(
478 ppapi::host::ReplyMessageContext reply_context
,
479 base::PlatformFileError error_code
,
480 base::PassPlatformFile file
) {
481 int32_t pp_error
= ::ppapi::PlatformFileErrorToPepperError(error_code
);
482 if (pp_error
== PP_OK
)
483 state_manager_
.SetOpenSucceed();
485 DCHECK(file_
== base::kInvalidPlatformFileValue
);
486 file_
= file
.ReleaseValue();
488 DCHECK(!quota_file_io_
.get());
489 if (file_
!= base::kInvalidPlatformFileValue
&&
490 (file_system_type_
== PP_FILESYSTEMTYPE_LOCALTEMPORARY
||
491 file_system_type_
== PP_FILESYSTEMTYPE_LOCALPERSISTENT
)) {
492 quota_file_io_
.reset(new webkit::ppapi::QuotaFileIO(
493 pp_instance(), file_
, file_system_url_
, file_system_type_
));
496 reply_context
.params
.set_result(pp_error
);
497 host()->SendReply(reply_context
, PpapiPluginMsg_FileIO_OpenReply());
498 state_manager_
.SetOperationFinished();
501 void PepperFileIOHost::ExecutePlatformOpenFileSystemURLCallback(
502 ppapi::host::ReplyMessageContext reply_context
,
503 base::PlatformFileError error_code
,
504 base::PassPlatformFile file
,
505 const PluginDelegate::NotifyCloseFileCallback
& callback
) {
506 if (error_code
== base::PLATFORM_FILE_OK
)
507 notify_close_file_callback_
= callback
;
508 ExecutePlatformOpenFileCallback(reply_context
, error_code
, file
);
511 void PepperFileIOHost::ExecutePlatformQueryCallback(
512 ppapi::host::ReplyMessageContext reply_context
,
513 base::PlatformFileError error_code
,
514 const base::PlatformFileInfo
& file_info
) {
516 pp_info
.size
= file_info
.size
;
517 pp_info
.creation_time
= TimeToPPTime(file_info
.creation_time
);
518 pp_info
.last_access_time
= TimeToPPTime(file_info
.last_accessed
);
519 pp_info
.last_modified_time
= TimeToPPTime(file_info
.last_modified
);
520 pp_info
.system_type
= file_system_type_
;
521 if (file_info
.is_directory
)
522 pp_info
.type
= PP_FILETYPE_DIRECTORY
;
524 pp_info
.type
= PP_FILETYPE_REGULAR
;
526 int32_t pp_error
= ::ppapi::PlatformFileErrorToPepperError(error_code
);
527 reply_context
.params
.set_result(pp_error
);
528 host()->SendReply(reply_context
,
529 PpapiPluginMsg_FileIO_QueryReply(pp_info
));
530 state_manager_
.SetOperationFinished();
533 void PepperFileIOHost::ExecutePlatformReadCallback(
534 ppapi::host::ReplyMessageContext reply_context
,
535 base::PlatformFileError error_code
,
536 const char* data
, int bytes_read
) {
537 int32_t pp_error
= ::ppapi::PlatformFileErrorToPepperError(error_code
);
539 // Only send the amount of data in the string that was actually read.
541 if (pp_error
== PP_OK
)
542 buffer
.append(data
, bytes_read
);
543 reply_context
.params
.set_result(ErrorOrByteNumber(pp_error
, bytes_read
));
544 host()->SendReply(reply_context
, PpapiPluginMsg_FileIO_ReadReply(buffer
));
545 state_manager_
.SetOperationFinished();
548 void PepperFileIOHost::ExecutePlatformWriteCallback(
549 ppapi::host::ReplyMessageContext reply_context
,
550 base::PlatformFileError error_code
,
552 int32_t pp_error
= ::ppapi::PlatformFileErrorToPepperError(error_code
);
553 reply_context
.params
.set_result(ErrorOrByteNumber(pp_error
, bytes_written
));
554 host()->SendReply(reply_context
, PpapiPluginMsg_FileIO_GeneralReply());
555 state_manager_
.SetOperationFinished();
558 void PepperFileIOHost::ExecutePlatformWillWriteCallback(
559 ppapi::host::ReplyMessageContext reply_context
,
560 base::PlatformFileError error_code
,
562 // On the plugin side, the callback expects a parameter with different meaning
563 // depends on whether is negative or not. It is the result here. We translate
565 int32_t pp_error
= ::ppapi::PlatformFileErrorToPepperError(error_code
);
566 reply_context
.params
.set_result(ErrorOrByteNumber(pp_error
, bytes_written
));
567 host()->SendReply(reply_context
, PpapiPluginMsg_FileIO_GeneralReply());
568 state_manager_
.SetOperationFinished();
571 } // namespace content