Commit 0d283901 authored by thestig@chromium.org's avatar thestig@chromium.org

Media Galleries: Add chrome.mediaGalleries.addUserSelectedFolder().

BUG=318411

Review URL: https://codereview.chromium.org/93643002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@244128 0039d316-1c4b-4281-b951-d872f2087c98
parent 4d61441b
......@@ -18,11 +18,14 @@
#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/extension_prefs.h"
#include "chrome/browser/media_galleries/media_file_system_registry.h"
#include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
#include "chrome/browser/media_galleries/media_galleries_histograms.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/storage_monitor/storage_info.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
......@@ -35,10 +38,13 @@
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "extensions/common/extension.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
#include "grit/generated_resources.h"
#include "net/base/mime_sniffer.h"
#include "ui/base/l10n/l10n_util.h"
using content::WebContents;
using web_modal::WebContentsModalDialogManager;
......@@ -76,6 +82,146 @@ MediaFileSystemRegistry* media_file_system_registry() {
return g_browser_process->media_file_system_registry();
}
WebContents* GetWebContents(content::RenderViewHost* rvh,
Profile* profile,
const std::string& app_id) {
WebContents* contents = WebContents::FromRenderViewHost(rvh);
WebContentsModalDialogManager* web_contents_modal_dialog_manager =
WebContentsModalDialogManager::FromWebContents(contents);
if (!web_contents_modal_dialog_manager) {
// If there is no WebContentsModalDialogManager, then this contents is
// probably the background page for an app. Try to find a shell window to
// host the dialog.
apps::ShellWindow* window = apps::ShellWindowRegistry::Get(
profile)->GetCurrentShellWindowForApp(app_id);
contents = window ? window->web_contents() : NULL;
}
return contents;
}
base::ListValue* ConstructFileSystemList(
content::RenderViewHost* rvh,
const Extension* extension,
const std::vector<MediaFileSystemInfo>& filesystems) {
if (!rvh)
return NULL;
MediaGalleriesPermission::CheckParam read_param(
MediaGalleriesPermission::kReadPermission);
bool has_read_permission = PermissionsData::CheckAPIPermissionWithParam(
extension, APIPermission::kMediaGalleries, &read_param);
MediaGalleriesPermission::CheckParam copy_to_param(
MediaGalleriesPermission::kCopyToPermission);
bool has_copy_to_permission = PermissionsData::CheckAPIPermissionWithParam(
extension, APIPermission::kMediaGalleries, &copy_to_param);
MediaGalleriesPermission::CheckParam delete_param(
MediaGalleriesPermission::kDeletePermission);
bool has_delete_permission = PermissionsData::CheckAPIPermissionWithParam(
extension, APIPermission::kMediaGalleries, &delete_param);
const int child_id = rvh->GetProcess()->GetID();
scoped_ptr<base::ListValue> list(new base::ListValue());
for (size_t i = 0; i < filesystems.size(); ++i) {
scoped_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(file_system_dict_value.release());
if (filesystems[i].path.empty())
continue;
if (has_read_permission) {
content::ChildProcessSecurityPolicy* policy =
content::ChildProcessSecurityPolicy::GetInstance();
policy->GrantReadFileSystem(child_id, filesystems[i].fsid);
if (has_delete_permission) {
policy->GrantDeleteFromFileSystem(child_id, filesystems[i].fsid);
if (has_copy_to_permission) {
policy->GrantCopyIntoFileSystem(child_id, filesystems[i].fsid);
}
}
}
}
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_->GetView()->GetNativeView()),
NULL);
}
// ui::SelectFileDialog::Listener implementation.
virtual void FileSelected(const base::FilePath& path,
int index,
void* params) OVERRIDE {
callback_.Run(path);
Release(); // Balanced in Show().
}
virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
void* params) OVERRIDE {
NOTREACHED() << "Should not be able to select multiple files";
}
virtual void FileSelectionCanceled(void* params) OVERRIDE {
callback_.Run(base::FilePath());
Release(); // Balanced in Show().
}
private:
friend class base::RefCounted<SelectDirectoryDialog>;
virtual ~SelectDirectoryDialog() {}
scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
WebContents* web_contents_;
Callback callback_;
DISALLOW_COPY_AND_ASSIGN(SelectDirectoryDialog);
};
} // namespace
MediaGalleriesGetMediaFileSystemsFunction::
......@@ -153,96 +299,28 @@ void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() {
void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries(
const std::vector<MediaFileSystemInfo>& filesystems) {
content::RenderViewHost* rvh = render_view_host();
if (!rvh) {
scoped_ptr<base::ListValue> list(
ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
if (!list.get()) {
SendResponse(false);
return;
}
MediaGalleriesPermission::CheckParam read_param(
MediaGalleriesPermission::kReadPermission);
bool has_read_permission = PermissionsData::CheckAPIPermissionWithParam(
GetExtension(), APIPermission::kMediaGalleries, &read_param);
MediaGalleriesPermission::CheckParam copy_to_param(
MediaGalleriesPermission::kCopyToPermission);
bool has_copy_to_permission = PermissionsData::CheckAPIPermissionWithParam(
GetExtension(), APIPermission::kMediaGalleries, &copy_to_param);
MediaGalleriesPermission::CheckParam delete_param(
MediaGalleriesPermission::kDeletePermission);
bool has_delete_permission = PermissionsData::CheckAPIPermissionWithParam(
GetExtension(), APIPermission::kMediaGalleries, &delete_param);
const int child_id = rvh->GetProcess()->GetID();
base::ListValue* list = new base::ListValue();
for (size_t i = 0; i < filesystems.size(); i++) {
scoped_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(file_system_dict_value.release());
if (filesystems[i].path.empty())
continue;
if (has_read_permission) {
content::ChildProcessSecurityPolicy* policy =
content::ChildProcessSecurityPolicy::GetInstance();
policy->GrantReadFileSystem(child_id, filesystems[i].fsid);
if (has_delete_permission) {
policy->GrantDeleteFromFileSystem(child_id, filesystems[i].fsid);
if (has_copy_to_permission) {
policy->GrantCopyIntoFileSystem(child_id, filesystems[i].fsid);
}
}
}
}
// The custom JS binding will use this list to create DOMFileSystem objects.
SetResult(list);
SetResult(list.release());
SendResponse(true);
}
void MediaGalleriesGetMediaFileSystemsFunction::ShowDialog() {
media_galleries::UsageCount(media_galleries::SHOW_DIALOG);
WebContents* contents = WebContents::FromRenderViewHost(render_view_host());
WebContentsModalDialogManager* web_contents_modal_dialog_manager =
WebContentsModalDialogManager::FromWebContents(contents);
if (!web_contents_modal_dialog_manager) {
// If there is no WebContentsModalDialogManager, then this contents is
// probably the background page for an app. Try to find a shell window to
// host the dialog.
apps::ShellWindow* window = apps::ShellWindowRegistry::Get(
GetProfile())->GetCurrentShellWindowForApp(GetExtension()->id());
if (!window) {
SendResponse(false);
return;
}
contents = window->web_contents();
}
const Extension* extension = GetExtension();
WebContents* contents =
GetWebContents(render_view_host(), GetProfile(), extension->id());
// Controller will delete itself.
base::Closure cb = base::Bind(
&MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries, this);
new MediaGalleriesDialogController(contents, *GetExtension(), cb);
new MediaGalleriesDialogController(contents, *extension, cb);
}
void MediaGalleriesGetMediaFileSystemsFunction::GetMediaFileSystemsForExtension(
......@@ -325,6 +403,103 @@ void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries(
SendResponse(true);
}
MediaGalleriesAddUserSelectedFolderFunction::
~MediaGalleriesAddUserSelectedFolderFunction() {}
bool MediaGalleriesAddUserSelectedFolderFunction::RunImpl() {
if (!ApiIsAccessible(&error_))
return false;
media_galleries::UsageCount(media_galleries::ADD_USER_SELECTED_FOLDER);
MediaGalleriesPreferences* preferences =
media_file_system_registry()->GetPreferences(GetProfile());
preferences->EnsureInitialized(base::Bind(
&MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit,
this));
return true;
}
void MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit() {
Profile* profile = GetProfile();
const std::string& app_id = GetExtension()->id();
WebContents* contents = GetWebContents(render_view_host(), profile, app_id);
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()),
GetExtension()->id(),
selected_directory);
MediaGalleriesPreferences* preferences =
media_file_system_registry()->GetPreferences(GetProfile());
MediaGalleryPrefId pref_id =
preferences->AddGalleryByPath(selected_directory);
preferences->SetGalleryPermissionForExtension(*GetExtension(), pref_id, true);
GetMediaFileSystemsForExtension(base::Bind(
&MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
this,
pref_id));
}
void MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId(
MediaGalleryPrefId pref_id,
const std::vector<MediaFileSystemInfo>& filesystems) {
scoped_ptr<base::ListValue> list(
ConstructFileSystemList(render_view_host(), GetExtension(), 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;
}
}
}
base::DictionaryValue* results = new base::DictionaryValue;
results->SetWithoutPathExpansion("mediaFileSystems", list.release());
results->SetIntegerWithoutPathExpansion("selectedFileSystemIndex", index);
SetResult(results);
SendResponse(true);
}
void
MediaGalleriesAddUserSelectedFolderFunction::GetMediaFileSystemsForExtension(
const MediaFileSystemsCallback& cb) {
if (!render_view_host()) {
cb.Run(std::vector<MediaFileSystemInfo>());
return;
}
MediaFileSystemRegistry* registry = media_file_system_registry();
DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
registry->GetMediaFileSystemsForExtension(
render_view_host(), GetExtension(), cb);
}
MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {}
bool MediaGalleriesGetMetadataFunction::RunImpl() {
......
......@@ -8,6 +8,7 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_MEDIA_GALLERIES_MEDIA_GALLERIES_API_H_
#define CHROME_BROWSER_EXTENSIONS_API_MEDIA_GALLERIES_MEDIA_GALLERIES_API_H_
#include <string>
#include <vector>
#include "base/memory/scoped_ptr.h"
......@@ -78,6 +79,39 @@ class MediaGalleriesGetAllMediaFileSystemMetadataFunction
const MediaStorageUtil::DeviceIdSet* available_devices);
};
class MediaGalleriesAddUserSelectedFolderFunction
: public ChromeAsyncExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("mediaGalleries.addUserSelectedFolder",
MEDIAGALLERIES_ADDUSERSELECTEDFOLDER)
protected:
virtual ~MediaGalleriesAddUserSelectedFolderFunction();
virtual bool RunImpl() OVERRIDE;
private:
// Bottom half for RunImpl, invoked after the preferences is initialized.
void OnPreferencesInit();
// Callback for the directory prompt request, with the input from the user.
// If |selected_directory| is empty, then the user canceled.
// Either handle the user canceled case or add the selected gallery.
void OnDirectorySelected(const base::FilePath& selected_directory);
// Callback for the directory prompt request. |pref_id| is for the gallery
// the user just added. |filesystems| is the entire list of file systems.
// The fsid for the file system that corresponds to |pref_id| will be
// appended to the list of file systems returned to the caller. The
// Javascript binding for this API will interpret the list appropriately.
void ReturnGalleriesAndId(
MediaGalleryPrefId pref_id,
const std::vector<MediaFileSystemInfo>& filesystems);
// A helper method that calls
// MediaFileSystemRegistry::GetMediaFileSystemsForExtension().
void GetMediaFileSystemsForExtension(const MediaFileSystemsCallback& cb);
};
class MediaGalleriesGetMetadataFunction : public ChromeAsyncExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("mediaGalleries.getMetadata",
......
......@@ -717,6 +717,7 @@ enum HistogramValue {
BOOKMARKMANAGERPRIVATE_REDO,
BOOKMARKMANAGERPRIVATE_UNDOINFO,
BOOKMARKMANAGERPRIVATE_REDOINFO,
MEDIAGALLERIES_ADDUSERSELECTEDFOLDER,
ENUM_BOUNDARY // Last entry: Add new entries above.
};
......
......@@ -21,6 +21,7 @@ enum MediaGalleriesUsages {
PREFS_INITIALIZED_ERROR,
GET_ALL_MEDIA_FILE_SYSTEM_METADATA,
GET_METADATA,
ADD_USER_SELECTED_FOLDER,
MEDIA_GALLERIES_NUM_USAGES
};
......
......@@ -41,6 +41,10 @@ namespace mediaGalleries {
callback MediaFileSystemsCallback =
void ([instanceOf=DOMFileSystem] optional object[] mediaFileSystems);
callback AddUserFolderCallback =
void ([instanceOf=DOMFileSystem] object[] mediaFileSystems,
DOMString selectedFileSystemName);
[inline_doc] dictionary MediaFileSystemMetadata {
// The name of the file system.
DOMString name;
......@@ -97,6 +101,11 @@ namespace mediaGalleries {
static void getMediaFileSystems(optional MediaFileSystemsDetails details,
MediaFileSystemsCallback callback);
// Present a directory picker to the user and add the selected directory
// as a gallery. If the user cancels the picker, selectedFileSystemName
// will be empty.
static void addUserSelectedFolder(AddUserFolderCallback callback);
// Get metadata about a specific media file system.
[nocompile] static MediaFileSystemMetadata getMediaFileSystemMetadata(
[instanceOf=DOMFileSystem] object mediaFileSystem);
......
......@@ -11,13 +11,7 @@ var blobNatives = requireNative('blob_natives');
var mediaGalleriesMetadata = {};
binding.registerCustomHook(function(bindingsAPI, extensionId) {
var apiFunctions = bindingsAPI.apiFunctions;
// getMediaFileSystems uses a custom callback so that it can instantiate and
// return an array of file system objects.
apiFunctions.setCustomCallback('getMediaFileSystems',
function(name, request, response) {
function createFileSystemObjectsAndUpdateMetadata(response) {
var result = null;
mediaGalleriesMetadata = {}; // Clear any previous metadata.
if (response) {
......@@ -31,11 +25,42 @@ binding.registerCustomHook(function(bindingsAPI, extensionId) {
mediaGalleriesMetadata[filesystem.name] = metadata;
}
}
return result;
}
binding.registerCustomHook(function(bindingsAPI, extensionId) {
var apiFunctions = bindingsAPI.apiFunctions;
// getMediaFileSystems uses a custom callback so that it can instantiate and
// return an array of file system objects.
apiFunctions.setCustomCallback('getMediaFileSystems',
function(name, request, response) {
var result = createFileSystemObjectsAndUpdateMetadata(response);
if (request.callback)
request.callback(result);
request.callback = null;
});
// addUserSelectedFolder uses a custom callback so that it can instantiate
// and return an array of file system objects.
apiFunctions.setCustomCallback('addUserSelectedFolder',
function(name, request, response) {
var fileSystems = [];
var selectedFileSystemName = "";
if (response && 'mediaFileSystems' in response &&
'selectedFileSystemIndex' in response) {
fileSystems = createFileSystemObjectsAndUpdateMetadata(
response['mediaFileSystems']);
var selectedFileSystemIndex = response['selectedFileSystemIndex'];
if (selectedFileSystemIndex >= 0) {
selectedFileSystemName = fileSystems[selectedFileSystemIndex].name;
}
}
if (request.callback)
request.callback(fileSystems, selectedFileSystemName);
request.callback = null;
});
apiFunctions.setHandleRequest('getMediaFileSystemMetadata',
function(filesystem) {
if (filesystem && filesystem.name &&
......
......@@ -24537,6 +24537,7 @@ other types of suffix sets.
<int value="649" label="MEDIAGALLERIES_GETMETADATA"/>
<int value="650" label="INPUT_IME_SENDKEYEVENTS"/>
<int value="651" label="INPUT_IME_HIDEINPUTVIEW"/>
<int value="652" label="MEDIAGALLERIES_ADDUSERSELECTEDFOLDER"/>
</enum>
<enum name="ExtensionInstallCause" type="int">
......@@ -27205,6 +27206,7 @@ other types of suffix sets.
<int value="9" label="Preferences initialized"/>
<int value="10" label="Preferences initialization failed"/>
<int value="11" label="GetAllMediaFileSystemMetadata API invocations"/>
<int value="12" label="AddUserSelectedFolder API invocations"/>
</enum>
<enum name="MediaKeyError" type="int">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment