| // Copyright (c) 2012 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. |
| |
| // Implements the Chrome Extensions Media Galleries API. |
| |
| #include "chrome/browser/extensions/api/media_galleries/media_galleries_api.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/callback.h" |
| #include "base/lazy_instance.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/extensions/api/file_system/file_system_api.h" |
| #include "chrome/browser/extensions/blob_reader.h" |
| #include "chrome/browser/extensions/chrome_extension_function_details.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/media_galleries/fileapi/safe_media_metadata_parser.h" |
| #include "chrome/browser/media_galleries/gallery_watch_manager.h" |
| #include "chrome/browser/media_galleries/media_file_system_registry.h" |
| #include "chrome/browser/media_galleries/media_galleries_histograms.h" |
| #include "chrome/browser/media_galleries/media_galleries_permission_controller.h" |
| #include "chrome/browser/media_galleries/media_galleries_preferences.h" |
| #include "chrome/browser/platform_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/chrome_select_file_policy.h" |
| #include "chrome/common/extensions/api/media_galleries.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/storage_monitor/storage_info.h" |
| #include "content/public/browser/blob_handle.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/child_process_security_policy.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "extensions/browser/blob_holder.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/permissions/api_permission.h" |
| #include "extensions/common/permissions/media_galleries_permission.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| #include "net/base/mime_sniffer.h" |
| #include "storage/browser/blob/blob_data_handle.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| using content::WebContents; |
| using storage_monitor::MediaStorageUtil; |
| using storage_monitor::StorageInfo; |
| |
| namespace extensions { |
| |
| namespace MediaGalleries = api::media_galleries; |
| namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems; |
| namespace AddGalleryWatch = MediaGalleries::AddGalleryWatch; |
| namespace RemoveGalleryWatch = MediaGalleries::RemoveGalleryWatch; |
| |
| namespace { |
| |
| const char kDisallowedByPolicy[] = |
| "Media Galleries API is disallowed by policy: "; |
| const char kInvalidGalleryIdMsg[] = "Invalid gallery id."; |
| const char kMissingEventListener[] = "Missing event listener registration."; |
| |
| const char kDeviceIdKey[] = "deviceId"; |
| const char kGalleryIdKey[] = "galleryId"; |
| const char kIsAvailableKey[] = "isAvailable"; |
| const char kIsMediaDeviceKey[] = "isMediaDevice"; |
| const char kIsRemovableKey[] = "isRemovable"; |
| const char kNameKey[] = "name"; |
| |
| const char kMetadataKey[] = "metadata"; |
| const char kAttachedImagesBlobInfoKey[] = "attachedImagesBlobInfo"; |
| const char kBlobUUIDKey[] = "blobUUID"; |
| const char kTypeKey[] = "type"; |
| const char kSizeKey[] = "size"; |
| |
| const char kInvalidGalleryId[] = "-1"; |
| |
| MediaFileSystemRegistry* media_file_system_registry() { |
| return g_browser_process->media_file_system_registry(); |
| } |
| |
| GalleryWatchManager* gallery_watch_manager() { |
| return media_file_system_registry()->gallery_watch_manager(); |
| } |
| |
| // Checks whether the MediaGalleries API is currently accessible (it may be |
| // disallowed even if an extension has the requisite permission). Then |
| // initializes the MediaGalleriesPreferences |
| bool Setup(Profile* profile, std::string* error, base::Closure callback) { |
| if (!ChromeSelectFilePolicy::FileSelectDialogsAllowed()) { |
| *error = std::string(kDisallowedByPolicy) + |
| prefs::kAllowFileSelectionDialogs; |
| return false; |
| } |
| |
| MediaGalleriesPreferences* preferences = |
| media_file_system_registry()->GetPreferences(profile); |
| preferences->EnsureInitialized(callback); |
| return true; |
| } |
| |
| // Returns true and sets |gallery_file_path| and |gallery_pref_id| if the |
| // |gallery_id| is valid and returns false otherwise. |
| bool GetGalleryFilePathAndId(const std::string& gallery_id, |
| Profile* profile, |
| const Extension* extension, |
| base::FilePath* gallery_file_path, |
| MediaGalleryPrefId* gallery_pref_id) { |
| MediaGalleryPrefId pref_id; |
| if (!base::StringToUint64(gallery_id, &pref_id)) |
| return false; |
| MediaGalleriesPreferences* preferences = |
| g_browser_process->media_file_system_registry()->GetPreferences(profile); |
| base::FilePath file_path( |
| preferences->LookUpGalleryPathForExtension(pref_id, extension, false)); |
| if (file_path.empty()) |
| return false; |
| *gallery_pref_id = pref_id; |
| *gallery_file_path = file_path; |
| return true; |
| } |
| |
| base::ListValue* ConstructFileSystemList( |
| content::RenderFrameHost* rfh, |
| const Extension* extension, |
| const std::vector<MediaFileSystemInfo>& filesystems) { |
| if (!rfh) |
| return NULL; |
| |
| MediaGalleriesPermission::CheckParam read_param( |
| MediaGalleriesPermission::kReadPermission); |
| const PermissionsData* permissions_data = extension->permissions_data(); |
| bool has_read_permission = permissions_data->CheckAPIPermissionWithParam( |
| APIPermission::kMediaGalleries, &read_param); |
| MediaGalleriesPermission::CheckParam copy_to_param( |
| MediaGalleriesPermission::kCopyToPermission); |
| bool has_copy_to_permission = permissions_data->CheckAPIPermissionWithParam( |
| APIPermission::kMediaGalleries, ©_to_param); |
| MediaGalleriesPermission::CheckParam delete_param( |
| MediaGalleriesPermission::kDeletePermission); |
| bool has_delete_permission = permissions_data->CheckAPIPermissionWithParam( |
| APIPermission::kMediaGalleries, &delete_param); |
| |
| const int child_id = rfh->GetProcess()->GetID(); |
| std::unique_ptr<base::ListValue> list(new base::ListValue()); |
| for (size_t i = 0; i < filesystems.size(); ++i) { |
| std::unique_ptr<base::DictionaryValue> file_system_dict_value( |
| new base::DictionaryValue()); |
| |
| // Send the file system id so the renderer can create a valid FileSystem |
| // object. |
| file_system_dict_value->SetStringWithoutPathExpansion( |
| "fsid", filesystems[i].fsid); |
| |
| file_system_dict_value->SetStringWithoutPathExpansion( |
| kNameKey, filesystems[i].name); |
| file_system_dict_value->SetStringWithoutPathExpansion( |
| kGalleryIdKey, |
| base::Uint64ToString(filesystems[i].pref_id)); |
| if (!filesystems[i].transient_device_id.empty()) { |
| file_system_dict_value->SetStringWithoutPathExpansion( |
| kDeviceIdKey, filesystems[i].transient_device_id); |
| } |
| file_system_dict_value->SetBooleanWithoutPathExpansion( |
| kIsRemovableKey, filesystems[i].removable); |
| file_system_dict_value->SetBooleanWithoutPathExpansion( |
| kIsMediaDeviceKey, filesystems[i].media_device); |
| file_system_dict_value->SetBooleanWithoutPathExpansion( |
| kIsAvailableKey, true); |
| |
| list->Append(std::move(file_system_dict_value)); |
| |
| if (filesystems[i].path.empty()) |
| continue; |
| |
| if (has_read_permission) { |
| content::ChildProcessSecurityPolicy* policy = |
| content::ChildProcessSecurityPolicy::GetInstance(); |
| policy->GrantReadFile(child_id, filesystems[i].path); |
| if (has_delete_permission) { |
| policy->GrantDeleteFrom(child_id, filesystems[i].path); |
| if (has_copy_to_permission) { |
| policy->GrantCopyInto(child_id, filesystems[i].path); |
| } |
| } |
| } |
| } |
| |
| return list.release(); |
| } |
| |
| class SelectDirectoryDialog : public ui::SelectFileDialog::Listener, |
| public base::RefCounted<SelectDirectoryDialog> { |
| public: |
| // Selected file path, or an empty path if the user canceled. |
| typedef base::Callback<void(const base::FilePath&)> Callback; |
| |
| SelectDirectoryDialog(WebContents* web_contents, const Callback& callback) |
| : web_contents_(web_contents), |
| callback_(callback) { |
| select_file_dialog_ = ui::SelectFileDialog::Create( |
| this, new ChromeSelectFilePolicy(web_contents)); |
| } |
| |
| void Show(const base::FilePath& default_path) { |
| AddRef(); // Balanced in the two reachable listener outcomes. |
| select_file_dialog_->SelectFile( |
| ui::SelectFileDialog::SELECT_FOLDER, |
| l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY_TITLE), |
| default_path, |
| NULL, |
| 0, |
| base::FilePath::StringType(), |
| platform_util::GetTopLevel(web_contents_->GetNativeView()), |
| NULL); |
| } |
| |
| // ui::SelectFileDialog::Listener implementation. |
| void FileSelected(const base::FilePath& path, |
| int index, |
| void* params) override { |
| callback_.Run(path); |
| Release(); // Balanced in Show(). |
| } |
| |
| void MultiFilesSelected(const std::vector<base::FilePath>& files, |
| void* params) override { |
| NOTREACHED() << "Should not be able to select multiple files"; |
| } |
| |
| void FileSelectionCanceled(void* params) override { |
| callback_.Run(base::FilePath()); |
| Release(); // Balanced in Show(). |
| } |
| |
| private: |
| friend class base::RefCounted<SelectDirectoryDialog>; |
| ~SelectDirectoryDialog() override {} |
| |
| scoped_refptr<ui::SelectFileDialog> select_file_dialog_; |
| WebContents* web_contents_; |
| Callback callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SelectDirectoryDialog); |
| }; |
| |
| } // namespace |
| |
| MediaGalleriesEventRouter::MediaGalleriesEventRouter( |
| content::BrowserContext* context) |
| : profile_(Profile::FromBrowserContext(context)), weak_ptr_factory_(this) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| DCHECK(profile_); |
| |
| EventRouter::Get(profile_)->RegisterObserver( |
| this, MediaGalleries::OnGalleryChanged::kEventName); |
| |
| gallery_watch_manager()->AddObserver(profile_, this); |
| } |
| |
| MediaGalleriesEventRouter::~MediaGalleriesEventRouter() { |
| } |
| |
| void MediaGalleriesEventRouter::Shutdown() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| |
| EventRouter::Get(profile_)->UnregisterObserver(this); |
| |
| gallery_watch_manager()->RemoveObserver(profile_); |
| } |
| |
| static base::LazyInstance< |
| BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter> > g_factory = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| // static |
| BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>* |
| MediaGalleriesEventRouter::GetFactoryInstance() { |
| return g_factory.Pointer(); |
| } |
| |
| // static |
| MediaGalleriesEventRouter* MediaGalleriesEventRouter::Get( |
| content::BrowserContext* context) { |
| DCHECK(media_file_system_registry() |
| ->GetPreferences(Profile::FromBrowserContext(context)) |
| ->IsInitialized()); |
| return BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>::Get(context); |
| } |
| |
| bool MediaGalleriesEventRouter::ExtensionHasGalleryChangeListener( |
| const std::string& extension_id) const { |
| return EventRouter::Get(profile_)->ExtensionHasEventListener( |
| extension_id, MediaGalleries::OnGalleryChanged::kEventName); |
| } |
| |
| void MediaGalleriesEventRouter::DispatchEventToExtension( |
| const std::string& extension_id, |
| events::HistogramValue histogram_value, |
| const std::string& event_name, |
| std::unique_ptr<base::ListValue> event_args) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| EventRouter* router = EventRouter::Get(profile_); |
| if (!router->ExtensionHasEventListener(extension_id, event_name)) |
| return; |
| |
| std::unique_ptr<extensions::Event> event(new extensions::Event( |
| histogram_value, event_name, std::move(event_args))); |
| router->DispatchEventToExtension(extension_id, std::move(event)); |
| } |
| |
| void MediaGalleriesEventRouter::OnGalleryChanged( |
| const std::string& extension_id, MediaGalleryPrefId gallery_id) { |
| MediaGalleries::GalleryChangeDetails details; |
| details.type = MediaGalleries::GALLERY_CHANGE_TYPE_CONTENTS_CHANGED; |
| details.gallery_id = base::Uint64ToString(gallery_id); |
| DispatchEventToExtension(extension_id, |
| events::MEDIA_GALLERIES_ON_GALLERY_CHANGED, |
| MediaGalleries::OnGalleryChanged::kEventName, |
| MediaGalleries::OnGalleryChanged::Create(details)); |
| } |
| |
| void MediaGalleriesEventRouter::OnGalleryWatchDropped( |
| const std::string& extension_id, MediaGalleryPrefId gallery_id) { |
| MediaGalleries::GalleryChangeDetails details; |
| details.type = MediaGalleries::GALLERY_CHANGE_TYPE_WATCH_DROPPED; |
| details.gallery_id = gallery_id; |
| DispatchEventToExtension(extension_id, |
| events::MEDIA_GALLERIES_ON_GALLERY_CHANGED, |
| MediaGalleries::OnGalleryChanged::kEventName, |
| MediaGalleries::OnGalleryChanged::Create(details)); |
| } |
| |
| void MediaGalleriesEventRouter::OnListenerRemoved( |
| const EventListenerInfo& details) { |
| if (details.event_name == MediaGalleries::OnGalleryChanged::kEventName && |
| !ExtensionHasGalleryChangeListener(details.extension_id)) { |
| gallery_watch_manager()->RemoveAllWatches(profile_, details.extension_id); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // MediaGalleriesGetMediaFileSystemsFunction // |
| /////////////////////////////////////////////////////////////////////////////// |
| MediaGalleriesGetMediaFileSystemsFunction:: |
| ~MediaGalleriesGetMediaFileSystemsFunction() {} |
| |
| bool MediaGalleriesGetMediaFileSystemsFunction::RunAsync() { |
| media_galleries::UsageCount(media_galleries::GET_MEDIA_FILE_SYSTEMS); |
| std::unique_ptr<GetMediaFileSystems::Params> params( |
| GetMediaFileSystems::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| MediaGalleries::GetMediaFileSystemsInteractivity interactive = |
| MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO; |
| if (params->details.get() && params->details->interactive != MediaGalleries:: |
| GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE) { |
| interactive = params->details->interactive; |
| } |
| |
| return Setup(GetProfile(), &error_, base::Bind( |
| &MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit, this, |
| interactive)); |
| } |
| |
| void MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit( |
| MediaGalleries::GetMediaFileSystemsInteractivity interactive) { |
| switch (interactive) { |
| case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_YES: { |
| // The MediaFileSystemRegistry only updates preferences for extensions |
| // that it knows are in use. Since this may be the first call to |
| // chrome.getMediaFileSystems for this extension, call |
| // GetMediaFileSystemsForExtension() here solely so that |
| // MediaFileSystemRegistry will send preference changes. |
| GetMediaFileSystemsForExtension(base::Bind( |
| &MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog, this)); |
| return; |
| } |
| case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_IF_NEEDED: { |
| GetMediaFileSystemsForExtension(base::Bind( |
| &MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries, |
| this)); |
| return; |
| } |
| case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO: |
| GetAndReturnGalleries(); |
| return; |
| case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE: |
| NOTREACHED(); |
| } |
| SendResponse(false); |
| } |
| |
| void MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog( |
| const std::vector<MediaFileSystemInfo>& /*filesystems*/) { |
| ShowDialog(); |
| } |
| |
| void MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries( |
| const std::vector<MediaFileSystemInfo>& filesystems) { |
| if (filesystems.empty()) |
| ShowDialog(); |
| else |
| ReturnGalleries(filesystems); |
| } |
| |
| void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() { |
| GetMediaFileSystemsForExtension(base::Bind( |
| &MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries, this)); |
| } |
| |
| void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries( |
| const std::vector<MediaFileSystemInfo>& filesystems) { |
| std::unique_ptr<base::ListValue> list( |
| ConstructFileSystemList(render_frame_host(), extension(), filesystems)); |
| if (!list.get()) { |
| SendResponse(false); |
| return; |
| } |
| |
| // The custom JS binding will use this list to create DOMFileSystem objects. |
| SetResult(std::move(list)); |
| SendResponse(true); |
| } |
| |
| void MediaGalleriesGetMediaFileSystemsFunction::ShowDialog() { |
| media_galleries::UsageCount(media_galleries::SHOW_DIALOG); |
| WebContents* contents = |
| ChromeExtensionFunctionDetails(this).GetOriginWebContents(); |
| if (!contents) { |
| SendResponse(false); |
| return; |
| } |
| |
| // Controller will delete itself. |
| base::Closure cb = base::Bind( |
| &MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries, this); |
| new MediaGalleriesPermissionController(contents, *extension(), cb); |
| } |
| |
| void MediaGalleriesGetMediaFileSystemsFunction::GetMediaFileSystemsForExtension( |
| const MediaFileSystemsCallback& cb) { |
| if (!render_frame_host()) { |
| cb.Run(std::vector<MediaFileSystemInfo>()); |
| return; |
| } |
| MediaFileSystemRegistry* registry = media_file_system_registry(); |
| DCHECK(registry->GetPreferences(GetProfile())->IsInitialized()); |
| registry->GetMediaFileSystemsForExtension(GetSenderWebContents(), extension(), |
| cb); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // MediaGalleriesAddUserSelectedFolderFunction // |
| /////////////////////////////////////////////////////////////////////////////// |
| MediaGalleriesAddUserSelectedFolderFunction:: |
| ~MediaGalleriesAddUserSelectedFolderFunction() {} |
| |
| bool MediaGalleriesAddUserSelectedFolderFunction::RunAsync() { |
| media_galleries::UsageCount(media_galleries::ADD_USER_SELECTED_FOLDER); |
| return Setup(GetProfile(), &error_, base::Bind( |
| &MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit, this)); |
| } |
| |
| void MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit() { |
| Profile* profile = GetProfile(); |
| const std::string& app_id = extension()->id(); |
| WebContents* contents = |
| ChromeExtensionFunctionDetails(this).GetOriginWebContents(); |
| if (!contents) { |
| SendResponse(false); |
| return; |
| } |
| |
| if (!user_gesture()) { |
| OnDirectorySelected(base::FilePath()); |
| return; |
| } |
| |
| base::FilePath last_used_path = |
| extensions::file_system_api::GetLastChooseEntryDirectory( |
| extensions::ExtensionPrefs::Get(profile), app_id); |
| SelectDirectoryDialog::Callback callback = base::Bind( |
| &MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected, this); |
| scoped_refptr<SelectDirectoryDialog> select_directory_dialog = |
| new SelectDirectoryDialog(contents, callback); |
| select_directory_dialog->Show(last_used_path); |
| } |
| |
| void MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected( |
| const base::FilePath& selected_directory) { |
| if (selected_directory.empty()) { |
| // User cancelled case. |
| GetMediaFileSystemsForExtension(base::Bind( |
| &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId, |
| this, |
| kInvalidMediaGalleryPrefId)); |
| return; |
| } |
| |
| extensions::file_system_api::SetLastChooseEntryDirectory( |
| extensions::ExtensionPrefs::Get(GetProfile()), |
| extension()->id(), |
| selected_directory); |
| |
| MediaGalleriesPreferences* preferences = |
| media_file_system_registry()->GetPreferences(GetProfile()); |
| MediaGalleryPrefId pref_id = |
| preferences->AddGalleryByPath(selected_directory, |
| MediaGalleryPrefInfo::kUserAdded); |
| preferences->SetGalleryPermissionForExtension(*extension(), pref_id, true); |
| |
| GetMediaFileSystemsForExtension(base::Bind( |
| &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId, |
| this, |
| pref_id)); |
| } |
| |
| void MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId( |
| MediaGalleryPrefId pref_id, |
| const std::vector<MediaFileSystemInfo>& filesystems) { |
| std::unique_ptr<base::ListValue> list( |
| ConstructFileSystemList(render_frame_host(), extension(), filesystems)); |
| if (!list.get()) { |
| SendResponse(false); |
| return; |
| } |
| |
| int index = -1; |
| if (pref_id != kInvalidMediaGalleryPrefId) { |
| for (size_t i = 0; i < filesystems.size(); ++i) { |
| if (filesystems[i].pref_id == pref_id) { |
| index = i; |
| break; |
| } |
| } |
| } |
| std::unique_ptr<base::DictionaryValue> results(new base::DictionaryValue); |
| results->SetWithoutPathExpansion("mediaFileSystems", list.release()); |
| results->SetIntegerWithoutPathExpansion("selectedFileSystemIndex", index); |
| SetResult(std::move(results)); |
| SendResponse(true); |
| } |
| |
| void |
| MediaGalleriesAddUserSelectedFolderFunction::GetMediaFileSystemsForExtension( |
| const MediaFileSystemsCallback& cb) { |
| if (!render_frame_host()) { |
| cb.Run(std::vector<MediaFileSystemInfo>()); |
| return; |
| } |
| MediaFileSystemRegistry* registry = media_file_system_registry(); |
| DCHECK(registry->GetPreferences(GetProfile())->IsInitialized()); |
| registry->GetMediaFileSystemsForExtension(GetSenderWebContents(), extension(), |
| cb); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // MediaGalleriesGetMetadataFunction // |
| /////////////////////////////////////////////////////////////////////////////// |
| MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {} |
| |
| bool MediaGalleriesGetMetadataFunction::RunAsync() { |
| media_galleries::UsageCount(media_galleries::GET_METADATA); |
| std::string blob_uuid; |
| EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &blob_uuid)); |
| |
| const base::Value* options_value = NULL; |
| if (!args_->Get(1, &options_value)) |
| return false; |
| std::unique_ptr<MediaGalleries::MediaMetadataOptions> options = |
| MediaGalleries::MediaMetadataOptions::FromValue(*options_value); |
| if (!options) |
| return false; |
| |
| return Setup(GetProfile(), &error_, base::Bind( |
| &MediaGalleriesGetMetadataFunction::OnPreferencesInit, this, |
| options->metadata_type, blob_uuid)); |
| } |
| |
| void MediaGalleriesGetMetadataFunction::OnPreferencesInit( |
| MediaGalleries::GetMetadataType metadata_type, |
| const std::string& blob_uuid) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // BlobReader is self-deleting. |
| BlobReader* reader = new BlobReader( |
| GetProfile(), |
| blob_uuid, |
| base::Bind(&MediaGalleriesGetMetadataFunction::GetMetadata, this, |
| metadata_type, blob_uuid)); |
| reader->SetByteRange(0, net::kMaxBytesToSniff); |
| reader->Start(); |
| } |
| |
| void MediaGalleriesGetMetadataFunction::GetMetadata( |
| MediaGalleries::GetMetadataType metadata_type, |
| const std::string& blob_uuid, |
| std::unique_ptr<std::string> blob_header, |
| int64_t total_blob_length) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| std::string mime_type; |
| bool mime_type_sniffed = net::SniffMimeTypeFromLocalData( |
| blob_header->c_str(), blob_header->size(), &mime_type); |
| |
| if (!mime_type_sniffed) { |
| SendResponse(false); |
| return; |
| } |
| |
| if (metadata_type == MediaGalleries::GET_METADATA_TYPE_MIMETYPEONLY) { |
| MediaGalleries::MediaMetadata metadata; |
| metadata.mime_type = mime_type; |
| |
| std::unique_ptr<base::DictionaryValue> result_dictionary( |
| new base::DictionaryValue); |
| result_dictionary->Set(kMetadataKey, metadata.ToValue().release()); |
| SetResult(std::move(result_dictionary)); |
| SendResponse(true); |
| return; |
| } |
| |
| // We get attached images by default. GET_METADATA_TYPE_NONE is the default |
| // value if the caller doesn't specify the metadata type. |
| bool get_attached_images = |
| metadata_type == MediaGalleries::GET_METADATA_TYPE_ALL || |
| metadata_type == MediaGalleries::GET_METADATA_TYPE_NONE; |
| |
| scoped_refptr<metadata::SafeMediaMetadataParser> parser( |
| new metadata::SafeMediaMetadataParser(GetProfile(), blob_uuid, |
| total_blob_length, mime_type, |
| get_attached_images)); |
| parser->Start(base::Bind( |
| &MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone, this)); |
| } |
| |
| void MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone( |
| bool parse_success, |
| std::unique_ptr<base::DictionaryValue> metadata_dictionary, |
| std::unique_ptr<std::vector<metadata::AttachedImage>> attached_images) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| if (!parse_success) { |
| SendResponse(false); |
| return; |
| } |
| |
| DCHECK(metadata_dictionary.get()); |
| DCHECK(attached_images.get()); |
| |
| std::unique_ptr<base::DictionaryValue> result_dictionary( |
| new base::DictionaryValue); |
| result_dictionary->Set(kMetadataKey, metadata_dictionary.release()); |
| |
| if (attached_images->empty()) { |
| SetResult(std::move(result_dictionary)); |
| SendResponse(true); |
| return; |
| } |
| |
| result_dictionary->Set(kAttachedImagesBlobInfoKey, new base::ListValue); |
| metadata::AttachedImage* first_image = &attached_images->front(); |
| content::BrowserContext::CreateMemoryBackedBlob( |
| GetProfile(), first_image->data.c_str(), first_image->data.size(), |
| base::Bind(&MediaGalleriesGetMetadataFunction::ConstructNextBlob, this, |
| base::Passed(&result_dictionary), |
| base::Passed(&attached_images), |
| base::Passed(base::WrapUnique(new std::vector<std::string>)))); |
| } |
| |
| void MediaGalleriesGetMetadataFunction::ConstructNextBlob( |
| std::unique_ptr<base::DictionaryValue> result_dictionary, |
| std::unique_ptr<std::vector<metadata::AttachedImage>> attached_images, |
| std::unique_ptr<std::vector<std::string>> blob_uuids, |
| std::unique_ptr<content::BlobHandle> current_blob) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| DCHECK(result_dictionary.get()); |
| DCHECK(attached_images.get()); |
| DCHECK(blob_uuids.get()); |
| DCHECK(current_blob.get()); |
| |
| DCHECK(!attached_images->empty()); |
| DCHECK_LT(blob_uuids->size(), attached_images->size()); |
| |
| // For the newly constructed Blob, store its image's metadata and Blob UUID. |
| base::ListValue* attached_images_list = NULL; |
| result_dictionary->GetList(kAttachedImagesBlobInfoKey, &attached_images_list); |
| DCHECK(attached_images_list); |
| DCHECK_LT(attached_images_list->GetSize(), attached_images->size()); |
| |
| metadata::AttachedImage* current_image = |
| &(*attached_images)[blob_uuids->size()]; |
| std::unique_ptr<base::DictionaryValue> attached_image( |
| new base::DictionaryValue); |
| attached_image->Set(kBlobUUIDKey, new base::StringValue( |
| current_blob->GetUUID())); |
| attached_image->Set(kTypeKey, new base::StringValue( |
| current_image->type)); |
| attached_image->Set(kSizeKey, new base::FundamentalValue( |
| base::checked_cast<int>(current_image->data.size()))); |
| attached_images_list->Append(std::move(attached_image)); |
| |
| blob_uuids->push_back(current_blob->GetUUID()); |
| extensions::BlobHolder* holder = |
| extensions::BlobHolder::FromRenderProcessHost( |
| render_frame_host()->GetProcess()); |
| holder->HoldBlobReference(std::move(current_blob)); |
| |
| // Construct the next Blob if necessary. |
| if (blob_uuids->size() < attached_images->size()) { |
| metadata::AttachedImage* next_image = |
| &(*attached_images)[blob_uuids->size()]; |
| content::BrowserContext::CreateMemoryBackedBlob( |
| GetProfile(), |
| next_image->data.c_str(), |
| next_image->data.size(), |
| base::Bind(&MediaGalleriesGetMetadataFunction::ConstructNextBlob, |
| this, base::Passed(&result_dictionary), |
| base::Passed(&attached_images), base::Passed(&blob_uuids))); |
| return; |
| } |
| |
| // All Blobs have been constructed. The renderer will take ownership. |
| SetResult(std::move(result_dictionary)); |
| SetTransferredBlobUUIDs(*blob_uuids); |
| SendResponse(true); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // MediaGalleriesAddGalleryWatchFunction // |
| /////////////////////////////////////////////////////////////////////////////// |
| MediaGalleriesAddGalleryWatchFunction:: |
| ~MediaGalleriesAddGalleryWatchFunction() { |
| } |
| |
| bool MediaGalleriesAddGalleryWatchFunction::RunAsync() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| DCHECK(GetProfile()); |
| if (!render_frame_host() || !render_frame_host()->GetProcess()) |
| return false; |
| |
| std::unique_ptr<AddGalleryWatch::Params> params( |
| AddGalleryWatch::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| MediaGalleriesPreferences* preferences = |
| g_browser_process->media_file_system_registry()->GetPreferences( |
| GetProfile()); |
| preferences->EnsureInitialized( |
| base::Bind(&MediaGalleriesAddGalleryWatchFunction::OnPreferencesInit, |
| this, |
| params->gallery_id)); |
| |
| return true; |
| } |
| |
| void MediaGalleriesAddGalleryWatchFunction::OnPreferencesInit( |
| const std::string& pref_id) { |
| base::FilePath gallery_file_path; |
| MediaGalleryPrefId gallery_pref_id = kInvalidMediaGalleryPrefId; |
| if (!GetGalleryFilePathAndId(pref_id, |
| GetProfile(), |
| extension(), |
| &gallery_file_path, |
| &gallery_pref_id)) { |
| api::media_galleries::AddGalleryWatchResult result; |
| error_ = kInvalidGalleryIdMsg; |
| result.gallery_id = kInvalidGalleryId; |
| result.success = false; |
| SetResult(result.ToValue()); |
| SendResponse(false); |
| return; |
| } |
| |
| gallery_watch_manager()->AddWatch( |
| GetProfile(), |
| extension(), |
| gallery_pref_id, |
| base::Bind(&MediaGalleriesAddGalleryWatchFunction::HandleResponse, |
| this, |
| gallery_pref_id)); |
| } |
| |
| void MediaGalleriesAddGalleryWatchFunction::HandleResponse( |
| MediaGalleryPrefId gallery_id, |
| const std::string& error) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // If an app added a file watch without any event listeners on the |
| // onGalleryChanged event, that's an error. |
| MediaGalleriesEventRouter* api = MediaGalleriesEventRouter::Get(GetProfile()); |
| api::media_galleries::AddGalleryWatchResult result; |
| result.gallery_id = base::Uint64ToString(gallery_id); |
| |
| if (!api->ExtensionHasGalleryChangeListener(extension()->id())) { |
| result.success = false; |
| SetResult(result.ToValue()); |
| error_ = kMissingEventListener; |
| SendResponse(false); |
| return; |
| } |
| |
| result.success = error.empty(); |
| SetResult(result.ToValue()); |
| if (error.empty()) { |
| SendResponse(true); |
| } else { |
| error_ = error.c_str(); |
| SendResponse(false); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // MediaGalleriesRemoveGalleryWatchFunction // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| MediaGalleriesRemoveGalleryWatchFunction:: |
| ~MediaGalleriesRemoveGalleryWatchFunction() { |
| } |
| |
| bool MediaGalleriesRemoveGalleryWatchFunction::RunAsync() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (!render_frame_host() || !render_frame_host()->GetProcess()) |
| return false; |
| |
| std::unique_ptr<RemoveGalleryWatch::Params> params( |
| RemoveGalleryWatch::Params::Create(*args_)); |
| EXTENSION_FUNCTION_VALIDATE(params.get()); |
| |
| MediaGalleriesPreferences* preferences = |
| g_browser_process->media_file_system_registry()->GetPreferences( |
| GetProfile()); |
| preferences->EnsureInitialized( |
| base::Bind(&MediaGalleriesRemoveGalleryWatchFunction::OnPreferencesInit, |
| this, |
| params->gallery_id)); |
| return true; |
| } |
| |
| void MediaGalleriesRemoveGalleryWatchFunction::OnPreferencesInit( |
| const std::string& pref_id) { |
| base::FilePath gallery_file_path; |
| MediaGalleryPrefId gallery_pref_id = 0; |
| if (!GetGalleryFilePathAndId(pref_id, |
| GetProfile(), |
| extension(), |
| &gallery_file_path, |
| &gallery_pref_id)) { |
| error_ = kInvalidGalleryIdMsg; |
| SendResponse(false); |
| return; |
| } |
| |
| gallery_watch_manager()->RemoveWatch( |
| GetProfile(), extension_id(), gallery_pref_id); |
| SendResponse(true); |
| } |
| |
| } // namespace extensions |