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