|  | // Copyright 2013 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 "ppapi/proxy/file_ref_resource.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include "base/numerics/safe_conversions.h" | 
|  | #include "ppapi/c/pp_directory_entry.h" | 
|  | #include "ppapi/c/pp_instance.h" | 
|  | #include "ppapi/c/pp_resource.h" | 
|  | #include "ppapi/proxy/ppapi_messages.h" | 
|  | #include "ppapi/shared_impl/array_writer.h" | 
|  | #include "ppapi/shared_impl/file_ref_util.h" | 
|  | #include "ppapi/shared_impl/resource.h" | 
|  | #include "ppapi/shared_impl/resource_tracker.h" | 
|  | #include "ppapi/shared_impl/var.h" | 
|  | #include "ppapi/thunk/enter.h" | 
|  | #include "ppapi/thunk/ppb_file_system_api.h" | 
|  |  | 
|  | namespace ppapi { | 
|  | namespace proxy { | 
|  |  | 
|  | FileRefResource::FileRefResource( | 
|  | Connection connection, | 
|  | PP_Instance instance, | 
|  | const FileRefCreateInfo& create_info) | 
|  | : PluginResource(connection, instance), | 
|  | create_info_(create_info), | 
|  | file_system_resource_(create_info.file_system_plugin_resource) { | 
|  | if (uses_internal_paths()) { | 
|  | // If path ends with a slash, then normalize it away unless path is | 
|  | // the root path. | 
|  | int path_size = base::checked_cast<int>(create_info_.internal_path.size()); | 
|  | if (path_size > 1 && create_info_.internal_path.at(path_size - 1) == '/') | 
|  | create_info_.internal_path.erase(path_size - 1, 1); | 
|  |  | 
|  | path_var_ = new StringVar(create_info_.internal_path); | 
|  | create_info_.display_name = GetNameForInternalFilePath( | 
|  | create_info_.internal_path); | 
|  | } else { | 
|  | DCHECK(!create_info_.display_name.empty()); | 
|  | } | 
|  | name_var_ = new StringVar(create_info_.display_name); | 
|  |  | 
|  | if (create_info_.browser_pending_host_resource_id != 0 && | 
|  | create_info_.renderer_pending_host_resource_id != 0) { | 
|  | AttachToPendingHost(BROWSER, create_info_.browser_pending_host_resource_id); | 
|  | AttachToPendingHost(RENDERER, | 
|  | create_info_.renderer_pending_host_resource_id); | 
|  | } else { | 
|  | CHECK_EQ(0, create_info_.browser_pending_host_resource_id); | 
|  | CHECK_EQ(0, create_info_.renderer_pending_host_resource_id); | 
|  | CHECK(uses_internal_paths()); | 
|  | SendCreate(BROWSER, PpapiHostMsg_FileRef_CreateForFileAPI( | 
|  | create_info.file_system_plugin_resource, | 
|  | create_info.internal_path)); | 
|  | SendCreate(RENDERER, PpapiHostMsg_FileRef_CreateForFileAPI( | 
|  | create_info.file_system_plugin_resource, | 
|  | create_info.internal_path)); | 
|  | } | 
|  | } | 
|  |  | 
|  | FileRefResource::~FileRefResource() { | 
|  | } | 
|  |  | 
|  | // static | 
|  | PP_Resource FileRefResource::CreateFileRef( | 
|  | Connection connection, | 
|  | PP_Instance instance, | 
|  | const FileRefCreateInfo& create_info) { | 
|  | // If we have a valid file_system resource, ensure that its type matches that | 
|  | // of the fs_type parameter. | 
|  | if (create_info.file_system_plugin_resource != 0) { | 
|  | thunk::EnterResourceNoLock<thunk::PPB_FileSystem_API> enter( | 
|  | create_info.file_system_plugin_resource, true); | 
|  | if (enter.failed()) | 
|  | return 0; | 
|  | if (enter.object()->GetType() != create_info.file_system_type) { | 
|  | NOTREACHED() << "file system type mismatch with resource"; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (create_info.file_system_type == PP_FILESYSTEMTYPE_LOCALPERSISTENT || | 
|  | create_info.file_system_type == PP_FILESYSTEMTYPE_LOCALTEMPORARY) { | 
|  | if (!IsValidInternalPath(create_info.internal_path)) | 
|  | return 0; | 
|  | } | 
|  | return (new FileRefResource(connection, | 
|  | instance, | 
|  | create_info))->GetReference(); | 
|  | } | 
|  |  | 
|  | thunk::PPB_FileRef_API* FileRefResource::AsPPB_FileRef_API() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | PP_FileSystemType FileRefResource::GetFileSystemType() const { | 
|  | return create_info_.file_system_type; | 
|  | } | 
|  |  | 
|  | PP_Var FileRefResource::GetName() const { | 
|  | return name_var_->GetPPVar(); | 
|  | } | 
|  |  | 
|  | PP_Var FileRefResource::GetPath() const { | 
|  | if (!uses_internal_paths()) | 
|  | return PP_MakeUndefined(); | 
|  | return path_var_->GetPPVar(); | 
|  | } | 
|  |  | 
|  | PP_Resource FileRefResource::GetParent() { | 
|  | if (!uses_internal_paths()) | 
|  | return 0; | 
|  |  | 
|  | size_t pos = create_info_.internal_path.rfind('/'); | 
|  | CHECK(pos != std::string::npos); | 
|  | if (pos == 0) | 
|  | pos++; | 
|  | std::string parent_path = create_info_.internal_path.substr(0, pos); | 
|  |  | 
|  | ppapi::FileRefCreateInfo parent_info; | 
|  | parent_info.file_system_type = create_info_.file_system_type; | 
|  | parent_info.internal_path = parent_path; | 
|  | parent_info.display_name = GetNameForInternalFilePath(parent_path); | 
|  | parent_info.file_system_plugin_resource = | 
|  | create_info_.file_system_plugin_resource; | 
|  |  | 
|  | return (new FileRefResource(connection(), | 
|  | pp_instance(), | 
|  | parent_info))->GetReference(); | 
|  | } | 
|  |  | 
|  | int32_t FileRefResource::MakeDirectory( | 
|  | int32_t make_directory_flags, | 
|  | scoped_refptr<TrackedCallback> callback) { | 
|  | Call<PpapiPluginMsg_FileRef_MakeDirectoryReply>( | 
|  | BROWSER, | 
|  | PpapiHostMsg_FileRef_MakeDirectory(make_directory_flags), | 
|  | base::Bind(&FileRefResource::RunTrackedCallback, this, callback)); | 
|  | return PP_OK_COMPLETIONPENDING; | 
|  | } | 
|  |  | 
|  | int32_t FileRefResource::Touch(PP_Time last_access_time, | 
|  | PP_Time last_modified_time, | 
|  | scoped_refptr<TrackedCallback> callback) { | 
|  | Call<PpapiPluginMsg_FileRef_TouchReply>( | 
|  | BROWSER, | 
|  | PpapiHostMsg_FileRef_Touch(last_access_time, | 
|  | last_modified_time), | 
|  | base::Bind(&FileRefResource::RunTrackedCallback, this, callback)); | 
|  | return PP_OK_COMPLETIONPENDING; | 
|  | } | 
|  |  | 
|  | int32_t FileRefResource::Delete(scoped_refptr<TrackedCallback> callback) { | 
|  | Call<PpapiPluginMsg_FileRef_DeleteReply>( | 
|  | BROWSER, | 
|  | PpapiHostMsg_FileRef_Delete(), | 
|  | base::Bind(&FileRefResource::RunTrackedCallback, this, callback)); | 
|  | return PP_OK_COMPLETIONPENDING; | 
|  | } | 
|  |  | 
|  | int32_t FileRefResource::Rename(PP_Resource new_file_ref, | 
|  | scoped_refptr<TrackedCallback> callback) { | 
|  | Call<PpapiPluginMsg_FileRef_RenameReply>( | 
|  | BROWSER, | 
|  | PpapiHostMsg_FileRef_Rename(new_file_ref), | 
|  | base::Bind(&FileRefResource::RunTrackedCallback, this, callback)); | 
|  | return PP_OK_COMPLETIONPENDING; | 
|  | } | 
|  |  | 
|  | int32_t FileRefResource::Query(PP_FileInfo* info, | 
|  | scoped_refptr<TrackedCallback> callback) { | 
|  | if (info == NULL) | 
|  | return PP_ERROR_BADARGUMENT; | 
|  |  | 
|  | Call<PpapiPluginMsg_FileRef_QueryReply>( | 
|  | BROWSER, | 
|  | PpapiHostMsg_FileRef_Query(), | 
|  | base::Bind(&FileRefResource::OnQueryReply, this, info, callback)); | 
|  | return PP_OK_COMPLETIONPENDING; | 
|  | } | 
|  |  | 
|  | int32_t FileRefResource::ReadDirectoryEntries( | 
|  | const PP_ArrayOutput& output, | 
|  | scoped_refptr<TrackedCallback> callback) { | 
|  | Call<PpapiPluginMsg_FileRef_ReadDirectoryEntriesReply>( | 
|  | BROWSER, | 
|  | PpapiHostMsg_FileRef_ReadDirectoryEntries(), | 
|  | base::Bind(&FileRefResource::OnDirectoryEntriesReply, | 
|  | this, output, callback)); | 
|  | return PP_OK_COMPLETIONPENDING; | 
|  | } | 
|  |  | 
|  | const FileRefCreateInfo& FileRefResource::GetCreateInfo() const { | 
|  | return create_info_; | 
|  | } | 
|  |  | 
|  | PP_Var FileRefResource::GetAbsolutePath() { | 
|  | if (!absolute_path_var_.get()) { | 
|  | std::string absolute_path; | 
|  | int32_t result = SyncCall<PpapiPluginMsg_FileRef_GetAbsolutePathReply>( | 
|  | BROWSER, PpapiHostMsg_FileRef_GetAbsolutePath(), &absolute_path); | 
|  | if (result != PP_OK) | 
|  | return PP_MakeUndefined(); | 
|  | absolute_path_var_ = new StringVar(absolute_path); | 
|  | } | 
|  | return absolute_path_var_->GetPPVar(); | 
|  | } | 
|  |  | 
|  | void FileRefResource::RunTrackedCallback( | 
|  | scoped_refptr<TrackedCallback> callback, | 
|  | const ResourceMessageReplyParams& params) { | 
|  | if (TrackedCallback::IsPending(callback)) | 
|  | callback->Run(params.result()); | 
|  | } | 
|  |  | 
|  | void FileRefResource::OnQueryReply( | 
|  | PP_FileInfo* out_info, | 
|  | scoped_refptr<TrackedCallback> callback, | 
|  | const ResourceMessageReplyParams& params, | 
|  | const PP_FileInfo& info) { | 
|  | if (!TrackedCallback::IsPending(callback)) | 
|  | return; | 
|  |  | 
|  | if (params.result() == PP_OK) | 
|  | *out_info = info; | 
|  | callback->Run(params.result()); | 
|  | } | 
|  |  | 
|  | void FileRefResource::OnDirectoryEntriesReply( | 
|  | const PP_ArrayOutput& output, | 
|  | scoped_refptr<TrackedCallback> callback, | 
|  | const ResourceMessageReplyParams& params, | 
|  | const std::vector<ppapi::FileRefCreateInfo>& infos, | 
|  | const std::vector<PP_FileType>& file_types) { | 
|  | if (!TrackedCallback::IsPending(callback)) | 
|  | return; | 
|  |  | 
|  | if (params.result() == PP_OK) { | 
|  | ArrayWriter writer(output); | 
|  | if (!writer.is_valid()) { | 
|  | callback->Run(PP_ERROR_BADARGUMENT); | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::vector<PP_DirectoryEntry> entries; | 
|  | for (size_t i = 0; i < infos.size(); ++i) { | 
|  | PP_DirectoryEntry entry; | 
|  | entry.file_ref = FileRefResource::CreateFileRef(connection(), | 
|  | pp_instance(), | 
|  | infos[i]); | 
|  | entry.file_type = file_types[i]; | 
|  | entries.push_back(entry); | 
|  | } | 
|  |  | 
|  | writer.StoreVector(entries); | 
|  | } | 
|  | callback->Run(params.result()); | 
|  | } | 
|  |  | 
|  | bool FileRefResource::uses_internal_paths() const { | 
|  | return (create_info_.file_system_type != PP_FILESYSTEMTYPE_EXTERNAL) || | 
|  | !create_info_.internal_path.empty(); | 
|  | } | 
|  |  | 
|  | }  // namespace proxy | 
|  | }  // namespace ppapi |