// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "webkit/plugins/ppapi/ppb_file_io_impl.h" #include "base/bind.h" #include "base/callback.h" #include "base/file_util.h" #include "base/file_util_proxy.h" #include "base/message_loop_proxy.h" #include "base/platform_file.h" #include "base/logging.h" #include "base/time.h" #include "ppapi/c/ppb_file_io.h" #include "ppapi/c/trusted/ppb_file_io_trusted.h" #include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_errors.h" #include "ppapi/shared_impl/file_type_conversion.h" #include "ppapi/shared_impl/time_conversion.h" #include "ppapi/thunk/enter.h" #include "ppapi/thunk/ppb_file_ref_api.h" #include "webkit/plugins/ppapi/common.h" #include "webkit/plugins/ppapi/plugin_module.h" #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" #include "webkit/plugins/ppapi/ppb_file_ref_impl.h" #include "webkit/plugins/ppapi/quota_file_io.h" #include "webkit/plugins/ppapi/resource_helper.h" using ppapi::PPTimeToTime; using ppapi::TimeToPPTime; using ppapi::thunk::PPB_FileRef_API; namespace webkit { namespace ppapi { PPB_FileIO_Impl::PPB_FileIO_Impl(PP_Instance instance) : ::ppapi::PPB_FileIO_Shared(instance), file_(base::kInvalidPlatformFileValue), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { } PPB_FileIO_Impl::~PPB_FileIO_Impl() { Close(); } int32_t PPB_FileIO_Impl::OpenValidated(PP_Resource file_ref_resource, PPB_FileRef_API* file_ref_api, int32_t open_flags, PP_CompletionCallback callback) { PPB_FileRef_Impl* file_ref = static_cast(file_ref_api); int flags = 0; if (!::ppapi::PepperFileOpenFlagsToPlatformFileFlags(open_flags, &flags)) return PP_ERROR_BADARGUMENT; PluginDelegate* plugin_delegate = GetPluginDelegate(); if (!plugin_delegate) return PP_ERROR_BADARGUMENT; if (file_ref->HasValidFileSystem()) { file_system_url_ = file_ref->GetFileSystemURL(); if (!plugin_delegate->AsyncOpenFileSystemURL( file_system_url_, flags, base::Bind(&PPB_FileIO_Impl::ExecutePlatformOpenFileCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; } else { if (file_system_type_ != PP_FILESYSTEMTYPE_EXTERNAL) return PP_ERROR_FAILED; if (!plugin_delegate->AsyncOpenFile( file_ref->GetSystemPath(), flags, base::Bind(&PPB_FileIO_Impl::ExecutePlatformOpenFileCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; } RegisterCallback(OPERATION_EXCLUSIVE, callback, NULL, NULL); return PP_OK_COMPLETIONPENDING; } int32_t PPB_FileIO_Impl::QueryValidated(PP_FileInfo* info, PP_CompletionCallback callback) { PluginDelegate* plugin_delegate = GetPluginDelegate(); if (!plugin_delegate) return PP_ERROR_FAILED; if (!base::FileUtilProxy::GetFileInfoFromPlatformFile( plugin_delegate->GetFileThreadMessageLoopProxy(), file_, base::Bind(&PPB_FileIO_Impl::ExecutePlatformQueryCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; RegisterCallback(OPERATION_EXCLUSIVE, callback, NULL, info); return PP_OK_COMPLETIONPENDING; } int32_t PPB_FileIO_Impl::TouchValidated(PP_Time last_access_time, PP_Time last_modified_time, PP_CompletionCallback callback) { PluginDelegate* plugin_delegate = GetPluginDelegate(); if (!plugin_delegate) return PP_ERROR_FAILED; if (!base::FileUtilProxy::Touch( plugin_delegate->GetFileThreadMessageLoopProxy(), file_, PPTimeToTime(last_access_time), PPTimeToTime(last_modified_time), base::Bind(&PPB_FileIO_Impl::ExecutePlatformGeneralCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; RegisterCallback(OPERATION_EXCLUSIVE, callback, NULL, NULL); return PP_OK_COMPLETIONPENDING; } int32_t PPB_FileIO_Impl::ReadValidated(int64_t offset, char* buffer, int32_t bytes_to_read, PP_CompletionCallback callback) { PluginDelegate* plugin_delegate = GetPluginDelegate(); if (!plugin_delegate) return PP_ERROR_FAILED; if (!base::FileUtilProxy::Read( plugin_delegate->GetFileThreadMessageLoopProxy(), file_, offset, bytes_to_read, base::Bind(&PPB_FileIO_Impl::ExecutePlatformReadCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; RegisterCallback(OPERATION_READ, callback, buffer, NULL); return PP_OK_COMPLETIONPENDING; } int32_t PPB_FileIO_Impl::WriteValidated(int64_t offset, const char* buffer, int32_t bytes_to_write, PP_CompletionCallback callback) { PluginDelegate* plugin_delegate = GetPluginDelegate(); if (!plugin_delegate) return PP_ERROR_FAILED; if (quota_file_io_.get()) { if (!quota_file_io_->Write( offset, buffer, bytes_to_write, base::Bind(&PPB_FileIO_Impl::ExecutePlatformWriteCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; } else { if (!base::FileUtilProxy::Write( plugin_delegate->GetFileThreadMessageLoopProxy(), file_, offset, buffer, bytes_to_write, base::Bind(&PPB_FileIO_Impl::ExecutePlatformWriteCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; } RegisterCallback(OPERATION_WRITE, callback, NULL, NULL); return PP_OK_COMPLETIONPENDING; } int32_t PPB_FileIO_Impl::SetLengthValidated(int64_t length, PP_CompletionCallback callback) { PluginDelegate* plugin_delegate = GetPluginDelegate(); if (!plugin_delegate) return PP_ERROR_FAILED; if (quota_file_io_.get()) { if (!quota_file_io_->SetLength( length, base::Bind(&PPB_FileIO_Impl::ExecutePlatformGeneralCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; } else { if (!base::FileUtilProxy::Truncate( plugin_delegate->GetFileThreadMessageLoopProxy(), file_, length, base::Bind(&PPB_FileIO_Impl::ExecutePlatformGeneralCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; } RegisterCallback(OPERATION_EXCLUSIVE, callback, NULL, NULL); return PP_OK_COMPLETIONPENDING; } int32_t PPB_FileIO_Impl::FlushValidated(PP_CompletionCallback callback) { PluginDelegate* plugin_delegate = GetPluginDelegate(); if (!plugin_delegate) return PP_ERROR_FAILED; if (!base::FileUtilProxy::Flush( plugin_delegate->GetFileThreadMessageLoopProxy(), file_, base::Bind(&PPB_FileIO_Impl::ExecutePlatformGeneralCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; RegisterCallback(OPERATION_EXCLUSIVE, callback, NULL, NULL); return PP_OK_COMPLETIONPENDING; } void PPB_FileIO_Impl::Close() { PluginDelegate* plugin_delegate = GetPluginDelegate(); if (file_ != base::kInvalidPlatformFileValue && plugin_delegate) { base::FileUtilProxy::Close( plugin_delegate->GetFileThreadMessageLoopProxy(), file_, base::FileUtilProxy::StatusCallback()); file_ = base::kInvalidPlatformFileValue; quota_file_io_.reset(); } } int32_t PPB_FileIO_Impl::GetOSFileDescriptor() { #if defined(OS_POSIX) return file_; #elif defined(OS_WIN) return reinterpret_cast(file_); #else #error "Platform not supported." #endif } int32_t PPB_FileIO_Impl::WillWrite(int64_t offset, int32_t bytes_to_write, PP_CompletionCallback callback) { int32_t rv = CommonCallValidation(true, OPERATION_EXCLUSIVE, callback); if (rv != PP_OK) return rv; if (!quota_file_io_.get()) return PP_OK; if (!quota_file_io_->WillWrite( offset, bytes_to_write, base::Bind(&PPB_FileIO_Impl::ExecutePlatformWillWriteCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; RegisterCallback(OPERATION_EXCLUSIVE, callback, NULL, NULL); return PP_OK_COMPLETIONPENDING; } int32_t PPB_FileIO_Impl::WillSetLength(int64_t length, PP_CompletionCallback callback) { int32_t rv = CommonCallValidation(true, OPERATION_EXCLUSIVE, callback); if (rv != PP_OK) return rv; if (!quota_file_io_.get()) return PP_OK; if (!quota_file_io_->WillSetLength( length, base::Bind(&PPB_FileIO_Impl::ExecutePlatformGeneralCallback, weak_factory_.GetWeakPtr()))) return PP_ERROR_FAILED; RegisterCallback(OPERATION_EXCLUSIVE, callback, NULL, NULL); return PP_OK_COMPLETIONPENDING; } PluginDelegate* PPB_FileIO_Impl::GetPluginDelegate() { return ResourceHelper::GetPluginDelegate(this); } void PPB_FileIO_Impl::ExecutePlatformGeneralCallback( base::PlatformFileError error_code) { ExecuteGeneralCallback(::ppapi::PlatformFileErrorToPepperError(error_code)); } void PPB_FileIO_Impl::ExecutePlatformOpenFileCallback( base::PlatformFileError error_code, base::PassPlatformFile file) { DCHECK(file_ == base::kInvalidPlatformFileValue); file_ = file.ReleaseValue(); DCHECK(!quota_file_io_.get()); if (file_ != base::kInvalidPlatformFileValue && (file_system_type_ == PP_FILESYSTEMTYPE_LOCALTEMPORARY || file_system_type_ == PP_FILESYSTEMTYPE_LOCALPERSISTENT)) { quota_file_io_.reset(new QuotaFileIO( pp_instance(), file_, file_system_url_, file_system_type_)); } ExecuteOpenFileCallback(::ppapi::PlatformFileErrorToPepperError(error_code)); } void PPB_FileIO_Impl::ExecutePlatformQueryCallback( base::PlatformFileError error_code, const base::PlatformFileInfo& file_info) { PP_FileInfo pp_info; pp_info.size = file_info.size; pp_info.creation_time = TimeToPPTime(file_info.creation_time); pp_info.last_access_time = TimeToPPTime(file_info.last_accessed); pp_info.last_modified_time = TimeToPPTime(file_info.last_modified); pp_info.system_type = file_system_type_; if (file_info.is_directory) pp_info.type = PP_FILETYPE_DIRECTORY; else pp_info.type = PP_FILETYPE_REGULAR; ExecuteQueryCallback(::ppapi::PlatformFileErrorToPepperError(error_code), pp_info); } void PPB_FileIO_Impl::ExecutePlatformReadCallback( base::PlatformFileError error_code, const char* data, int bytes_read) { // Map the error code, OK getting mapped to the # of bytes read. int32_t pp_result = ::ppapi::PlatformFileErrorToPepperError(error_code); pp_result = pp_result == PP_OK ? bytes_read : pp_result; ExecuteReadCallback(pp_result, data); } void PPB_FileIO_Impl::ExecutePlatformWriteCallback( base::PlatformFileError error_code, int bytes_written) { int32_t pp_result = ::ppapi::PlatformFileErrorToPepperError(error_code); ExecuteGeneralCallback(pp_result == PP_OK ? bytes_written : pp_result); } void PPB_FileIO_Impl::ExecutePlatformWillWriteCallback( base::PlatformFileError error_code, int bytes_written) { if (pending_op_ != OPERATION_EXCLUSIVE || callbacks_.empty()) { NOTREACHED(); return; } if (error_code != base::PLATFORM_FILE_OK) { RunAndRemoveFirstPendingCallback( ::ppapi::PlatformFileErrorToPepperError(error_code)); } else { RunAndRemoveFirstPendingCallback(bytes_written); } } } // namespace ppapi } // namespace webkit