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 "ppapi/proxy/file_io_resource.h"
8 #include "base/task_runner_util.h"
9 #include "ipc/ipc_message.h"
10 #include "ppapi/c/pp_errors.h"
11 #include "ppapi/proxy/ppapi_messages.h"
12 #include "ppapi/shared_impl/array_writer.h"
13 #include "ppapi/shared_impl/file_ref_create_info.h"
14 #include "ppapi/shared_impl/file_system_util.h"
15 #include "ppapi/shared_impl/file_type_conversion.h"
16 #include "ppapi/shared_impl/ppapi_globals.h"
17 #include "ppapi/shared_impl/proxy_lock.h"
18 #include "ppapi/shared_impl/resource_tracker.h"
19 #include "ppapi/thunk/enter.h"
20 #include "ppapi/thunk/ppb_file_ref_api.h"
21 #include "ppapi/thunk/ppb_file_system_api.h"
23 using ppapi::thunk::EnterResourceNoLock
;
24 using ppapi::thunk::PPB_FileIO_API
;
25 using ppapi::thunk::PPB_FileRef_API
;
26 using ppapi::thunk::PPB_FileSystem_API
;
30 // We must allocate a buffer sized according to the request of the plugin. To
31 // reduce the chance of out-of-memory errors, we cap the read and write size to
32 // 32MB. This is OK since the API specifies that it may perform a partial read
34 static const int32_t kMaxReadWriteSize
= 32 * 1024 * 1024; // 32MB
36 // An adapter to let Read() share the same implementation with ReadToArray().
37 void* DummyGetDataBuffer(void* user_data
, uint32_t count
, uint32_t size
) {
41 // File thread task to close the file handle.
42 void DoClose(base::PlatformFile file
) {
43 base::ClosePlatformFile(file
);
51 FileIOResource::QueryOp::QueryOp(scoped_refptr
<FileHandleHolder
> file_handle
)
52 : file_handle_(file_handle
) {
56 FileIOResource::QueryOp::~QueryOp() {
59 int32_t FileIOResource::QueryOp::DoWork() {
60 // TODO(rvargas): Convert this code to use base::File.
61 base::File
file(file_handle_
->raw_handle());
62 bool success
= file
.GetInfo(&file_info_
);
63 file
.TakePlatformFile();
64 return success
? PP_OK
: PP_ERROR_FAILED
;
67 FileIOResource::ReadOp::ReadOp(scoped_refptr
<FileHandleHolder
> file_handle
,
69 int32_t bytes_to_read
)
70 : file_handle_(file_handle
),
72 bytes_to_read_(bytes_to_read
) {
76 FileIOResource::ReadOp::~ReadOp() {
79 int32_t FileIOResource::ReadOp::DoWork() {
80 DCHECK(!buffer_
.get());
81 buffer_
.reset(new char[bytes_to_read_
]);
82 return base::ReadPlatformFile(
83 file_handle_
->raw_handle(), offset_
, buffer_
.get(), bytes_to_read_
);
86 FileIOResource::WriteOp::WriteOp(scoped_refptr
<FileHandleHolder
> file_handle
,
89 int32_t bytes_to_write
,
91 : file_handle_(file_handle
),
94 bytes_to_write_(bytes_to_write
),
98 FileIOResource::WriteOp::~WriteOp() {
101 int32_t FileIOResource::WriteOp::DoWork() {
102 // We can't just call WritePlatformFile in append mode, since NaCl doesn't
103 // implement fcntl, causing the function to call pwrite, which is incorrect.
105 return base::WritePlatformFileAtCurrentPos(
106 file_handle_
->raw_handle(), buffer_
, bytes_to_write_
);
108 return base::WritePlatformFile(
109 file_handle_
->raw_handle(), offset_
, buffer_
, bytes_to_write_
);
113 FileIOResource::FileIOResource(Connection connection
, PP_Instance instance
)
114 : PluginResource(connection
, instance
),
115 file_system_type_(PP_FILESYSTEMTYPE_INVALID
),
117 max_written_offset_(0),
118 append_mode_write_amount_(0),
120 called_close_(false) {
121 SendCreate(BROWSER
, PpapiHostMsg_FileIO_Create());
124 FileIOResource::~FileIOResource() {
128 PPB_FileIO_API
* FileIOResource::AsPPB_FileIO_API() {
132 int32_t FileIOResource::Open(PP_Resource file_ref
,
134 scoped_refptr
<TrackedCallback
> callback
) {
135 EnterResourceNoLock
<PPB_FileRef_API
> enter_file_ref(file_ref
, true);
136 if (enter_file_ref
.failed())
137 return PP_ERROR_BADRESOURCE
;
139 PPB_FileRef_API
* file_ref_api
= enter_file_ref
.object();
140 const FileRefCreateInfo
& create_info
= file_ref_api
->GetCreateInfo();
141 if (!FileSystemTypeIsValid(create_info
.file_system_type
)) {
143 return PP_ERROR_FAILED
;
145 int32_t rv
= state_manager_
.CheckOperationState(
146 FileIOStateManager::OPERATION_EXCLUSIVE
, false);
150 open_flags_
= open_flags
;
151 file_system_type_
= create_info
.file_system_type
;
153 if (create_info
.file_system_plugin_resource
) {
154 EnterResourceNoLock
<PPB_FileSystem_API
> enter_file_system(
155 create_info
.file_system_plugin_resource
, true);
156 if (enter_file_system
.failed())
157 return PP_ERROR_FAILED
;
158 // Take a reference on the FileSystem resource. The FileIO host uses the
159 // FileSystem host for running tasks and checking quota.
160 file_system_resource_
= enter_file_system
.resource();
163 // Take a reference on the FileRef resource while we're opening the file; we
164 // don't want the plugin destroying it during the Open operation.
165 file_ref_
= enter_file_ref
.resource();
167 Call
<PpapiPluginMsg_FileIO_OpenReply
>(BROWSER
,
168 PpapiHostMsg_FileIO_Open(
171 base::Bind(&FileIOResource::OnPluginMsgOpenFileComplete
, this,
174 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
175 return PP_OK_COMPLETIONPENDING
;
178 int32_t FileIOResource::Query(PP_FileInfo
* info
,
179 scoped_refptr
<TrackedCallback
> callback
) {
180 int32_t rv
= state_manager_
.CheckOperationState(
181 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
185 return PP_ERROR_BADARGUMENT
;
186 if (!FileHandleHolder::IsValid(file_handle_
))
187 return PP_ERROR_FAILED
;
189 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
191 // If the callback is blocking, perform the task on the calling thread.
192 if (callback
->is_blocking()) {
193 int32_t result
= PP_ERROR_FAILED
;
194 base::File::Info file_info
;
195 // The plugin could release its reference to this instance when we release
196 // the proxy lock below.
197 scoped_refptr
<FileIOResource
> protect(this);
199 // Release the proxy lock while making a potentially slow file call.
200 ProxyAutoUnlock unlock
;
201 // TODO(rvargas): Convert this code to base::File.
202 base::File
file(file_handle_
->raw_handle());
203 bool success
= file
.GetInfo(&file_info
);
204 file
.TakePlatformFile();
208 if (result
== PP_OK
) {
209 // This writes the file info into the plugin's PP_FileInfo struct.
210 ppapi::FileInfoToPepperFileInfo(file_info
,
214 state_manager_
.SetOperationFinished();
218 // For the non-blocking case, post a task to the file thread and add a
219 // completion task to write the result.
220 scoped_refptr
<QueryOp
> query_op(new QueryOp(file_handle_
));
221 base::PostTaskAndReplyWithResult(
222 PpapiGlobals::Get()->GetFileTaskRunner(),
224 Bind(&FileIOResource::QueryOp::DoWork
, query_op
),
225 RunWhileLocked(Bind(&TrackedCallback::Run
, callback
)));
226 callback
->set_completion_task(
227 Bind(&FileIOResource::OnQueryComplete
, this, query_op
, info
));
229 return PP_OK_COMPLETIONPENDING
;
232 int32_t FileIOResource::Touch(PP_Time last_access_time
,
233 PP_Time last_modified_time
,
234 scoped_refptr
<TrackedCallback
> callback
) {
235 int32_t rv
= state_manager_
.CheckOperationState(
236 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
240 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(BROWSER
,
241 PpapiHostMsg_FileIO_Touch(last_access_time
, last_modified_time
),
242 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
245 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
246 return PP_OK_COMPLETIONPENDING
;
249 int32_t FileIOResource::Read(int64_t offset
,
251 int32_t bytes_to_read
,
252 scoped_refptr
<TrackedCallback
> callback
) {
253 int32_t rv
= state_manager_
.CheckOperationState(
254 FileIOStateManager::OPERATION_READ
, true);
258 PP_ArrayOutput output_adapter
;
259 output_adapter
.GetDataBuffer
= &DummyGetDataBuffer
;
260 output_adapter
.user_data
= buffer
;
261 return ReadValidated(offset
, bytes_to_read
, output_adapter
, callback
);
264 int32_t FileIOResource::ReadToArray(int64_t offset
,
265 int32_t max_read_length
,
266 PP_ArrayOutput
* array_output
,
267 scoped_refptr
<TrackedCallback
> callback
) {
268 DCHECK(array_output
);
269 int32_t rv
= state_manager_
.CheckOperationState(
270 FileIOStateManager::OPERATION_READ
, true);
274 return ReadValidated(offset
, max_read_length
, *array_output
, callback
);
277 int32_t FileIOResource::Write(int64_t offset
,
279 int32_t bytes_to_write
,
280 scoped_refptr
<TrackedCallback
> callback
) {
282 return PP_ERROR_FAILED
;
283 if (offset
< 0 || bytes_to_write
< 0)
284 return PP_ERROR_FAILED
;
285 if (!FileHandleHolder::IsValid(file_handle_
))
286 return PP_ERROR_FAILED
;
288 int32_t rv
= state_manager_
.CheckOperationState(
289 FileIOStateManager::OPERATION_WRITE
, true);
293 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_WRITE
);
296 int64_t increase
= 0;
297 uint64_t max_offset
= 0;
298 bool append
= (open_flags_
& PP_FILEOPENFLAG_APPEND
) != 0;
300 increase
= bytes_to_write
;
302 uint64_t max_offset
= offset
+ bytes_to_write
;
303 if (max_offset
> static_cast<uint64_t>(kint64max
))
304 return PP_ERROR_FAILED
; // amount calculation would overflow.
305 increase
= static_cast<int64_t>(max_offset
) - max_written_offset_
;
310 file_system_resource_
->AsPPB_FileSystem_API()->RequestQuota(
312 base::Bind(&FileIOResource::OnRequestWriteQuotaComplete
,
314 offset
, buffer
, bytes_to_write
, callback
));
315 if (result
== PP_OK_COMPLETIONPENDING
)
316 return PP_OK_COMPLETIONPENDING
;
317 DCHECK(result
== increase
);
320 append_mode_write_amount_
+= bytes_to_write
;
322 max_written_offset_
= max_offset
;
325 return WriteValidated(offset
, buffer
, bytes_to_write
, callback
);
328 int32_t FileIOResource::SetLength(int64_t length
,
329 scoped_refptr
<TrackedCallback
> callback
) {
330 int32_t rv
= state_manager_
.CheckOperationState(
331 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
335 return PP_ERROR_FAILED
;
338 int64_t increase
= length
- max_written_offset_
;
341 file_system_resource_
->AsPPB_FileSystem_API()->RequestQuota(
343 base::Bind(&FileIOResource::OnRequestSetLengthQuotaComplete
,
346 if (result
== PP_OK_COMPLETIONPENDING
) {
347 state_manager_
.SetPendingOperation(
348 FileIOStateManager::OPERATION_EXCLUSIVE
);
349 return PP_OK_COMPLETIONPENDING
;
351 DCHECK(result
== increase
);
352 max_written_offset_
= length
;
356 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
357 SetLengthValidated(length
, callback
);
358 return PP_OK_COMPLETIONPENDING
;
361 int32_t FileIOResource::Flush(scoped_refptr
<TrackedCallback
> callback
) {
362 int32_t rv
= state_manager_
.CheckOperationState(
363 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
367 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(BROWSER
,
368 PpapiHostMsg_FileIO_Flush(),
369 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
372 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
373 return PP_OK_COMPLETIONPENDING
;
376 int64_t FileIOResource::GetMaxWrittenOffset() const {
377 return max_written_offset_
;
380 int64_t FileIOResource::GetAppendModeWriteAmount() const {
381 return append_mode_write_amount_
;
384 void FileIOResource::SetMaxWrittenOffset(int64_t max_written_offset
) {
385 max_written_offset_
= max_written_offset
;
388 void FileIOResource::SetAppendModeWriteAmount(
389 int64_t append_mode_write_amount
) {
390 append_mode_write_amount_
= append_mode_write_amount
;
393 void FileIOResource::Close() {
397 called_close_
= true;
399 check_quota_
= false;
400 file_system_resource_
->AsPPB_FileSystem_API()->CloseQuotaFile(
407 Post(BROWSER
, PpapiHostMsg_FileIO_Close(
408 FileGrowth(max_written_offset_
, append_mode_write_amount_
)));
411 int32_t FileIOResource::RequestOSFileHandle(
412 PP_FileHandle
* handle
,
413 scoped_refptr
<TrackedCallback
> callback
) {
414 int32_t rv
= state_manager_
.CheckOperationState(
415 FileIOStateManager::OPERATION_EXCLUSIVE
, true);
419 Call
<PpapiPluginMsg_FileIO_RequestOSFileHandleReply
>(BROWSER
,
420 PpapiHostMsg_FileIO_RequestOSFileHandle(),
421 base::Bind(&FileIOResource::OnPluginMsgRequestOSFileHandleComplete
, this,
424 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_EXCLUSIVE
);
425 return PP_OK_COMPLETIONPENDING
;
428 FileIOResource::FileHandleHolder::FileHandleHolder(PP_FileHandle file_handle
)
429 : raw_handle_(file_handle
) {
433 bool FileIOResource::FileHandleHolder::IsValid(
434 const scoped_refptr
<FileIOResource::FileHandleHolder
>& handle
) {
435 return handle
&& (handle
->raw_handle() != base::kInvalidPlatformFileValue
);
438 FileIOResource::FileHandleHolder::~FileHandleHolder() {
439 if (raw_handle_
!= base::kInvalidPlatformFileValue
) {
440 base::TaskRunner
* file_task_runner
=
441 PpapiGlobals::Get()->GetFileTaskRunner();
442 file_task_runner
->PostTask(FROM_HERE
,
443 base::Bind(&DoClose
, raw_handle_
));
447 int32_t FileIOResource::ReadValidated(int64_t offset
,
448 int32_t bytes_to_read
,
449 const PP_ArrayOutput
& array_output
,
450 scoped_refptr
<TrackedCallback
> callback
) {
451 if (bytes_to_read
< 0)
452 return PP_ERROR_FAILED
;
453 if (!FileHandleHolder::IsValid(file_handle_
))
454 return PP_ERROR_FAILED
;
456 state_manager_
.SetPendingOperation(FileIOStateManager::OPERATION_READ
);
458 bytes_to_read
= std::min(bytes_to_read
, kMaxReadWriteSize
);
459 if (callback
->is_blocking()) {
460 char* buffer
= static_cast<char*>(
461 array_output
.GetDataBuffer(array_output
.user_data
, bytes_to_read
, 1));
462 int32_t result
= PP_ERROR_FAILED
;
463 // The plugin could release its reference to this instance when we release
464 // the proxy lock below.
465 scoped_refptr
<FileIOResource
> protect(this);
467 // Release the proxy lock while making a potentially slow file call.
468 ProxyAutoUnlock unlock
;
469 result
= base::ReadPlatformFile(
470 file_handle_
->raw_handle(), offset
, buffer
, bytes_to_read
);
472 result
= PP_ERROR_FAILED
;
474 state_manager_
.SetOperationFinished();
478 // For the non-blocking case, post a task to the file thread.
479 scoped_refptr
<ReadOp
> read_op(
480 new ReadOp(file_handle_
, offset
, bytes_to_read
));
481 base::PostTaskAndReplyWithResult(
482 PpapiGlobals::Get()->GetFileTaskRunner(),
484 Bind(&FileIOResource::ReadOp::DoWork
, read_op
),
485 RunWhileLocked(Bind(&TrackedCallback::Run
, callback
)));
486 callback
->set_completion_task(
487 Bind(&FileIOResource::OnReadComplete
, this, read_op
, array_output
));
489 return PP_OK_COMPLETIONPENDING
;
492 int32_t FileIOResource::WriteValidated(
495 int32_t bytes_to_write
,
496 scoped_refptr
<TrackedCallback
> callback
) {
497 bool append
= (open_flags_
& PP_FILEOPENFLAG_APPEND
) != 0;
498 if (callback
->is_blocking()) {
501 // Release the proxy lock while making a potentially slow file call.
502 ProxyAutoUnlock unlock
;
504 result
= base::WritePlatformFileAtCurrentPos(
505 file_handle_
->raw_handle(), buffer
, bytes_to_write
);
507 result
= base::WritePlatformFile(
508 file_handle_
->raw_handle(), offset
, buffer
, bytes_to_write
);
512 result
= PP_ERROR_FAILED
;
514 state_manager_
.SetOperationFinished();
518 // For the non-blocking case, post a task to the file thread.
519 scoped_refptr
<WriteOp
> write_op(
520 new WriteOp(file_handle_
, offset
, buffer
, bytes_to_write
, append
));
521 base::PostTaskAndReplyWithResult(
522 PpapiGlobals::Get()->GetFileTaskRunner(),
524 Bind(&FileIOResource::WriteOp::DoWork
, write_op
),
525 RunWhileLocked(Bind(&TrackedCallback::Run
, callback
)));
526 callback
->set_completion_task(
527 Bind(&FileIOResource::OnWriteComplete
, this, write_op
));
529 return PP_OK_COMPLETIONPENDING
;
532 void FileIOResource::SetLengthValidated(
534 scoped_refptr
<TrackedCallback
> callback
) {
535 Call
<PpapiPluginMsg_FileIO_GeneralReply
>(BROWSER
,
536 PpapiHostMsg_FileIO_SetLength(length
),
537 base::Bind(&FileIOResource::OnPluginMsgGeneralComplete
, this,
540 // On the browser side we grow |max_written_offset_| monotonically, due to the
541 // unpredictable ordering of plugin side Write and SetLength calls. Match that
543 if (max_written_offset_
< length
)
544 max_written_offset_
= length
;
547 int32_t FileIOResource::OnQueryComplete(scoped_refptr
<QueryOp
> query_op
,
550 DCHECK(state_manager_
.get_pending_operation() ==
551 FileIOStateManager::OPERATION_EXCLUSIVE
);
553 if (result
== PP_OK
) {
554 // This writes the file info into the plugin's PP_FileInfo struct.
555 ppapi::FileInfoToPepperFileInfo(query_op
->file_info(),
559 state_manager_
.SetOperationFinished();
563 int32_t FileIOResource::OnReadComplete(scoped_refptr
<ReadOp
> read_op
,
564 PP_ArrayOutput array_output
,
566 DCHECK(state_manager_
.get_pending_operation() ==
567 FileIOStateManager::OPERATION_READ
);
570 output
.set_pp_array_output(array_output
);
571 if (output
.is_valid())
572 output
.StoreArray(read_op
->buffer(), result
);
574 result
= PP_ERROR_FAILED
;
576 // The read operation failed.
577 result
= PP_ERROR_FAILED
;
579 state_manager_
.SetOperationFinished();
583 void FileIOResource::OnRequestWriteQuotaComplete(
586 int32_t bytes_to_write
,
587 scoped_refptr
<TrackedCallback
> callback
,
589 DCHECK(granted
>= 0);
591 callback
->Run(PP_ERROR_NOQUOTA
);
594 if (open_flags_
& PP_FILEOPENFLAG_APPEND
) {
595 DCHECK_LE(bytes_to_write
, granted
);
596 append_mode_write_amount_
+= bytes_to_write
;
598 DCHECK_LE(offset
+ bytes_to_write
- max_written_offset_
, granted
);
600 int64_t max_offset
= offset
+ bytes_to_write
;
601 if (max_written_offset_
< max_offset
)
602 max_written_offset_
= max_offset
;
605 int32_t result
= WriteValidated(offset
, buffer
, bytes_to_write
, callback
);
606 if (result
!= PP_OK_COMPLETIONPENDING
)
607 callback
->Run(result
);
610 void FileIOResource::OnRequestSetLengthQuotaComplete(
612 scoped_refptr
<TrackedCallback
> callback
,
614 DCHECK(granted
>= 0);
616 callback
->Run(PP_ERROR_NOQUOTA
);
620 DCHECK_LE(length
- max_written_offset_
, granted
);
621 if (max_written_offset_
< length
)
622 max_written_offset_
= length
;
623 SetLengthValidated(length
, callback
);
626 int32_t FileIOResource::OnWriteComplete(scoped_refptr
<WriteOp
> write_op
,
628 DCHECK(state_manager_
.get_pending_operation() ==
629 FileIOStateManager::OPERATION_WRITE
);
630 // |result| is the return value of WritePlatformFile; -1 indicates failure.
632 result
= PP_ERROR_FAILED
;
634 state_manager_
.SetOperationFinished();
638 void FileIOResource::OnPluginMsgGeneralComplete(
639 scoped_refptr
<TrackedCallback
> callback
,
640 const ResourceMessageReplyParams
& params
) {
641 DCHECK(state_manager_
.get_pending_operation() ==
642 FileIOStateManager::OPERATION_EXCLUSIVE
||
643 state_manager_
.get_pending_operation() ==
644 FileIOStateManager::OPERATION_WRITE
);
645 // End this operation now, so the user's callback can execute another FileIO
646 // operation, assuming there are no other pending operations.
647 state_manager_
.SetOperationFinished();
648 callback
->Run(params
.result());
651 void FileIOResource::OnPluginMsgOpenFileComplete(
652 scoped_refptr
<TrackedCallback
> callback
,
653 const ResourceMessageReplyParams
& params
,
654 PP_Resource quota_file_system
,
655 int64_t max_written_offset
) {
656 DCHECK(state_manager_
.get_pending_operation() ==
657 FileIOStateManager::OPERATION_EXCLUSIVE
);
659 // Release the FileRef resource.
661 int32_t result
= params
.result();
662 if (result
== PP_OK
) {
663 state_manager_
.SetOpenSucceed();
665 if (quota_file_system
) {
666 DCHECK(quota_file_system
== file_system_resource_
->pp_resource());
668 max_written_offset_
= max_written_offset
;
669 file_system_resource_
->AsPPB_FileSystem_API()->OpenQuotaFile(
673 IPC::PlatformFileForTransit transit_file
;
674 if (params
.TakeFileHandleAtIndex(0, &transit_file
)) {
675 file_handle_
= new FileHandleHolder(
676 IPC::PlatformFileForTransitToPlatformFile(transit_file
));
679 // End this operation now, so the user's callback can execute another FileIO
680 // operation, assuming there are no other pending operations.
681 state_manager_
.SetOperationFinished();
682 callback
->Run(result
);
685 void FileIOResource::OnPluginMsgRequestOSFileHandleComplete(
686 scoped_refptr
<TrackedCallback
> callback
,
687 PP_FileHandle
* output_handle
,
688 const ResourceMessageReplyParams
& params
) {
689 DCHECK(state_manager_
.get_pending_operation() ==
690 FileIOStateManager::OPERATION_EXCLUSIVE
);
692 if (!TrackedCallback::IsPending(callback
)) {
693 state_manager_
.SetOperationFinished();
697 int32_t result
= params
.result();
698 IPC::PlatformFileForTransit transit_file
;
699 if (!params
.TakeFileHandleAtIndex(0, &transit_file
))
700 result
= PP_ERROR_FAILED
;
701 *output_handle
= IPC::PlatformFileForTransitToPlatformFile(transit_file
);
703 // End this operation now, so the user's callback can execute another FileIO
704 // operation, assuming there are no other pending operations.
705 state_manager_
.SetOperationFinished();
706 callback
->Run(result
);