OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "webkit/plugins/ppapi/ppb_file_ref_impl.h" | |
6 | |
7 #include "base/files/file_util_proxy.h" | |
8 #include "base/platform_file.h" | |
9 #include "base/strings/string_util.h" | |
10 #include "base/strings/utf_string_conversions.h" | |
11 #include "net/base/escape.h" | |
12 #include "ppapi/c/pp_errors.h" | |
13 #include "ppapi/shared_impl/file_type_conversion.h" | |
14 #include "ppapi/shared_impl/time_conversion.h" | |
15 #include "ppapi/shared_impl/var.h" | |
16 #include "ppapi/thunk/enter.h" | |
17 #include "ppapi/thunk/ppb_file_system_api.h" | |
18 #include "url/gurl.h" | |
19 #include "webkit/common/fileapi/directory_entry.h" | |
20 #include "webkit/common/fileapi/file_system_util.h" | |
21 #include "webkit/plugins/ppapi/common.h" | |
22 #include "webkit/plugins/ppapi/plugin_delegate.h" | |
23 #include "webkit/plugins/ppapi/plugin_module.h" | |
24 #include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" | |
25 #include "webkit/plugins/ppapi/resource_helper.h" | |
26 | |
27 using ppapi::HostResource; | |
28 using ppapi::PPB_FileRef_CreateInfo; | |
29 using ppapi::PPTimeToTime; | |
30 using ppapi::StringVar; | |
31 using ppapi::TrackedCallback; | |
32 using ppapi::thunk::EnterResourceNoLock; | |
33 using ppapi::thunk::PPB_FileRef_API; | |
34 using ppapi::thunk::PPB_FileSystem_API; | |
35 | |
36 namespace webkit { | |
37 namespace ppapi { | |
38 | |
39 namespace { | |
40 | |
41 bool IsValidLocalPath(const std::string& path) { | |
42 // The path must start with '/' | |
43 if (path.empty() || path[0] != '/') | |
44 return false; | |
45 | |
46 // The path must contain valid UTF-8 characters. | |
47 if (!IsStringUTF8(path)) | |
48 return false; | |
49 | |
50 #if defined(OS_WIN) | |
51 base::FilePath::StringType path_win(path.begin(), path.end()); | |
52 base::FilePath file_path(path_win); | |
53 #else | |
54 base::FilePath file_path(path); | |
55 #endif | |
56 if (file_path.ReferencesParent()) | |
57 return false; | |
58 | |
59 return true; | |
60 } | |
61 | |
62 void TrimTrailingSlash(std::string* path) { | |
63 // If this path ends with a slash, then normalize it away unless path is the | |
64 // root path. | |
65 if (path->size() > 1 && path->at(path->size() - 1) == '/') | |
66 path->erase(path->size() - 1, 1); | |
67 } | |
68 | |
69 std::string GetNameForExternalFilePath(const base::FilePath& in_path) { | |
70 const base::FilePath::StringType& path = in_path.value(); | |
71 size_t pos = path.rfind(base::FilePath::kSeparators[0]); | |
72 CHECK(pos != base::FilePath::StringType::npos); | |
73 #if defined(OS_WIN) | |
74 return WideToUTF8(path.substr(pos + 1)); | |
75 #elif defined(OS_POSIX) | |
76 return path.substr(pos + 1); | |
77 #else | |
78 #error "Unsupported platform." | |
79 #endif | |
80 } | |
81 | |
82 std::string GetNameForVirtualFilePath(const std::string& path) { | |
83 if (path.size() == 1 && path[0] == '/') | |
84 return path; | |
85 | |
86 // There should always be a leading slash at least! | |
87 size_t pos = path.rfind('/'); | |
88 CHECK(pos != std::string::npos); | |
89 return path.substr(pos + 1); | |
90 } | |
91 | |
92 void IgnoreCloseCallback(base::PlatformFileError error_code) { | |
93 } | |
94 | |
95 void PlatformFileInfoToPPFileInfo( | |
96 const base::PlatformFileInfo& file_info, | |
97 PP_FileSystemType file_system_type, | |
98 PP_FileInfo* info) { | |
99 DCHECK(info); | |
100 ::ppapi::PlatformFileInfoToPepperFileInfo(file_info, file_system_type, info); | |
101 } | |
102 | |
103 void GetFileInfoCallback( | |
104 scoped_refptr<base::TaskRunner> task_runner, | |
105 base::PlatformFile file, | |
106 linked_ptr<PP_FileInfo> info, | |
107 scoped_refptr<TrackedCallback> callback, | |
108 base::PlatformFileError error_code, | |
109 const base::PlatformFileInfo& file_info) { | |
110 base::FileUtilProxy::Close( | |
111 task_runner.get(), file, base::Bind(&IgnoreCloseCallback)); | |
112 | |
113 if (!TrackedCallback::IsPending(callback)) | |
114 return; | |
115 | |
116 int32_t pp_error = ::ppapi::PlatformFileErrorToPepperError(error_code); | |
117 if (pp_error != PP_OK) { | |
118 callback->Run(pp_error); | |
119 return; | |
120 } | |
121 | |
122 PlatformFileInfoToPPFileInfo( | |
123 file_info, PP_FILESYSTEMTYPE_EXTERNAL, info.get()); | |
124 | |
125 callback->Run(PP_OK); | |
126 } | |
127 | |
128 void QueryCallback(scoped_refptr<base::TaskRunner> task_runner, | |
129 linked_ptr<PP_FileInfo> info, | |
130 scoped_refptr<TrackedCallback> callback, | |
131 base::PlatformFileError error_code, | |
132 base::PassPlatformFile passed_file) { | |
133 if (!TrackedCallback::IsPending(callback)) | |
134 return; | |
135 | |
136 int32_t pp_error = ::ppapi::PlatformFileErrorToPepperError(error_code); | |
137 if (pp_error != PP_OK) { | |
138 callback->Run(pp_error); | |
139 return; | |
140 } | |
141 base::PlatformFile file = passed_file.ReleaseValue(); | |
142 | |
143 if (!base::FileUtilProxy::GetFileInfoFromPlatformFile( | |
144 task_runner.get(), | |
145 file, | |
146 base::Bind( | |
147 &GetFileInfoCallback, task_runner, file, info, callback))) { | |
148 base::FileUtilProxy::Close( | |
149 task_runner.get(), file, base::Bind(&IgnoreCloseCallback)); | |
150 callback->Run(PP_ERROR_FAILED); | |
151 } | |
152 } | |
153 | |
154 void DidReadMetadata( | |
155 scoped_refptr< ::ppapi::TrackedCallback> callback, | |
156 linked_ptr<PP_FileInfo> info, | |
157 PP_FileSystemType file_system_type, | |
158 const base::PlatformFileInfo& file_info) { | |
159 if (!TrackedCallback::IsPending(callback)) | |
160 return; | |
161 | |
162 PlatformFileInfoToPPFileInfo(file_info, file_system_type, info.get()); | |
163 callback->Run(PP_OK); | |
164 } | |
165 | |
166 void DidReadDirectory( | |
167 scoped_refptr< ::ppapi::TrackedCallback> callback, | |
168 PPB_FileRef_Impl* dir_ref, | |
169 linked_ptr<std::vector< ::ppapi::PPB_FileRef_CreateInfo> > dir_files, | |
170 linked_ptr<std::vector<PP_FileType> > dir_file_types, | |
171 const std::vector<fileapi::DirectoryEntry>& entries, | |
172 bool has_more) { | |
173 if (!TrackedCallback::IsPending(callback)) | |
174 return; | |
175 | |
176 // The current filesystem backend always returns false. | |
177 DCHECK(!has_more); | |
178 | |
179 DCHECK(dir_ref); | |
180 DCHECK(dir_files.get()); | |
181 DCHECK(dir_file_types.get()); | |
182 | |
183 std::string dir_path = dir_ref->GetCreateInfo().path; | |
184 if (dir_path.empty() || dir_path[dir_path.size() - 1] != '/') | |
185 dir_path += '/'; | |
186 | |
187 for (size_t i = 0; i < entries.size(); ++i) { | |
188 const fileapi::DirectoryEntry& entry = entries[i]; | |
189 scoped_refptr<PPB_FileRef_Impl> file_ref(PPB_FileRef_Impl::CreateInternal( | |
190 dir_ref->pp_instance(), | |
191 dir_ref->file_system_resource(), | |
192 dir_path + fileapi::FilePathToString(base::FilePath(entry.name)))); | |
193 dir_files->push_back(file_ref->GetCreateInfo()); | |
194 dir_file_types->push_back( | |
195 entry.is_directory ? PP_FILETYPE_DIRECTORY : PP_FILETYPE_REGULAR); | |
196 // Add a ref count on behalf of the plugin side. | |
197 file_ref->GetReference(); | |
198 } | |
199 CHECK_EQ(dir_files->size(), dir_file_types->size()); | |
200 | |
201 callback->Run(PP_OK); | |
202 } | |
203 | |
204 void DidFinishFileOperation( | |
205 scoped_refptr< ::ppapi::TrackedCallback> callback, | |
206 base::PlatformFileError error_code) { | |
207 if (callback->completed()) | |
208 return; | |
209 callback->Run(::ppapi::PlatformFileErrorToPepperError(error_code)); | |
210 } | |
211 | |
212 } // namespace | |
213 | |
214 PPB_FileRef_Impl::PPB_FileRef_Impl(const PPB_FileRef_CreateInfo& info, | |
215 PP_Resource file_system) | |
216 : PPB_FileRef_Shared(::ppapi::OBJECT_IS_IMPL, info), | |
217 file_system_(file_system), | |
218 external_file_system_path_() { | |
219 } | |
220 | |
221 PPB_FileRef_Impl::PPB_FileRef_Impl(const PPB_FileRef_CreateInfo& info, | |
222 const base::FilePath& external_file_path) | |
223 : PPB_FileRef_Shared(::ppapi::OBJECT_IS_IMPL, info), | |
224 file_system_(), | |
225 external_file_system_path_(external_file_path) { | |
226 } | |
227 | |
228 PPB_FileRef_Impl::~PPB_FileRef_Impl() { | |
229 } | |
230 | |
231 // static | |
232 PPB_FileRef_Impl* PPB_FileRef_Impl::CreateInternal(PP_Instance instance, | |
233 PP_Resource pp_file_system, | |
234 const std::string& path) { | |
235 PluginInstanceImpl* plugin_instance = | |
236 ResourceHelper::PPInstanceToPluginInstance(instance); | |
237 if (!plugin_instance || !plugin_instance->delegate()) | |
238 return 0; | |
239 | |
240 PP_FileSystemType type = | |
241 plugin_instance->delegate()->GetFileSystemType(instance, pp_file_system); | |
242 if (type != PP_FILESYSTEMTYPE_LOCALPERSISTENT && | |
243 type != PP_FILESYSTEMTYPE_LOCALTEMPORARY && | |
244 type != PP_FILESYSTEMTYPE_EXTERNAL && | |
245 type != PP_FILESYSTEMTYPE_ISOLATED) | |
246 return 0; | |
247 | |
248 PPB_FileRef_CreateInfo info; | |
249 info.resource = HostResource::MakeInstanceOnly(instance); | |
250 info.file_system_plugin_resource = pp_file_system; | |
251 info.file_system_type = type; | |
252 | |
253 // Validate the path. | |
254 info.path = path; | |
255 if (!IsValidLocalPath(info.path)) | |
256 return 0; | |
257 TrimTrailingSlash(&info.path); | |
258 | |
259 info.name = GetNameForVirtualFilePath(info.path); | |
260 | |
261 PPB_FileRef_Impl* file_ref = new PPB_FileRef_Impl(info, pp_file_system); | |
262 if (plugin_instance->delegate()->IsRunningInProcess(instance)) | |
263 file_ref->AddFileSystemRefCount(); | |
264 return file_ref; | |
265 } | |
266 | |
267 // static | |
268 PPB_FileRef_Impl* PPB_FileRef_Impl::CreateExternal( | |
269 PP_Instance instance, | |
270 const base::FilePath& external_file_path, | |
271 const std::string& display_name) { | |
272 PPB_FileRef_CreateInfo info; | |
273 info.resource = HostResource::MakeInstanceOnly(instance); | |
274 info.file_system_plugin_resource = 0; | |
275 info.file_system_type = PP_FILESYSTEMTYPE_EXTERNAL; | |
276 if (display_name.empty()) | |
277 info.name = GetNameForExternalFilePath(external_file_path); | |
278 else | |
279 info.name = display_name; | |
280 | |
281 return new PPB_FileRef_Impl(info, external_file_path); | |
282 } | |
283 | |
284 PP_Resource PPB_FileRef_Impl::GetParent() { | |
285 if (GetFileSystemType() == PP_FILESYSTEMTYPE_EXTERNAL) | |
286 return 0; | |
287 | |
288 const std::string& virtual_path = GetCreateInfo().path; | |
289 | |
290 // There should always be a leading slash at least! | |
291 size_t pos = virtual_path.rfind('/'); | |
292 CHECK(pos != std::string::npos); | |
293 | |
294 // If the path is "/foo", then we want to include the slash. | |
295 if (pos == 0) | |
296 pos++; | |
297 std::string parent_path = virtual_path.substr(0, pos); | |
298 | |
299 scoped_refptr<PPB_FileRef_Impl> parent_ref( | |
300 CreateInternal(pp_instance(), file_system_, parent_path)); | |
301 if (!parent_ref.get()) | |
302 return 0; | |
303 return parent_ref->GetReference(); | |
304 } | |
305 | |
306 int32_t PPB_FileRef_Impl::MakeDirectory( | |
307 PP_Bool make_ancestors, | |
308 scoped_refptr<TrackedCallback> callback) { | |
309 if (!IsValidNonExternalFileSystem()) | |
310 return PP_ERROR_NOACCESS; | |
311 | |
312 PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); | |
313 if (!plugin_instance) | |
314 return PP_ERROR_FAILED; | |
315 plugin_instance->delegate()->MakeDirectory( | |
316 GetFileSystemURL(), PP_ToBool(make_ancestors), | |
317 base::Bind(&DidFinishFileOperation, callback)); | |
318 return PP_OK_COMPLETIONPENDING; | |
319 } | |
320 | |
321 int32_t PPB_FileRef_Impl::Touch(PP_Time last_access_time, | |
322 PP_Time last_modified_time, | |
323 scoped_refptr<TrackedCallback> callback) { | |
324 if (!IsValidNonExternalFileSystem()) | |
325 return PP_ERROR_NOACCESS; | |
326 | |
327 PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); | |
328 if (!plugin_instance) | |
329 return PP_ERROR_FAILED; | |
330 plugin_instance->delegate()->Touch( | |
331 GetFileSystemURL(), | |
332 PPTimeToTime(last_access_time), | |
333 PPTimeToTime(last_modified_time), | |
334 base::Bind(&DidFinishFileOperation, callback)); | |
335 return PP_OK_COMPLETIONPENDING; | |
336 } | |
337 | |
338 int32_t PPB_FileRef_Impl::Delete(scoped_refptr<TrackedCallback> callback) { | |
339 if (!IsValidNonExternalFileSystem()) | |
340 return PP_ERROR_NOACCESS; | |
341 | |
342 PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); | |
343 if (!plugin_instance) | |
344 return PP_ERROR_FAILED; | |
345 plugin_instance->delegate()->Delete( | |
346 GetFileSystemURL(), | |
347 base::Bind(&DidFinishFileOperation, callback)); | |
348 return PP_OK_COMPLETIONPENDING; | |
349 } | |
350 | |
351 int32_t PPB_FileRef_Impl::Rename(PP_Resource new_pp_file_ref, | |
352 scoped_refptr<TrackedCallback> callback) { | |
353 EnterResourceNoLock<PPB_FileRef_API> enter(new_pp_file_ref, true); | |
354 if (enter.failed()) | |
355 return PP_ERROR_BADRESOURCE; | |
356 PPB_FileRef_Impl* new_file_ref = | |
357 static_cast<PPB_FileRef_Impl*>(enter.object()); | |
358 | |
359 if (!IsValidNonExternalFileSystem() || | |
360 file_system_ != new_file_ref->file_system_) | |
361 return PP_ERROR_NOACCESS; | |
362 | |
363 // TODO(viettrungluu): Also cancel when the new file ref is destroyed? | |
364 // http://crbug.com/67624 | |
365 PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); | |
366 if (!plugin_instance) | |
367 return PP_ERROR_FAILED; | |
368 plugin_instance->delegate()->Rename( | |
369 GetFileSystemURL(), new_file_ref->GetFileSystemURL(), | |
370 base::Bind(&DidFinishFileOperation, callback)); | |
371 return PP_OK_COMPLETIONPENDING; | |
372 } | |
373 | |
374 PP_Var PPB_FileRef_Impl::GetAbsolutePath() { | |
375 if (GetFileSystemType() != PP_FILESYSTEMTYPE_EXTERNAL) | |
376 return GetPath(); | |
377 if (!external_path_var_.get()) { | |
378 external_path_var_ = | |
379 new StringVar(external_file_system_path_.AsUTF8Unsafe()); | |
380 } | |
381 return external_path_var_->GetPPVar(); | |
382 } | |
383 | |
384 base::FilePath PPB_FileRef_Impl::GetSystemPath() const { | |
385 if (GetFileSystemType() != PP_FILESYSTEMTYPE_EXTERNAL) { | |
386 NOTREACHED(); | |
387 return base::FilePath(); | |
388 } | |
389 return external_file_system_path_; | |
390 } | |
391 | |
392 GURL PPB_FileRef_Impl::GetFileSystemURL() const { | |
393 if (GetFileSystemType() != PP_FILESYSTEMTYPE_LOCALPERSISTENT && | |
394 GetFileSystemType() != PP_FILESYSTEMTYPE_LOCALTEMPORARY && | |
395 GetFileSystemType() != PP_FILESYSTEMTYPE_EXTERNAL && | |
396 GetFileSystemType() != PP_FILESYSTEMTYPE_ISOLATED) { | |
397 NOTREACHED(); | |
398 return GURL(); | |
399 } | |
400 | |
401 const std::string& virtual_path = GetCreateInfo().path; | |
402 CHECK(!virtual_path.empty()); // Should always be at least "/". | |
403 | |
404 // Since |virtual_path_| starts with a '/', it looks like an absolute path. | |
405 // We need to trim off the '/' before calling Resolve, as FileSystem URLs | |
406 // start with a storage type identifier that looks like a path segment. | |
407 | |
408 PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); | |
409 PluginDelegate* delegate = | |
410 plugin_instance ? plugin_instance->delegate() : NULL; | |
411 if (!delegate) | |
412 return GURL(); | |
413 return GURL(delegate->GetFileSystemRootUrl(pp_instance(), file_system_)) | |
414 .Resolve(net::EscapePath(virtual_path.substr(1))); | |
415 } | |
416 | |
417 bool PPB_FileRef_Impl::IsValidNonExternalFileSystem() const { | |
418 PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); | |
419 PluginDelegate* delegate = | |
420 plugin_instance ? plugin_instance->delegate() : NULL; | |
421 return delegate && | |
422 delegate->IsFileSystemOpened(pp_instance(), file_system_) && | |
423 delegate->GetFileSystemType(pp_instance(), file_system_) != | |
424 PP_FILESYSTEMTYPE_EXTERNAL; | |
425 } | |
426 | |
427 bool PPB_FileRef_Impl::HasValidFileSystem() const { | |
428 PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); | |
429 PluginDelegate* delegate = | |
430 plugin_instance ? plugin_instance->delegate() : NULL; | |
431 return delegate && delegate->IsFileSystemOpened(pp_instance(), file_system_); | |
432 } | |
433 | |
434 int32_t PPB_FileRef_Impl::Query(PP_FileInfo* info, | |
435 scoped_refptr<TrackedCallback> callback) { | |
436 NOTREACHED(); | |
437 return PP_ERROR_FAILED; | |
438 } | |
439 | |
440 int32_t PPB_FileRef_Impl::QueryInHost( | |
441 linked_ptr<PP_FileInfo> info, | |
442 scoped_refptr<TrackedCallback> callback) { | |
443 scoped_refptr<PluginInstanceImpl> plugin_instance = | |
444 ResourceHelper::GetPluginInstance(this); | |
445 if (!plugin_instance.get()) | |
446 return PP_ERROR_FAILED; | |
447 | |
448 if (!file_system_) { | |
449 // External file system | |
450 // We have to do something totally different for external file systems. | |
451 | |
452 // TODO(teravest): Use the SequencedWorkerPool instead. | |
453 scoped_refptr<base::TaskRunner> task_runner = | |
454 plugin_instance->delegate()->GetFileThreadMessageLoopProxy(); | |
455 if (!plugin_instance->delegate()->AsyncOpenFile( | |
456 GetSystemPath(), | |
457 base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, | |
458 base::Bind(&QueryCallback, task_runner, info, callback))) | |
459 return PP_ERROR_FAILED; | |
460 } else { | |
461 // Non-external file system | |
462 if (!HasValidFileSystem()) | |
463 return PP_ERROR_NOACCESS; | |
464 | |
465 PluginInstanceImpl* plugin_instance = | |
466 ResourceHelper::GetPluginInstance(this); | |
467 PluginDelegate* delegate = | |
468 plugin_instance ? plugin_instance->delegate() : NULL; | |
469 if (!delegate) | |
470 return PP_ERROR_FAILED; | |
471 | |
472 PP_FileSystemType file_system_type = | |
473 delegate->GetFileSystemType(pp_instance(), file_system_); | |
474 plugin_instance->delegate()->Query( | |
475 GetFileSystemURL(), | |
476 base::Bind(&DidReadMetadata, callback, info, file_system_type), | |
477 base::Bind(&DidFinishFileOperation, callback)); | |
478 } | |
479 return PP_OK_COMPLETIONPENDING; | |
480 } | |
481 | |
482 int32_t PPB_FileRef_Impl::ReadDirectoryEntries( | |
483 const PP_ArrayOutput& output, | |
484 scoped_refptr<TrackedCallback> callback) { | |
485 NOTREACHED(); | |
486 return PP_ERROR_FAILED; | |
487 } | |
488 | |
489 int32_t PPB_FileRef_Impl::ReadDirectoryEntriesInHost( | |
490 linked_ptr<std::vector< ::ppapi::PPB_FileRef_CreateInfo> > files, | |
491 linked_ptr<std::vector<PP_FileType> > file_types, | |
492 scoped_refptr<TrackedCallback> callback) { | |
493 if (!IsValidNonExternalFileSystem()) | |
494 return PP_ERROR_NOACCESS; | |
495 | |
496 PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); | |
497 if (!plugin_instance) | |
498 return PP_ERROR_FAILED; | |
499 | |
500 // TODO(yzshen): Passing base::Unretained(this) to the callback could | |
501 // be dangerous. | |
502 plugin_instance->delegate()->ReadDirectoryEntries( | |
503 GetFileSystemURL(), | |
504 base::Bind(&DidReadDirectory, | |
505 callback, base::Unretained(this), files, file_types), | |
506 base::Bind(&DidFinishFileOperation, callback)); | |
507 return PP_OK_COMPLETIONPENDING; | |
508 } | |
509 | |
510 } // namespace ppapi | |
511 } // namespace webkit | |
OLD | NEW |