This source file includes following definitions.
- NotifyRenderViewHost
- FilePathListToSelectedFileInfoList
- dialog_mode_
- OnListFile
- OnListDone
- FileSelected
- FileSelectedWithExtraInfo
- MultiFilesSelected
- MultiFilesSelectedWithExtraInfo
- FileSelectionCanceled
- StartNewEnumeration
- OnListFile
- OnListDone
- GetFileTypesFromAcceptType
- RunFileChooser
- EnumerateDirectory
- RunFileChooser
- RunFileChooserOnFileThread
- RunFileChooserOnUIThread
- RunFileChooserEnd
- EnumerateDirectory
- EnumerateDirectoryEnd
- Observe
- IsAcceptTypeValid
#include "chrome/browser/file_select_helper.h"
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/file_chooser_params.h"
#include "grit/generated_resources.h"
#include "net/base/mime_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/shell_dialogs/selected_file_info.h"
using content::BrowserThread;
using content::FileChooserParams;
using content::RenderViewHost;
using content::RenderWidgetHost;
using content::WebContents;
namespace {
const int kFileSelectEnumerationId = -1;
void NotifyRenderViewHost(RenderViewHost* render_view_host,
const std::vector<ui::SelectedFileInfo>& files,
FileChooserParams::Mode dialog_mode) {
render_view_host->FilesSelectedInChooser(files, dialog_mode);
}
std::vector<ui::SelectedFileInfo> FilePathListToSelectedFileInfoList(
const std::vector<base::FilePath>& paths) {
std::vector<ui::SelectedFileInfo> selected_files;
for (size_t i = 0; i < paths.size(); ++i) {
selected_files.push_back(
ui::SelectedFileInfo(paths[i], paths[i]));
}
return selected_files;
}
}
struct FileSelectHelper::ActiveDirectoryEnumeration {
ActiveDirectoryEnumeration() : rvh_(NULL) {}
scoped_ptr<DirectoryListerDispatchDelegate> delegate_;
scoped_ptr<net::DirectoryLister> lister_;
RenderViewHost* rvh_;
std::vector<base::FilePath> results_;
};
FileSelectHelper::FileSelectHelper(Profile* profile)
: profile_(profile),
render_view_host_(NULL),
web_contents_(NULL),
select_file_dialog_(),
select_file_types_(),
dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE),
dialog_mode_(FileChooserParams::Open) {
}
FileSelectHelper::~FileSelectHelper() {
if (select_file_dialog_.get())
select_file_dialog_->ListenerDestroyed();
std::map<int, ActiveDirectoryEnumeration*>::iterator iter;
for (iter = directory_enumerations_.begin();
iter != directory_enumerations_.end();
++iter) {
iter->second->lister_.reset();
delete iter->second;
}
}
void FileSelectHelper::DirectoryListerDispatchDelegate::OnListFile(
const net::DirectoryLister::DirectoryListerData& data) {
parent_->OnListFile(id_, data);
}
void FileSelectHelper::DirectoryListerDispatchDelegate::OnListDone(int error) {
parent_->OnListDone(id_, error);
}
void FileSelectHelper::FileSelected(const base::FilePath& path,
int index, void* params) {
FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params);
}
void FileSelectHelper::FileSelectedWithExtraInfo(
const ui::SelectedFileInfo& file,
int index,
void* params) {
if (!render_view_host_)
return;
profile_->set_last_selected_directory(file.file_path.DirName());
const base::FilePath& path = file.local_path;
if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
StartNewEnumeration(path, kFileSelectEnumerationId, render_view_host_);
return;
}
std::vector<ui::SelectedFileInfo> files;
files.push_back(file);
NotifyRenderViewHost(render_view_host_, files, dialog_mode_);
RunFileChooserEnd();
}
void FileSelectHelper::MultiFilesSelected(
const std::vector<base::FilePath>& files,
void* params) {
std::vector<ui::SelectedFileInfo> selected_files =
FilePathListToSelectedFileInfoList(files);
MultiFilesSelectedWithExtraInfo(selected_files, params);
}
void FileSelectHelper::MultiFilesSelectedWithExtraInfo(
const std::vector<ui::SelectedFileInfo>& files,
void* params) {
if (!files.empty())
profile_->set_last_selected_directory(files[0].file_path.DirName());
if (!render_view_host_)
return;
NotifyRenderViewHost(render_view_host_, files, dialog_mode_);
RunFileChooserEnd();
}
void FileSelectHelper::FileSelectionCanceled(void* params) {
if (!render_view_host_)
return;
NotifyRenderViewHost(
render_view_host_, std::vector<ui::SelectedFileInfo>(),
dialog_mode_);
RunFileChooserEnd();
}
void FileSelectHelper::StartNewEnumeration(const base::FilePath& path,
int request_id,
RenderViewHost* render_view_host) {
scoped_ptr<ActiveDirectoryEnumeration> entry(new ActiveDirectoryEnumeration);
entry->rvh_ = render_view_host;
entry->delegate_.reset(new DirectoryListerDispatchDelegate(this, request_id));
entry->lister_.reset(new net::DirectoryLister(path,
true,
net::DirectoryLister::NO_SORT,
entry->delegate_.get()));
if (!entry->lister_->Start()) {
if (request_id == kFileSelectEnumerationId)
FileSelectionCanceled(NULL);
else
render_view_host->DirectoryEnumerationFinished(request_id,
entry->results_);
} else {
directory_enumerations_[request_id] = entry.release();
}
}
void FileSelectHelper::OnListFile(
int id,
const net::DirectoryLister::DirectoryListerData& data) {
ActiveDirectoryEnumeration* entry = directory_enumerations_[id];
if (data.info.IsDirectory())
return;
entry->results_.push_back(data.path);
}
void FileSelectHelper::OnListDone(int id, int error) {
scoped_ptr<ActiveDirectoryEnumeration> entry(directory_enumerations_[id]);
directory_enumerations_.erase(id);
if (!entry->rvh_)
return;
if (error) {
FileSelectionCanceled(NULL);
return;
}
std::vector<ui::SelectedFileInfo> selected_files =
FilePathListToSelectedFileInfoList(entry->results_);
if (id == kFileSelectEnumerationId)
NotifyRenderViewHost(entry->rvh_, selected_files, dialog_mode_);
else
entry->rvh_->DirectoryEnumerationFinished(id, entry->results_);
EnumerateDirectoryEnd();
}
scoped_ptr<ui::SelectFileDialog::FileTypeInfo>
FileSelectHelper::GetFileTypesFromAcceptType(
const std::vector<base::string16>& accept_types) {
scoped_ptr<ui::SelectFileDialog::FileTypeInfo> base_file_type(
new ui::SelectFileDialog::FileTypeInfo());
if (accept_types.empty())
return base_file_type.Pass();
scoped_ptr<ui::SelectFileDialog::FileTypeInfo> file_type(
new ui::SelectFileDialog::FileTypeInfo(*base_file_type));
file_type->include_all_files = true;
file_type->extensions.resize(1);
std::vector<base::FilePath::StringType>* extensions =
&file_type->extensions.back();
int valid_type_count = 0;
int description_id = 0;
for (size_t i = 0; i < accept_types.size(); ++i) {
std::string ascii_type = base::UTF16ToASCII(accept_types[i]);
if (!IsAcceptTypeValid(ascii_type))
continue;
size_t old_extension_size = extensions->size();
if (ascii_type[0] == '.') {
base::FilePath::StringType ext(ascii_type.begin(), ascii_type.end());
extensions->push_back(ext.substr(1));
} else {
if (ascii_type == "image/*")
description_id = IDS_IMAGE_FILES;
else if (ascii_type == "audio/*")
description_id = IDS_AUDIO_FILES;
else if (ascii_type == "video/*")
description_id = IDS_VIDEO_FILES;
net::GetExtensionsForMimeType(ascii_type, extensions);
}
if (extensions->size() > old_extension_size)
valid_type_count++;
}
if (valid_type_count == 0)
return base_file_type.Pass();
if (valid_type_count > 1 ||
(valid_type_count == 1 && description_id == 0 && extensions->size() > 1))
description_id = IDS_CUSTOM_FILES;
if (description_id) {
file_type->extension_description_overrides.push_back(
l10n_util::GetStringUTF16(description_id));
}
return file_type.Pass();
}
void FileSelectHelper::RunFileChooser(content::WebContents* tab,
const FileChooserParams& params) {
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
scoped_refptr<FileSelectHelper> file_select_helper(
new FileSelectHelper(profile));
file_select_helper->RunFileChooser(tab->GetRenderViewHost(), tab, params);
}
void FileSelectHelper::EnumerateDirectory(content::WebContents* tab,
int request_id,
const base::FilePath& path) {
Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
scoped_refptr<FileSelectHelper> file_select_helper(
new FileSelectHelper(profile));
file_select_helper->EnumerateDirectory(
request_id, tab->GetRenderViewHost(), path);
}
void FileSelectHelper::RunFileChooser(RenderViewHost* render_view_host,
content::WebContents* web_contents,
const FileChooserParams& params) {
DCHECK(!render_view_host_);
DCHECK(!web_contents_);
render_view_host_ = render_view_host;
web_contents_ = web_contents;
notification_registrar_.RemoveAll();
notification_registrar_.Add(
this, content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
content::Source<RenderWidgetHost>(render_view_host_));
notification_registrar_.Add(
this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
content::Source<WebContents>(web_contents_));
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&FileSelectHelper::RunFileChooserOnFileThread, this, params));
AddRef();
}
void FileSelectHelper::RunFileChooserOnFileThread(
const FileChooserParams& params) {
select_file_types_ = GetFileTypesFromAcceptType(params.accept_types);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&FileSelectHelper::RunFileChooserOnUIThread, this, params));
}
void FileSelectHelper::RunFileChooserOnUIThread(
const FileChooserParams& params) {
if (!render_view_host_ || !web_contents_) {
RunFileChooserEnd();
return;
}
select_file_dialog_ = ui::SelectFileDialog::Create(
this, new ChromeSelectFilePolicy(web_contents_));
if (!select_file_dialog_)
return;
dialog_mode_ = params.mode;
switch (params.mode) {
case FileChooserParams::Open:
dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
break;
case FileChooserParams::OpenMultiple:
dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
break;
case FileChooserParams::UploadFolder:
dialog_type_ = ui::SelectFileDialog::SELECT_UPLOAD_FOLDER;
break;
case FileChooserParams::Save:
dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
break;
default:
dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
NOTREACHED();
}
base::FilePath default_file_name = params.default_file_name.IsAbsolute() ?
params.default_file_name :
profile_->last_selected_directory().Append(params.default_file_name);
gfx::NativeWindow owning_window =
platform_util::GetTopLevel(render_view_host_->GetView()->GetNativeView());
#if defined(OS_ANDROID)
std::pair<std::vector<base::string16>, bool> accept_types =
std::make_pair(params.accept_types, params.capture);
#endif
select_file_dialog_->SelectFile(
dialog_type_,
params.title,
default_file_name,
select_file_types_.get(),
select_file_types_.get() && !select_file_types_->extensions.empty()
? 1
: 0,
base::FilePath::StringType(),
owning_window,
#if defined(OS_ANDROID)
&accept_types);
#else
NULL);
#endif
select_file_types_.reset();
}
void FileSelectHelper::RunFileChooserEnd() {
render_view_host_ = NULL;
web_contents_ = NULL;
Release();
}
void FileSelectHelper::EnumerateDirectory(int request_id,
RenderViewHost* render_view_host,
const base::FilePath& path) {
AddRef();
StartNewEnumeration(path, request_id, render_view_host);
}
void FileSelectHelper::EnumerateDirectoryEnd() {
Release();
}
void FileSelectHelper::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: {
DCHECK(content::Source<RenderWidgetHost>(source).ptr() ==
render_view_host_);
render_view_host_ = NULL;
break;
}
case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
DCHECK(content::Source<WebContents>(source).ptr() == web_contents_);
web_contents_ = NULL;
break;
}
default:
NOTREACHED();
}
}
bool FileSelectHelper::IsAcceptTypeValid(const std::string& accept_type) {
std::string unused;
if (accept_type.length() <= 1 ||
StringToLowerASCII(accept_type) != accept_type ||
base::TrimWhitespaceASCII(accept_type, base::TRIM_ALL, &unused) !=
base::TRIM_NONE) {
return false;
}
return true;
}