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.
5 #include "chrome/browser/media_galleries/fileapi/iphoto_file_util.h"
11 #include "base/bind_helpers.h"
12 #include "base/file_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/media_galleries/fileapi/iphoto_data_provider.h"
16 #include "chrome/browser/media_galleries/fileapi/media_path_filter.h"
17 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "webkit/browser/fileapi/file_system_operation_context.h"
20 #include "webkit/browser/fileapi/file_system_url.h"
21 #include "webkit/browser/fileapi/native_file_util.h"
22 #include "webkit/common/blob/shareable_file_reference.h"
23 #include "webkit/common/fileapi/directory_entry.h"
24 #include "webkit/common/fileapi/file_system_util.h"
26 using fileapi::DirectoryEntry;
32 base::File::Error MakeDirectoryFileInfo(base::File::Info* file_info) {
33 base::File::Info result;
34 result.is_directory = true;
36 return base::File::FILE_OK;
40 bool ContainsElement(const std::vector<T>& collection, const T& key) {
41 typename std::vector<T>::const_iterator it = collection.begin();
42 while (it != collection.end()) {
52 const char kIPhotoAlbumsDir[] = "Albums";
53 const char kIPhotoOriginalsDir[] = "Originals";
55 IPhotoFileUtil::IPhotoFileUtil(MediaPathFilter* media_path_filter)
56 : NativeMediaFileUtil(media_path_filter),
58 imported_registry_(NULL) {
61 IPhotoFileUtil::~IPhotoFileUtil() {
64 void IPhotoFileUtil::GetFileInfoOnTaskRunnerThread(
65 scoped_ptr<fileapi::FileSystemOperationContext> context,
66 const fileapi::FileSystemURL& url,
67 const GetFileInfoCallback& callback) {
68 IPhotoDataProvider* data_provider = GetDataProvider();
69 // |data_provider| may be NULL if the file system was revoked before this
70 // operation had a chance to run.
72 GetFileInfoWithFreshDataProvider(context.Pass(), url, callback, false);
74 data_provider->RefreshData(
75 base::Bind(&IPhotoFileUtil::GetFileInfoWithFreshDataProvider,
76 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
81 void IPhotoFileUtil::ReadDirectoryOnTaskRunnerThread(
82 scoped_ptr<fileapi::FileSystemOperationContext> context,
83 const fileapi::FileSystemURL& url,
84 const ReadDirectoryCallback& callback) {
85 IPhotoDataProvider* data_provider = GetDataProvider();
86 // |data_provider| may be NULL if the file system was revoked before this
87 // operation had a chance to run.
89 ReadDirectoryWithFreshDataProvider(context.Pass(), url, callback, false);
91 data_provider->RefreshData(
92 base::Bind(&IPhotoFileUtil::ReadDirectoryWithFreshDataProvider,
93 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
98 void IPhotoFileUtil::CreateSnapshotFileOnTaskRunnerThread(
99 scoped_ptr<fileapi::FileSystemOperationContext> context,
100 const fileapi::FileSystemURL& url,
101 const CreateSnapshotFileCallback& callback) {
102 IPhotoDataProvider* data_provider = GetDataProvider();
103 // |data_provider| may be NULL if the file system was revoked before this
104 // operation had a chance to run.
105 if (!data_provider) {
106 CreateSnapshotFileWithFreshDataProvider(context.Pass(), url, callback,
109 data_provider->RefreshData(
110 base::Bind(&IPhotoFileUtil::CreateSnapshotFileWithFreshDataProvider,
111 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
116 void IPhotoFileUtil::GetFileInfoWithFreshDataProvider(
117 scoped_ptr<fileapi::FileSystemOperationContext> context,
118 const fileapi::FileSystemURL& url,
119 const GetFileInfoCallback& callback,
122 if (!callback.is_null()) {
123 content::BrowserThread::PostTask(
124 content::BrowserThread::IO,
126 base::Bind(callback, base::File::FILE_ERROR_IO, base::File::Info()));
130 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
134 void IPhotoFileUtil::ReadDirectoryWithFreshDataProvider(
135 scoped_ptr<fileapi::FileSystemOperationContext> context,
136 const fileapi::FileSystemURL& url,
137 const ReadDirectoryCallback& callback,
140 if (!callback.is_null()) {
141 content::BrowserThread::PostTask(
142 content::BrowserThread::IO,
144 base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false));
148 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
152 void IPhotoFileUtil::CreateSnapshotFileWithFreshDataProvider(
153 scoped_ptr<fileapi::FileSystemOperationContext> context,
154 const fileapi::FileSystemURL& url,
155 const CreateSnapshotFileCallback& callback,
158 if (!callback.is_null()) {
159 base::File::Info file_info;
160 base::FilePath platform_path;
161 scoped_refptr<webkit_blob::ShareableFileReference> file_ref;
162 content::BrowserThread::PostTask(
163 content::BrowserThread::IO,
165 base::Bind(callback, base::File::FILE_ERROR_IO, file_info,
166 platform_path, file_ref));
170 NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
174 // Begin actual implementation.
176 base::File::Error IPhotoFileUtil::GetFileInfoSync(
177 fileapi::FileSystemOperationContext* context,
178 const fileapi::FileSystemURL& url,
179 base::File::Info* file_info,
180 base::FilePath* platform_path) {
181 std::vector<std::string> components;
182 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
184 if (components.size() == 0) {
185 return MakeDirectoryFileInfo(file_info);
188 // The 'Albums' directory.
189 if (components[0] == kIPhotoAlbumsDir) {
190 if (components.size() == 1) {
191 return MakeDirectoryFileInfo(file_info);
192 } else if (components.size() == 2) {
193 std::vector<std::string> albums =
194 GetDataProvider()->GetAlbumNames();
195 if (ContainsElement(albums, components[1]))
196 return MakeDirectoryFileInfo(file_info);
197 } else if (components.size() == 3) {
198 if (components[2] == kIPhotoOriginalsDir) {
199 if (GetDataProvider()->HasOriginals(components[1]))
200 return MakeDirectoryFileInfo(file_info);
202 return base::File::FILE_ERROR_NOT_FOUND;
205 base::FilePath location = GetDataProvider()->GetPhotoLocationInAlbum(
206 components[1], components[2]);
207 if (!location.empty()) {
208 return NativeMediaFileUtil::GetFileInfoSync(
209 context, url, file_info, platform_path);
211 } else if (components.size() == 4 &&
212 GetDataProvider()->HasOriginals(components[1]) &&
213 components[2] == kIPhotoOriginalsDir) {
214 base::FilePath location = GetDataProvider()->GetOriginalPhotoLocation(
215 components[1], components[3]);
216 if (!location.empty()) {
217 return NativeMediaFileUtil::GetFileInfoSync(
218 context, url, file_info, platform_path);
223 return base::File::FILE_ERROR_NOT_FOUND;
226 base::File::Error IPhotoFileUtil::ReadDirectorySync(
227 fileapi::FileSystemOperationContext* context,
228 const fileapi::FileSystemURL& url,
229 EntryList* file_list) {
230 DCHECK(file_list->empty());
231 std::vector<std::string> components;
232 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
234 // Root directory. Child is the /Albums dir.
235 if (components.size() == 0) {
236 file_list->push_back(DirectoryEntry(kIPhotoAlbumsDir,
237 DirectoryEntry::DIRECTORY,
239 return base::File::FILE_OK;
242 if (components[0] == kIPhotoAlbumsDir) {
243 if (components.size() == 1) {
244 // Albums dir contains all album names.
245 std::vector<std::string> albums =
246 GetDataProvider()->GetAlbumNames();
247 for (std::vector<std::string>::const_iterator it = albums.begin();
248 it != albums.end(); it++) {
249 file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
252 return base::File::FILE_OK;
253 } else if (components.size() == 2) {
254 std::vector<std::string> albums =
255 GetDataProvider()->GetAlbumNames();
256 if (!ContainsElement(albums, components[1]))
257 return base::File::FILE_ERROR_NOT_FOUND;
259 // Album dirs contain all photos in them.
260 if (GetDataProvider()->HasOriginals(components[1])) {
261 file_list->push_back(DirectoryEntry(kIPhotoOriginalsDir,
262 DirectoryEntry::DIRECTORY,
265 std::map<std::string, base::FilePath> locations =
266 GetDataProvider()->GetAlbumContents(components[1]);
267 for (std::map<std::string, base::FilePath>::const_iterator it =
269 it != locations.end(); it++) {
270 base::File::Info info;
271 if (!base::GetFileInfo(it->second, &info))
272 return base::File::FILE_ERROR_IO;
273 file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
274 info.size, info.last_modified));
276 return base::File::FILE_OK;
277 } else if (components.size() == 3 &&
278 components[2] == kIPhotoOriginalsDir &&
279 GetDataProvider()->HasOriginals(components[1])) {
280 std::map<std::string, base::FilePath> originals =
281 GetDataProvider()->GetOriginals(components[1]);
282 for (std::map<std::string, base::FilePath>::const_iterator it =
284 it != originals.end(); it++) {
285 base::File::Info info;
286 if (!base::GetFileInfo(it->second, &info))
287 return base::File::FILE_ERROR_IO;
288 file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
289 info.size, info.last_modified));
291 return base::File::FILE_OK;
295 return base::File::FILE_ERROR_NOT_FOUND;
298 base::File::Error IPhotoFileUtil::DeleteDirectorySync(
299 fileapi::FileSystemOperationContext* context,
300 const fileapi::FileSystemURL& url) {
301 return base::File::FILE_ERROR_SECURITY;
304 base::File::Error IPhotoFileUtil::DeleteFileSync(
305 fileapi::FileSystemOperationContext* context,
306 const fileapi::FileSystemURL& url) {
307 return base::File::FILE_ERROR_SECURITY;
311 base::File::Error IPhotoFileUtil::GetLocalFilePath(
312 fileapi::FileSystemOperationContext* context,
313 const fileapi::FileSystemURL& url,
314 base::FilePath* local_file_path) {
315 std::vector<std::string> components;
316 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
318 if (components.size() == 3 && components[0] == kIPhotoAlbumsDir) {
319 base::FilePath location = GetDataProvider()->GetPhotoLocationInAlbum(
320 components[1], components[2]);
321 if (!location.empty()) {
322 *local_file_path = location;
323 return base::File::FILE_OK;
327 if (components.size() == 4 && components[0] == kIPhotoAlbumsDir &&
328 GetDataProvider()->HasOriginals(components[1]) &&
329 components[2] == kIPhotoOriginalsDir) {
330 base::FilePath location = GetDataProvider()->GetOriginalPhotoLocation(
331 components[1], components[3]);
332 if (!location.empty()) {
333 *local_file_path = location;
334 return base::File::FILE_OK;
338 return base::File::FILE_ERROR_NOT_FOUND;
341 IPhotoDataProvider* IPhotoFileUtil::GetDataProvider() {
342 if (!imported_registry_)
343 imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
344 return imported_registry_->IPhotoDataProvider();
347 } // namespace iphoto