| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/files/file_path.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task_scheduler/lazy_task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/media_galleries/fileapi/media_file_validator_factory.h" |
| #include "chrome/browser/media_galleries/fileapi/media_path_filter.h" |
| #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h" |
| #include "chrome/browser/media_galleries/media_file_system_registry.h" |
| #include "chrome/browser/media_galleries/media_galleries_histograms.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/resource_request_info.h" |
| #include "content/public/browser/web_contents.h" |
| #include "extensions/browser/extension_system.h" |
| #include "net/url_request/url_request.h" |
| #include "storage/browser/fileapi/copy_or_move_file_validator.h" |
| #include "storage/browser/fileapi/file_stream_reader.h" |
| #include "storage/browser/fileapi/file_stream_writer.h" |
| #include "storage/browser/fileapi/file_system_context.h" |
| #include "storage/browser/fileapi/file_system_operation.h" |
| #include "storage/browser/fileapi/file_system_operation_context.h" |
| #include "storage/browser/fileapi/file_system_url.h" |
| #include "storage/browser/fileapi/native_file_util.h" |
| #include "storage/common/fileapi/file_system_types.h" |
| #include "storage/common/fileapi/file_system_util.h" |
| |
| #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) |
| #include "chrome/browser/media_galleries/fileapi/device_media_async_file_util.h" |
| #endif |
| |
| using storage::FileSystemContext; |
| using storage::FileSystemURL; |
| |
| namespace { |
| |
| const char kMediaGalleryMountPrefix[] = "media_galleries-"; |
| |
| base::LazySequencedTaskRunner g_media_task_runner = |
| LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER( |
| base::TaskTraits(base::MayBlock(), |
| base::TaskPriority::USER_VISIBLE, |
| base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN)); |
| |
| void OnPreferencesInit( |
| const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter, |
| const extensions::Extension* extension, |
| MediaGalleryPrefId pref_id, |
| base::OnceCallback<void(base::File::Error result)> callback) { |
| content::WebContents* contents = web_contents_getter.Run(); |
| if (!contents) { |
| content::BrowserThread::PostTask( |
| content::BrowserThread::IO, FROM_HERE, |
| base::BindOnce(std::move(callback), base::File::FILE_ERROR_FAILED)); |
| return; |
| } |
| MediaFileSystemRegistry* registry = |
| g_browser_process->media_file_system_registry(); |
| registry->RegisterMediaFileSystemForExtension(contents, extension, pref_id, |
| std::move(callback)); |
| } |
| |
| void AttemptAutoMountOnUIThread( |
| const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter, |
| const std::string& storage_domain, |
| const std::string& mount_point, |
| base::OnceCallback<void(base::File::Error result)> callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| content::WebContents* web_contents = web_contents_getter.Run(); |
| if (web_contents) { |
| Profile* profile = |
| Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| |
| ExtensionService* extension_service = |
| extensions::ExtensionSystem::Get(profile)->extension_service(); |
| const extensions::Extension* extension = |
| extension_service->GetExtensionById(storage_domain, |
| false /*include disabled*/); |
| std::string expected_mount_prefix = |
| MediaFileSystemBackend::ConstructMountName( |
| profile->GetPath(), storage_domain, kInvalidMediaGalleryPrefId); |
| MediaGalleryPrefId pref_id = kInvalidMediaGalleryPrefId; |
| if (extension && extension->id() == storage_domain && |
| base::StartsWith(mount_point, expected_mount_prefix, |
| base::CompareCase::SENSITIVE) && |
| base::StringToUint64(mount_point.substr(expected_mount_prefix.size()), |
| &pref_id) && |
| pref_id != kInvalidMediaGalleryPrefId) { |
| MediaGalleriesPreferences* preferences = |
| g_browser_process->media_file_system_registry()->GetPreferences( |
| profile); |
| // Pass the WebContentsGetter to the closure to prevent a use-after-free |
| // in the case that the web_contents is destroyed before the closure runs. |
| preferences->EnsureInitialized(base::Bind( |
| &OnPreferencesInit, web_contents_getter, base::RetainedRef(extension), |
| pref_id, base::Passed(&callback))); |
| return; |
| } |
| } |
| |
| content::BrowserThread::PostTask( |
| content::BrowserThread::IO, FROM_HERE, |
| base::BindOnce(std::move(callback), base::File::FILE_ERROR_NOT_FOUND)); |
| } |
| |
| content::WebContents* GetWebContentsFromFrameTreeNodeID( |
| int frame_tree_node_id) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| return content::WebContents::FromFrameTreeNodeId(frame_tree_node_id); |
| } |
| |
| } // namespace |
| |
| MediaFileSystemBackend::MediaFileSystemBackend( |
| const base::FilePath& profile_path) |
| : profile_path_(profile_path), |
| media_path_filter_(new MediaPathFilter), |
| media_copy_or_move_file_validator_factory_(new MediaFileValidatorFactory), |
| native_media_file_util_(new NativeMediaFileUtil(media_path_filter_.get())) |
| #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) |
| , |
| device_media_async_file_util_( |
| DeviceMediaAsyncFileUtil::Create(profile_path_, |
| APPLY_MEDIA_FILE_VALIDATION)) |
| #endif |
| { |
| } |
| |
| MediaFileSystemBackend::~MediaFileSystemBackend() { |
| } |
| |
| // static |
| void MediaFileSystemBackend::AssertCurrentlyOnMediaSequence() { |
| #if DCHECK_IS_ON() |
| DCHECK(g_media_task_runner.Get()->RunsTasksInCurrentSequence()); |
| #endif |
| } |
| |
| // static |
| scoped_refptr<base::SequencedTaskRunner> |
| MediaFileSystemBackend::MediaTaskRunner() { |
| return g_media_task_runner.Get(); |
| } |
| |
| // static |
| std::string MediaFileSystemBackend::ConstructMountName( |
| const base::FilePath& profile_path, |
| const std::string& extension_id, |
| MediaGalleryPrefId pref_id) { |
| std::string name(kMediaGalleryMountPrefix); |
| name.append(profile_path.BaseName().MaybeAsASCII()); |
| name.append("-"); |
| name.append(extension_id); |
| name.append("-"); |
| if (pref_id != kInvalidMediaGalleryPrefId) |
| name.append(base::NumberToString(pref_id)); |
| base::ReplaceChars(name, " /", "_", &name); |
| return name; |
| } |
| |
| // static |
| bool MediaFileSystemBackend::AttemptAutoMountForURLRequest( |
| const storage::FileSystemRequestInfo& request_info, |
| const storage::FileSystemURL& filesystem_url, |
| base::OnceCallback<void(base::File::Error result)> callback) { |
| if (request_info.storage_domain.empty() || |
| filesystem_url.type() != storage::kFileSystemTypeExternal || |
| request_info.storage_domain != filesystem_url.origin().host()) { |
| return false; |
| } |
| |
| const base::FilePath& virtual_path = filesystem_url.path(); |
| if (virtual_path.ReferencesParent()) |
| return false; |
| std::vector<base::FilePath::StringType> components; |
| virtual_path.GetComponents(&components); |
| if (components.empty()) |
| return false; |
| std::string mount_point = base::FilePath(components[0]).AsUTF8Unsafe(); |
| if (!base::StartsWith(mount_point, kMediaGalleryMountPrefix, |
| base::CompareCase::SENSITIVE)) |
| return false; |
| |
| content::ResourceRequestInfo::WebContentsGetter web_contents_getter; |
| if (request_info.content_id) { |
| web_contents_getter = base::BindRepeating( |
| &GetWebContentsFromFrameTreeNodeID, request_info.content_id); |
| } else { |
| const content::ResourceRequestInfo* resource_request_info = |
| content::ResourceRequestInfo::ForRequest(request_info.request); |
| if (!resource_request_info) |
| return false; |
| web_contents_getter = |
| resource_request_info->GetWebContentsGetterForRequest(); |
| } |
| |
| content::BrowserThread::PostTask( |
| content::BrowserThread::UI, FROM_HERE, |
| base::BindOnce(&AttemptAutoMountOnUIThread, web_contents_getter, |
| request_info.storage_domain, mount_point, |
| std::move(callback))); |
| return true; |
| } |
| |
| bool MediaFileSystemBackend::CanHandleType(storage::FileSystemType type) const { |
| switch (type) { |
| case storage::kFileSystemTypeNativeMedia: |
| case storage::kFileSystemTypeDeviceMedia: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| void MediaFileSystemBackend::Initialize(storage::FileSystemContext* context) { |
| } |
| |
| void MediaFileSystemBackend::ResolveURL(const FileSystemURL& url, |
| storage::OpenFileSystemMode mode, |
| OpenFileSystemCallback callback) { |
| // We never allow opening a new FileSystem via usual ResolveURL. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), GURL(), std::string(), |
| base::File::FILE_ERROR_SECURITY)); |
| } |
| |
| storage::AsyncFileUtil* MediaFileSystemBackend::GetAsyncFileUtil( |
| storage::FileSystemType type) { |
| // We count file system usages here, because we want to count (per session) |
| // when the file system is actually used for I/O, rather than merely present. |
| switch (type) { |
| case storage::kFileSystemTypeNativeMedia: |
| return native_media_file_util_.get(); |
| #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) |
| case storage::kFileSystemTypeDeviceMedia: |
| return device_media_async_file_util_.get(); |
| #endif |
| default: |
| NOTREACHED(); |
| } |
| return NULL; |
| } |
| |
| storage::WatcherManager* MediaFileSystemBackend::GetWatcherManager( |
| storage::FileSystemType type) { |
| return NULL; |
| } |
| |
| storage::CopyOrMoveFileValidatorFactory* |
| MediaFileSystemBackend::GetCopyOrMoveFileValidatorFactory( |
| storage::FileSystemType type, |
| base::File::Error* error_code) { |
| DCHECK(error_code); |
| *error_code = base::File::FILE_OK; |
| switch (type) { |
| case storage::kFileSystemTypeNativeMedia: |
| case storage::kFileSystemTypeDeviceMedia: |
| if (!media_copy_or_move_file_validator_factory_) { |
| *error_code = base::File::FILE_ERROR_SECURITY; |
| return NULL; |
| } |
| return media_copy_or_move_file_validator_factory_.get(); |
| default: |
| NOTREACHED(); |
| } |
| return NULL; |
| } |
| |
| storage::FileSystemOperation* MediaFileSystemBackend::CreateFileSystemOperation( |
| const FileSystemURL& url, |
| FileSystemContext* context, |
| base::File::Error* error_code) const { |
| std::unique_ptr<storage::FileSystemOperationContext> operation_context( |
| new storage::FileSystemOperationContext(context, |
| MediaTaskRunner().get())); |
| return storage::FileSystemOperation::Create(url, context, |
| std::move(operation_context)); |
| } |
| |
| bool MediaFileSystemBackend::SupportsStreaming( |
| const storage::FileSystemURL& url) const { |
| #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) |
| if (url.type() == storage::kFileSystemTypeDeviceMedia) |
| return device_media_async_file_util_->SupportsStreaming(url); |
| #endif |
| |
| return false; |
| } |
| |
| bool MediaFileSystemBackend::HasInplaceCopyImplementation( |
| storage::FileSystemType type) const { |
| DCHECK(type == storage::kFileSystemTypeNativeMedia || |
| type == storage::kFileSystemTypeDeviceMedia); |
| return true; |
| } |
| |
| std::unique_ptr<storage::FileStreamReader> |
| MediaFileSystemBackend::CreateFileStreamReader( |
| const FileSystemURL& url, |
| int64_t offset, |
| int64_t max_bytes_to_read, |
| const base::Time& expected_modification_time, |
| FileSystemContext* context) const { |
| #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) |
| if (url.type() == storage::kFileSystemTypeDeviceMedia) { |
| std::unique_ptr<storage::FileStreamReader> reader = |
| device_media_async_file_util_->GetFileStreamReader( |
| url, offset, expected_modification_time, context); |
| DCHECK(reader); |
| return reader; |
| } |
| #endif |
| |
| return std::unique_ptr<storage::FileStreamReader>( |
| storage::FileStreamReader::CreateForLocalFile( |
| context->default_file_task_runner(), url.path(), offset, |
| expected_modification_time)); |
| } |
| |
| std::unique_ptr<storage::FileStreamWriter> |
| MediaFileSystemBackend::CreateFileStreamWriter( |
| const FileSystemURL& url, |
| int64_t offset, |
| FileSystemContext* context) const { |
| return std::unique_ptr<storage::FileStreamWriter>( |
| storage::FileStreamWriter::CreateForLocalFile( |
| context->default_file_task_runner(), url.path(), offset, |
| storage::FileStreamWriter::OPEN_EXISTING_FILE)); |
| } |
| |
| storage::FileSystemQuotaUtil* MediaFileSystemBackend::GetQuotaUtil() { |
| // No quota support. |
| return NULL; |
| } |
| |
| const storage::UpdateObserverList* MediaFileSystemBackend::GetUpdateObservers( |
| storage::FileSystemType type) const { |
| return NULL; |
| } |
| |
| const storage::ChangeObserverList* MediaFileSystemBackend::GetChangeObservers( |
| storage::FileSystemType type) const { |
| return NULL; |
| } |
| |
| const storage::AccessObserverList* MediaFileSystemBackend::GetAccessObservers( |
| storage::FileSystemType type) const { |
| return NULL; |
| } |