1 // Copyright (c) 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 "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/files/file_path.h" 11 #include "base/logging.h" 12 #include "base/message_loop/message_loop_proxy.h" 13 #include "base/sequenced_task_runner.h" 14 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_util.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "base/threading/sequenced_worker_pool.h" 18 #include "chrome/browser/browser_process.h" 19 #include "chrome/browser/extensions/extension_service.h" 20 #include "chrome/browser/media_galleries/fileapi/device_media_async_file_util.h" 21 #include "chrome/browser/media_galleries/fileapi/media_file_validator_factory.h" 22 #include "chrome/browser/media_galleries/fileapi/media_path_filter.h" 23 #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h" 24 #include "chrome/browser/media_galleries/media_file_system_registry.h" 25 #include "chrome/browser/profiles/profile.h" 26 #include "content/public/browser/browser_thread.h" 27 #include "content/public/browser/render_process_host.h" 28 #include "content/public/browser/render_view_host.h" 29 #include "content/public/browser/resource_request_info.h" 30 #include "extensions/browser/extension_system.h" 31 #include "net/url_request/url_request.h" 32 #include "webkit/browser/blob/file_stream_reader.h" 33 #include "webkit/browser/fileapi/copy_or_move_file_validator.h" 34 #include "webkit/browser/fileapi/file_stream_writer.h" 35 #include "webkit/browser/fileapi/file_system_context.h" 36 #include "webkit/browser/fileapi/file_system_operation.h" 37 #include "webkit/browser/fileapi/file_system_operation_context.h" 38 #include "webkit/browser/fileapi/file_system_url.h" 39 #include "webkit/browser/fileapi/native_file_util.h" 40 #include "webkit/common/fileapi/file_system_types.h" 41 #include "webkit/common/fileapi/file_system_util.h" 42 43 #if defined(OS_WIN) || defined(OS_MACOSX) 44 #include "chrome/browser/media_galleries/fileapi/itunes_file_util.h" 45 #include "chrome/browser/media_galleries/fileapi/picasa_file_util.h" 46 #endif // defined(OS_WIN) || defined(OS_MACOSX) 47 48 #if defined(OS_MACOSX) 49 #include "chrome/browser/media_galleries/fileapi/iphoto_file_util.h" 50 #endif // defined(OS_MACOSX) 51 52 using fileapi::FileSystemContext; 53 using fileapi::FileSystemURL; 54 55 namespace { 56 57 const char kMediaGalleryMountPrefix[] = "media_galleries-"; 58 59 void OnPreferencesInit( 60 const content::RenderViewHost* rvh, 61 const extensions::Extension* extension, 62 MediaGalleryPrefId pref_id, 63 const base::Callback<void(base::File::Error result)>& callback) { 64 MediaFileSystemRegistry* registry = 65 g_browser_process->media_file_system_registry(); 66 registry->RegisterMediaFileSystemForExtension(rvh, extension, pref_id, 67 callback); 68 } 69 70 void AttemptAutoMountOnUIThread( 71 int32 process_id, 72 int32 routing_id, 73 const std::string& storage_domain, 74 const std::string& mount_point, 75 const base::Callback<void(base::File::Error result)>& callback) { 76 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 77 78 content::RenderViewHost* rvh = 79 content::RenderViewHost::FromID(process_id, routing_id); 80 if (rvh) { 81 Profile* profile = 82 Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext()); 83 84 ExtensionService* extension_service = 85 extensions::ExtensionSystem::Get(profile)->extension_service(); 86 const extensions::Extension* extension = 87 extension_service->GetExtensionById(storage_domain, 88 false /*include disabled*/); 89 std::string expected_mount_prefix = 90 MediaFileSystemBackend::ConstructMountName( 91 profile->GetPath(), storage_domain, kInvalidMediaGalleryPrefId); 92 MediaGalleryPrefId pref_id = kInvalidMediaGalleryPrefId; 93 if (extension && 94 extension->id() == storage_domain && 95 StartsWithASCII(mount_point, expected_mount_prefix, true) && 96 base::StringToUint64(mount_point.substr(expected_mount_prefix.size()), 97 &pref_id) && 98 pref_id != kInvalidMediaGalleryPrefId) { 99 MediaGalleriesPreferences* preferences = 100 g_browser_process->media_file_system_registry()->GetPreferences( 101 profile); 102 preferences->EnsureInitialized( 103 base::Bind(&OnPreferencesInit, rvh, extension, pref_id, callback)); 104 return; 105 } 106 } 107 108 content::BrowserThread::PostTask( 109 content::BrowserThread::IO, 110 FROM_HERE, 111 base::Bind(callback, base::File::FILE_ERROR_NOT_FOUND)); 112 } 113 114 } // namespace 115 116 const char MediaFileSystemBackend::kMediaTaskRunnerName[] = 117 "media-task-runner"; 118 119 MediaFileSystemBackend::MediaFileSystemBackend( 120 const base::FilePath& profile_path, 121 base::SequencedTaskRunner* media_task_runner) 122 : profile_path_(profile_path), 123 media_task_runner_(media_task_runner), 124 media_path_filter_(new MediaPathFilter), 125 media_copy_or_move_file_validator_factory_(new MediaFileValidatorFactory), 126 native_media_file_util_( 127 new NativeMediaFileUtil(media_path_filter_.get())), 128 device_media_async_file_util_( 129 DeviceMediaAsyncFileUtil::Create(profile_path_, 130 APPLY_MEDIA_FILE_VALIDATION)) 131 #if defined(OS_WIN) || defined(OS_MACOSX) 132 , 133 picasa_file_util_(new picasa::PicasaFileUtil(media_path_filter_.get())), 134 itunes_file_util_(new itunes::ITunesFileUtil(media_path_filter_.get())) 135 #endif // defined(OS_WIN) || defined(OS_MACOSX) 136 #if defined(OS_MACOSX) 137 , 138 iphoto_file_util_(new iphoto::IPhotoFileUtil(media_path_filter_.get())) 139 #endif // defined(OS_MACOSX) 140 { 141 } 142 143 MediaFileSystemBackend::~MediaFileSystemBackend() { 144 } 145 146 // static 147 bool MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread() { 148 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 149 base::SequencedWorkerPool::SequenceToken media_sequence_token = 150 pool->GetNamedSequenceToken(kMediaTaskRunnerName); 151 return pool->IsRunningSequenceOnCurrentThread(media_sequence_token); 152 } 153 154 // static 155 scoped_refptr<base::SequencedTaskRunner> 156 MediaFileSystemBackend::MediaTaskRunner() { 157 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 158 base::SequencedWorkerPool::SequenceToken media_sequence_token = 159 pool->GetNamedSequenceToken(kMediaTaskRunnerName); 160 return pool->GetSequencedTaskRunner(media_sequence_token); 161 } 162 163 // static 164 std::string MediaFileSystemBackend::ConstructMountName( 165 const base::FilePath& profile_path, 166 const std::string& extension_id, 167 MediaGalleryPrefId pref_id) { 168 std::string name(kMediaGalleryMountPrefix); 169 name.append(profile_path.BaseName().MaybeAsASCII()); 170 name.append("-"); 171 name.append(extension_id); 172 name.append("-"); 173 if (pref_id != kInvalidMediaGalleryPrefId) 174 name.append(base::Uint64ToString(pref_id)); 175 base::ReplaceChars(name, " /", "_", &name); 176 return name; 177 } 178 179 // static 180 bool MediaFileSystemBackend::AttemptAutoMountForURLRequest( 181 const net::URLRequest* url_request, 182 const fileapi::FileSystemURL& filesystem_url, 183 const std::string& storage_domain, 184 const base::Callback<void(base::File::Error result)>& callback) { 185 if (storage_domain.empty() || 186 filesystem_url.type() != fileapi::kFileSystemTypeExternal || 187 storage_domain != filesystem_url.origin().host()) { 188 return false; 189 } 190 191 const base::FilePath& virtual_path = filesystem_url.path(); 192 if (virtual_path.ReferencesParent()) 193 return false; 194 std::vector<base::FilePath::StringType> components; 195 virtual_path.GetComponents(&components); 196 if (components.empty()) 197 return false; 198 std::string mount_point = base::FilePath(components[0]).AsUTF8Unsafe(); 199 if (!StartsWithASCII(mount_point, kMediaGalleryMountPrefix, true)) 200 return false; 201 202 const content::ResourceRequestInfo* request_info = 203 content::ResourceRequestInfo::ForRequest(url_request); 204 if (!request_info) 205 return false; 206 207 content::BrowserThread::PostTask( 208 content::BrowserThread::UI, 209 FROM_HERE, 210 base::Bind(&AttemptAutoMountOnUIThread, request_info->GetChildID(), 211 request_info->GetRouteID(), storage_domain, mount_point, 212 callback)); 213 return true; 214 } 215 216 bool MediaFileSystemBackend::CanHandleType( 217 fileapi::FileSystemType type) const { 218 switch (type) { 219 case fileapi::kFileSystemTypeNativeMedia: 220 case fileapi::kFileSystemTypeDeviceMedia: 221 #if defined(OS_WIN) || defined(OS_MACOSX) 222 case fileapi::kFileSystemTypePicasa: 223 case fileapi::kFileSystemTypeItunes: 224 #endif // defined(OS_WIN) || defined(OS_MACOSX) 225 #if defined(OS_MACOSX) 226 case fileapi::kFileSystemTypeIphoto: 227 #endif // defined(OS_MACOSX) 228 return true; 229 default: 230 return false; 231 } 232 } 233 234 void MediaFileSystemBackend::Initialize(fileapi::FileSystemContext* context) { 235 } 236 237 void MediaFileSystemBackend::ResolveURL( 238 const FileSystemURL& url, 239 fileapi::OpenFileSystemMode mode, 240 const OpenFileSystemCallback& callback) { 241 // We never allow opening a new FileSystem via usual ResolveURL. 242 base::MessageLoopProxy::current()->PostTask( 243 FROM_HERE, 244 base::Bind(callback, 245 GURL(), 246 std::string(), 247 base::File::FILE_ERROR_SECURITY)); 248 } 249 250 fileapi::AsyncFileUtil* MediaFileSystemBackend::GetAsyncFileUtil( 251 fileapi::FileSystemType type) { 252 switch (type) { 253 case fileapi::kFileSystemTypeNativeMedia: 254 return native_media_file_util_.get(); 255 case fileapi::kFileSystemTypeDeviceMedia: 256 return device_media_async_file_util_.get(); 257 #if defined(OS_WIN) || defined(OS_MACOSX) 258 case fileapi::kFileSystemTypeItunes: 259 return itunes_file_util_.get(); 260 case fileapi::kFileSystemTypePicasa: 261 return picasa_file_util_.get(); 262 #endif // defined(OS_WIN) || defined(OS_MACOSX) 263 #if defined(OS_MACOSX) 264 case fileapi::kFileSystemTypeIphoto: 265 return iphoto_file_util_.get(); 266 #endif // defined(OS_MACOSX) 267 default: 268 NOTREACHED(); 269 } 270 return NULL; 271 } 272 273 fileapi::CopyOrMoveFileValidatorFactory* 274 MediaFileSystemBackend::GetCopyOrMoveFileValidatorFactory( 275 fileapi::FileSystemType type, base::File::Error* error_code) { 276 DCHECK(error_code); 277 *error_code = base::File::FILE_OK; 278 switch (type) { 279 case fileapi::kFileSystemTypeNativeMedia: 280 case fileapi::kFileSystemTypeDeviceMedia: 281 case fileapi::kFileSystemTypeIphoto: 282 case fileapi::kFileSystemTypeItunes: 283 if (!media_copy_or_move_file_validator_factory_) { 284 *error_code = base::File::FILE_ERROR_SECURITY; 285 return NULL; 286 } 287 return media_copy_or_move_file_validator_factory_.get(); 288 default: 289 NOTREACHED(); 290 } 291 return NULL; 292 } 293 294 fileapi::FileSystemOperation* 295 MediaFileSystemBackend::CreateFileSystemOperation( 296 const FileSystemURL& url, 297 FileSystemContext* context, 298 base::File::Error* error_code) const { 299 scoped_ptr<fileapi::FileSystemOperationContext> operation_context( 300 new fileapi::FileSystemOperationContext( 301 context, media_task_runner_.get())); 302 return fileapi::FileSystemOperation::Create( 303 url, context, operation_context.Pass()); 304 } 305 306 bool MediaFileSystemBackend::SupportsStreaming( 307 const fileapi::FileSystemURL& url) const { 308 if (url.type() == fileapi::kFileSystemTypeDeviceMedia) { 309 DCHECK(device_media_async_file_util_); 310 return device_media_async_file_util_->SupportsStreaming(url); 311 } 312 313 return false; 314 } 315 316 scoped_ptr<webkit_blob::FileStreamReader> 317 MediaFileSystemBackend::CreateFileStreamReader( 318 const FileSystemURL& url, 319 int64 offset, 320 const base::Time& expected_modification_time, 321 FileSystemContext* context) const { 322 if (url.type() == fileapi::kFileSystemTypeDeviceMedia) { 323 DCHECK(device_media_async_file_util_); 324 scoped_ptr<webkit_blob::FileStreamReader> reader = 325 device_media_async_file_util_->GetFileStreamReader( 326 url, offset, expected_modification_time, context); 327 DCHECK(reader); 328 return reader.Pass(); 329 } 330 331 return scoped_ptr<webkit_blob::FileStreamReader>( 332 webkit_blob::FileStreamReader::CreateForLocalFile( 333 context->default_file_task_runner(), 334 url.path(), offset, expected_modification_time)); 335 } 336 337 scoped_ptr<fileapi::FileStreamWriter> 338 MediaFileSystemBackend::CreateFileStreamWriter( 339 const FileSystemURL& url, 340 int64 offset, 341 FileSystemContext* context) const { 342 return scoped_ptr<fileapi::FileStreamWriter>( 343 fileapi::FileStreamWriter::CreateForLocalFile( 344 context->default_file_task_runner(), 345 url.path(), 346 offset, 347 fileapi::FileStreamWriter::OPEN_EXISTING_FILE)); 348 } 349 350 fileapi::FileSystemQuotaUtil* 351 MediaFileSystemBackend::GetQuotaUtil() { 352 // No quota support. 353 return NULL; 354 } 355