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()) {
50 std::vector<std::string> GetVirtualPathComponents(
51 const fileapi::FileSystemURL& url) {
52 ImportedMediaGalleryRegistry* imported_registry =
53 ImportedMediaGalleryRegistry::GetInstance();
54 base::FilePath root = imported_registry->ImportedRoot().AppendASCII("iphoto");
56 DCHECK(root.IsParent(url.path()) || root == url.path());
57 base::FilePath virtual_path;
58 root.AppendRelativePath(url.path(), &virtual_path);
60 std::vector<std::string> result;
61 fileapi::VirtualPath::GetComponentsUTF8Unsafe(virtual_path, &result);
67 const char kIPhotoAlbumsDir[] = "Albums";
68 const char kIPhotoOriginalsDir[] = "Originals";
70 IPhotoFileUtil::IPhotoFileUtil(MediaPathFilter* media_path_filter)
71 : NativeMediaFileUtil(media_path_filter),
73 imported_registry_(NULL) {
76 IPhotoFileUtil::~IPhotoFileUtil() {
79 void IPhotoFileUtil::GetFileInfoOnTaskRunnerThread(
80 scoped_ptr<fileapi::FileSystemOperationContext> context,
81 const fileapi::FileSystemURL& url,
82 const GetFileInfoCallback& callback) {
83 GetDataProvider()->RefreshData(
84 base::Bind(&IPhotoFileUtil::GetFileInfoWithFreshDataProvider,
85 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
89 void IPhotoFileUtil::ReadDirectoryOnTaskRunnerThread(
90 scoped_ptr<fileapi::FileSystemOperationContext> context,
91 const fileapi::FileSystemURL& url,
92 const ReadDirectoryCallback& callback) {
93 GetDataProvider()->RefreshData(
94 base::Bind(&IPhotoFileUtil::ReadDirectoryWithFreshDataProvider,
95 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
99 void IPhotoFileUtil::CreateSnapshotFileOnTaskRunnerThread(
100 scoped_ptr<fileapi::FileSystemOperationContext> context,
101 const fileapi::FileSystemURL& url,
102 const CreateSnapshotFileCallback& callback) {
103 GetDataProvider()->RefreshData(
104 base::Bind(&IPhotoFileUtil::CreateSnapshotFileWithFreshDataProvider,
105 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
109 void IPhotoFileUtil::GetFileInfoWithFreshDataProvider(
110 scoped_ptr<fileapi::FileSystemOperationContext> context,
111 const fileapi::FileSystemURL& url,
112 const GetFileInfoCallback& callback,
115 if (!callback.is_null()) {
116 content::BrowserThread::PostTask(
117 content::BrowserThread::IO,
119 base::Bind(callback, base::File::FILE_ERROR_IO, base::File::Info()));
123 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
127 void IPhotoFileUtil::ReadDirectoryWithFreshDataProvider(
128 scoped_ptr<fileapi::FileSystemOperationContext> context,
129 const fileapi::FileSystemURL& url,
130 const ReadDirectoryCallback& callback,
133 if (!callback.is_null()) {
134 content::BrowserThread::PostTask(
135 content::BrowserThread::IO,
137 base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false));
141 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
145 void IPhotoFileUtil::CreateSnapshotFileWithFreshDataProvider(
146 scoped_ptr<fileapi::FileSystemOperationContext> context,
147 const fileapi::FileSystemURL& url,
148 const CreateSnapshotFileCallback& callback,
151 if (!callback.is_null()) {
152 base::File::Info file_info;
153 base::FilePath platform_path;
154 scoped_refptr<webkit_blob::ShareableFileReference> file_ref;
155 content::BrowserThread::PostTask(
156 content::BrowserThread::IO,
158 base::Bind(callback, base::File::FILE_ERROR_IO, file_info,
159 platform_path, file_ref));
163 NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
167 // Begin actual implementation.
169 base::File::Error IPhotoFileUtil::GetFileInfoSync(
170 fileapi::FileSystemOperationContext* context,
171 const fileapi::FileSystemURL& url,
172 base::File::Info* file_info,
173 base::FilePath* platform_path) {
174 std::vector<std::string> components = GetVirtualPathComponents(url);
176 if (components.size() == 0) {
177 return MakeDirectoryFileInfo(file_info);
180 // The 'Albums' directory.
181 if (components[0] == kIPhotoAlbumsDir) {
182 if (components.size() == 1) {
183 return MakeDirectoryFileInfo(file_info);
184 } else if (components.size() == 2) {
185 std::vector<std::string> albums =
186 GetDataProvider()->GetAlbumNames();
187 if (ContainsElement(albums, components[1]))
188 return MakeDirectoryFileInfo(file_info);
189 } else if (components.size() == 3) {
190 if (components[2] == kIPhotoOriginalsDir) {
191 if (GetDataProvider()->HasOriginals(components[1]))
192 return MakeDirectoryFileInfo(file_info);
194 return base::File::FILE_ERROR_NOT_FOUND;
197 base::FilePath location = GetDataProvider()->GetPhotoLocationInAlbum(
198 components[1], components[2]);
199 if (!location.empty()) {
200 return NativeMediaFileUtil::GetFileInfoSync(
201 context, url, file_info, platform_path);
203 } else if (components.size() == 4 &&
204 GetDataProvider()->HasOriginals(components[1]) &&
205 components[2] == kIPhotoOriginalsDir) {
206 base::FilePath location = GetDataProvider()->GetOriginalPhotoLocation(
207 components[1], components[3]);
208 if (!location.empty()) {
209 return NativeMediaFileUtil::GetFileInfoSync(
210 context, url, file_info, platform_path);
215 return base::File::FILE_ERROR_NOT_FOUND;
218 base::File::Error IPhotoFileUtil::ReadDirectorySync(
219 fileapi::FileSystemOperationContext* context,
220 const fileapi::FileSystemURL& url,
221 EntryList* file_list) {
222 DCHECK(file_list->empty());
223 std::vector<std::string> components = GetVirtualPathComponents(url);
225 // Root directory. Child is the /Albums dir.
226 if (components.size() == 0) {
227 file_list->push_back(DirectoryEntry(kIPhotoAlbumsDir,
228 DirectoryEntry::DIRECTORY,
230 return base::File::FILE_OK;
233 if (components[0] == kIPhotoAlbumsDir) {
234 if (components.size() == 1) {
235 // Albums dir contains all album names.
236 std::vector<std::string> albums =
237 GetDataProvider()->GetAlbumNames();
238 for (std::vector<std::string>::const_iterator it = albums.begin();
239 it != albums.end(); it++) {
240 file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
243 return base::File::FILE_OK;
244 } else if (components.size() == 2) {
245 std::vector<std::string> albums =
246 GetDataProvider()->GetAlbumNames();
247 if (!ContainsElement(albums, components[1]))
248 return base::File::FILE_ERROR_NOT_FOUND;
250 // Album dirs contain all photos in them.
251 if (GetDataProvider()->HasOriginals(components[1])) {
252 file_list->push_back(DirectoryEntry(kIPhotoOriginalsDir,
253 DirectoryEntry::DIRECTORY,
256 std::map<std::string, base::FilePath> locations =
257 GetDataProvider()->GetAlbumContents(components[1]);
258 for (std::map<std::string, base::FilePath>::const_iterator it =
260 it != locations.end(); it++) {
261 base::File::Info info;
262 if (!base::GetFileInfo(it->second, &info))
263 return base::File::FILE_ERROR_IO;
264 file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
265 info.size, info.last_modified));
267 return base::File::FILE_OK;
268 } else if (components.size() == 3 &&
269 components[2] == kIPhotoOriginalsDir &&
270 GetDataProvider()->HasOriginals(components[1])) {
271 std::map<std::string, base::FilePath> originals =
272 GetDataProvider()->GetOriginals(components[1]);
273 for (std::map<std::string, base::FilePath>::const_iterator it =
275 it != originals.end(); it++) {
276 base::File::Info info;
277 if (!base::GetFileInfo(it->second, &info))
278 return base::File::FILE_ERROR_IO;
279 file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
280 info.size, info.last_modified));
282 return base::File::FILE_OK;
286 return base::File::FILE_ERROR_NOT_FOUND;
289 base::File::Error IPhotoFileUtil::DeleteDirectorySync(
290 fileapi::FileSystemOperationContext* context,
291 const fileapi::FileSystemURL& url) {
292 return base::File::FILE_ERROR_SECURITY;
295 base::File::Error IPhotoFileUtil::DeleteFileSync(
296 fileapi::FileSystemOperationContext* context,
297 const fileapi::FileSystemURL& url) {
298 return base::File::FILE_ERROR_SECURITY;
302 base::File::Error IPhotoFileUtil::GetLocalFilePath(
303 fileapi::FileSystemOperationContext* context,
304 const fileapi::FileSystemURL& url,
305 base::FilePath* local_file_path) {
306 std::vector<std::string> components = GetVirtualPathComponents(url);
308 if (components.size() == 3 && components[0] == kIPhotoAlbumsDir) {
309 base::FilePath location = GetDataProvider()->GetPhotoLocationInAlbum(
310 components[1], components[2]);
311 if (!location.empty()) {
312 *local_file_path = location;
313 return base::File::FILE_OK;
317 if (components.size() == 4 && components[0] == kIPhotoAlbumsDir &&
318 GetDataProvider()->HasOriginals(components[1]) &&
319 components[2] == kIPhotoOriginalsDir) {
320 base::FilePath location = GetDataProvider()->GetOriginalPhotoLocation(
321 components[1], components[3]);
322 if (!location.empty()) {
323 *local_file_path = location;
324 return base::File::FILE_OK;
328 return base::File::FILE_ERROR_NOT_FOUND;
331 IPhotoDataProvider* IPhotoFileUtil::GetDataProvider() {
332 if (!imported_registry_)
333 imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
334 return imported_registry_->IPhotoDataProvider();
337 } // namespace iphoto