Home | History | Annotate | Download | only in fileapi
      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