blob: dd6ccd573ffb145101e39ccfc1b08cef7fa51a06 [file] [log] [blame]
// 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 "components/web_modal/web_contents_modal_dialog_manager.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/app_window/app_window.h"
#include "extensions/browser/app_window/app_window_registry.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, &copy_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);
};
// Returns a web contents to use as the source for a prompt showing to the user.
// The web contents has to support modal dialogs, so it can't be the app's
// background page.
content::WebContents* GetWebContentsForPrompt(
content::WebContents* sender_web_contents,
content::BrowserContext* browser_context,
const std::string& app_id) {
// Check if the sender web contents supports modal dialogs.
if (sender_web_contents &&
web_modal::WebContentsModalDialogManager::FromWebContents(
sender_web_contents)) {
return sender_web_contents;
}
// Otherwise, check for the current app window for the app (app windows
// support modal dialogs).
if (!app_id.empty()) {
AppWindow* window = AppWindowRegistry::Get(browser_context)
->GetCurrentAppWindowForApp(app_id);
if (window)
return window->web_contents();
}
return nullptr;
}
} // 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>>::DestructorAtExit
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 =
GetWebContentsForPrompt(GetSenderWebContents(),
browser_context(),
extension()->id());
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() {
const std::string& app_id = extension()->id();
WebContents* contents =
GetWebContentsForPrompt(GetSenderWebContents(),
browser_context(),
app_id);
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(browser_context()), 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", std::move(list));
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());
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, std::move(metadata_dictionary));
if (attached_images->empty()) {
SetResult(std::move(result_dictionary));
SendResponse(true);
return;
}
result_dictionary->Set(kAttachedImagesBlobInfoKey,
base::MakeUnique<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->SetString(kBlobUUIDKey, current_blob->GetUUID());
attached_image->SetString(kTypeKey, current_image->type);
attached_image->SetInteger(
kSizeKey, base::checked_cast<int>(current_image->data.size()));
attached_images_list->Append(std::move(attached_image));
blob_uuids->push_back(current_blob->GetUUID());
content::RenderProcessHost* render_process_host =
render_frame_host()->GetProcess();
if (!render_process_host) {
SendResponse(false);
return;
}
extensions::BlobHolder* holder =
extensions::BlobHolder::FromRenderProcessHost(render_process_host);
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