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.
4
5#include "ppapi/proxy/file_ref_resource.h"
6
7#include "ppapi/c/pp_directory_entry.h"
8#include "ppapi/c/pp_instance.h"
9#include "ppapi/c/pp_resource.h"
10#include "ppapi/proxy/ppapi_messages.h"
11#include "ppapi/shared_impl/array_writer.h"
12#include "ppapi/shared_impl/file_ref_util.h"
13#include "ppapi/shared_impl/resource.h"
14#include "ppapi/shared_impl/resource_tracker.h"
15#include "ppapi/shared_impl/var.h"
16#include "ppapi/thunk/enter.h"
17#include "ppapi/thunk/ppb_file_system_api.h"
18
19namespace ppapi {
20namespace proxy {
21
22FileRefResource::FileRefResource(
23    Connection connection,
24    PP_Instance instance,
25    const FileRefCreateInfo& create_info)
26    : PluginResource(connection, instance),
27      create_info_(create_info),
28      file_system_resource_(create_info.file_system_plugin_resource) {
29  if (uses_internal_paths()) {
30    // If path ends with a slash, then normalize it away unless path is
31    // the root path.
32    int path_size = create_info_.internal_path.size();
33    if (path_size > 1 && create_info_.internal_path.at(path_size - 1) == '/')
34      create_info_.internal_path.erase(path_size - 1, 1);
35
36    path_var_ = new StringVar(create_info_.internal_path);
37    create_info_.display_name = GetNameForInternalFilePath(
38        create_info_.internal_path);
39  } else {
40    DCHECK(!create_info_.display_name.empty());
41  }
42  name_var_ = new StringVar(create_info_.display_name);
43
44  if (create_info_.browser_pending_host_resource_id != 0 &&
45      create_info_.renderer_pending_host_resource_id != 0) {
46    AttachToPendingHost(BROWSER, create_info_.browser_pending_host_resource_id);
47    AttachToPendingHost(RENDERER,
48                        create_info_.renderer_pending_host_resource_id);
49  } else {
50    CHECK_EQ(0, create_info_.browser_pending_host_resource_id);
51    CHECK_EQ(0, create_info_.renderer_pending_host_resource_id);
52    CHECK(uses_internal_paths());
53    SendCreate(BROWSER, PpapiHostMsg_FileRef_CreateForFileAPI(
54        create_info.file_system_plugin_resource,
55        create_info.internal_path));
56    SendCreate(RENDERER, PpapiHostMsg_FileRef_CreateForFileAPI(
57        create_info.file_system_plugin_resource,
58        create_info.internal_path));
59  }
60}
61
62FileRefResource::~FileRefResource() {
63}
64
65// static
66PP_Resource FileRefResource::CreateFileRef(
67    Connection connection,
68    PP_Instance instance,
69    const FileRefCreateInfo& create_info) {
70  // If we have a valid file_system resource, ensure that its type matches that
71  // of the fs_type parameter.
72  if (create_info.file_system_plugin_resource != 0) {
73    thunk::EnterResourceNoLock<thunk::PPB_FileSystem_API> enter(
74        create_info.file_system_plugin_resource, true);
75    if (enter.failed())
76      return 0;
77    if (enter.object()->GetType() != create_info.file_system_type) {
78      NOTREACHED() << "file system type mismatch with resource";
79      return 0;
80    }
81  }
82
83  if (create_info.file_system_type == PP_FILESYSTEMTYPE_LOCALPERSISTENT ||
84      create_info.file_system_type == PP_FILESYSTEMTYPE_LOCALTEMPORARY) {
85    if (!IsValidInternalPath(create_info.internal_path))
86      return 0;
87  }
88  return (new FileRefResource(connection,
89                              instance,
90                              create_info))->GetReference();
91}
92
93thunk::PPB_FileRef_API* FileRefResource::AsPPB_FileRef_API() {
94  return this;
95}
96
97PP_FileSystemType FileRefResource::GetFileSystemType() const {
98  return create_info_.file_system_type;
99}
100
101PP_Var FileRefResource::GetName() const {
102  return name_var_->GetPPVar();
103}
104
105PP_Var FileRefResource::GetPath() const {
106  if (!uses_internal_paths())
107    return PP_MakeUndefined();
108  return path_var_->GetPPVar();
109}
110
111PP_Resource FileRefResource::GetParent() {
112  if (!uses_internal_paths())
113    return 0;
114
115  size_t pos = create_info_.internal_path.rfind('/');
116  CHECK(pos != std::string::npos);
117  if (pos == 0)
118    pos++;
119  std::string parent_path = create_info_.internal_path.substr(0, pos);
120
121  ppapi::FileRefCreateInfo parent_info;
122  parent_info.file_system_type = create_info_.file_system_type;
123  parent_info.internal_path = parent_path;
124  parent_info.display_name = GetNameForInternalFilePath(parent_path);
125  parent_info.file_system_plugin_resource =
126      create_info_.file_system_plugin_resource;
127
128  return (new FileRefResource(connection(),
129                              pp_instance(),
130                              parent_info))->GetReference();
131}
132
133int32_t FileRefResource::MakeDirectory(
134    int32_t make_directory_flags,
135    scoped_refptr<TrackedCallback> callback) {
136  Call<PpapiPluginMsg_FileRef_MakeDirectoryReply>(
137      BROWSER,
138      PpapiHostMsg_FileRef_MakeDirectory(make_directory_flags),
139      base::Bind(&FileRefResource::RunTrackedCallback, this, callback));
140  return PP_OK_COMPLETIONPENDING;
141}
142
143int32_t FileRefResource::Touch(PP_Time last_access_time,
144                               PP_Time last_modified_time,
145                               scoped_refptr<TrackedCallback> callback) {
146  Call<PpapiPluginMsg_FileRef_TouchReply>(
147      BROWSER,
148      PpapiHostMsg_FileRef_Touch(last_access_time,
149                                 last_modified_time),
150      base::Bind(&FileRefResource::RunTrackedCallback, this, callback));
151  return PP_OK_COMPLETIONPENDING;
152}
153
154int32_t FileRefResource::Delete(scoped_refptr<TrackedCallback> callback) {
155  Call<PpapiPluginMsg_FileRef_DeleteReply>(
156      BROWSER,
157      PpapiHostMsg_FileRef_Delete(),
158      base::Bind(&FileRefResource::RunTrackedCallback, this, callback));
159  return PP_OK_COMPLETIONPENDING;
160}
161
162int32_t FileRefResource::Rename(PP_Resource new_file_ref,
163                                scoped_refptr<TrackedCallback> callback) {
164  Call<PpapiPluginMsg_FileRef_RenameReply>(
165      BROWSER,
166      PpapiHostMsg_FileRef_Rename(new_file_ref),
167      base::Bind(&FileRefResource::RunTrackedCallback, this, callback));
168  return PP_OK_COMPLETIONPENDING;
169}
170
171int32_t FileRefResource::Query(PP_FileInfo* info,
172                               scoped_refptr<TrackedCallback> callback) {
173  if (info == NULL)
174    return PP_ERROR_BADARGUMENT;
175
176  Call<PpapiPluginMsg_FileRef_QueryReply>(
177      BROWSER,
178      PpapiHostMsg_FileRef_Query(),
179      base::Bind(&FileRefResource::OnQueryReply, this, info, callback));
180  return PP_OK_COMPLETIONPENDING;
181}
182
183int32_t FileRefResource::ReadDirectoryEntries(
184    const PP_ArrayOutput& output,
185    scoped_refptr<TrackedCallback> callback) {
186  Call<PpapiPluginMsg_FileRef_ReadDirectoryEntriesReply>(
187      BROWSER,
188      PpapiHostMsg_FileRef_ReadDirectoryEntries(),
189      base::Bind(&FileRefResource::OnDirectoryEntriesReply,
190                 this, output, callback));
191  return PP_OK_COMPLETIONPENDING;
192}
193
194const FileRefCreateInfo& FileRefResource::GetCreateInfo() const {
195  return create_info_;
196}
197
198PP_Var FileRefResource::GetAbsolutePath() {
199  if (!absolute_path_var_.get()) {
200    std::string absolute_path;
201    int32_t result = SyncCall<PpapiPluginMsg_FileRef_GetAbsolutePathReply>(
202        BROWSER, PpapiHostMsg_FileRef_GetAbsolutePath(), &absolute_path);
203    if (result != PP_OK)
204      return PP_MakeUndefined();
205    absolute_path_var_ = new StringVar(absolute_path);
206  }
207  return absolute_path_var_->GetPPVar();
208}
209
210void FileRefResource::RunTrackedCallback(
211    scoped_refptr<TrackedCallback> callback,
212    const ResourceMessageReplyParams& params) {
213  if (TrackedCallback::IsPending(callback))
214    callback->Run(params.result());
215}
216
217void FileRefResource::OnQueryReply(
218    PP_FileInfo* out_info,
219    scoped_refptr<TrackedCallback> callback,
220    const ResourceMessageReplyParams& params,
221    const PP_FileInfo& info) {
222  if (!TrackedCallback::IsPending(callback))
223    return;
224
225  if (params.result() == PP_OK)
226    *out_info = info;
227  callback->Run(params.result());
228}
229
230void FileRefResource::OnDirectoryEntriesReply(
231    const PP_ArrayOutput& output,
232    scoped_refptr<TrackedCallback> callback,
233    const ResourceMessageReplyParams& params,
234    const std::vector<ppapi::FileRefCreateInfo>& infos,
235    const std::vector<PP_FileType>& file_types) {
236  if (!TrackedCallback::IsPending(callback))
237    return;
238
239  if (params.result() == PP_OK) {
240    ArrayWriter writer(output);
241    if (!writer.is_valid()) {
242      callback->Run(PP_ERROR_BADARGUMENT);
243      return;
244    }
245
246    std::vector<PP_DirectoryEntry> entries;
247    for (size_t i = 0; i < infos.size(); ++i) {
248      PP_DirectoryEntry entry;
249      entry.file_ref = FileRefResource::CreateFileRef(connection(),
250                                                      pp_instance(),
251                                                      infos[i]);
252      entry.file_type = file_types[i];
253      entries.push_back(entry);
254    }
255
256    writer.StoreVector(entries);
257  }
258  callback->Run(params.result());
259}
260
261bool FileRefResource::uses_internal_paths() const {
262  return (create_info_.file_system_type != PP_FILESYSTEMTYPE_EXTERNAL) ||
263         !create_info_.internal_path.empty();
264}
265
266}  // namespace proxy
267}  // namespace ppapi
268