- Add open folder dialog support (FILE_DIALOG_OPEN_FOLDER mode) for CefBrowserHost::RunFileDialog (issue #1030).

- Standardize file dialog behavior across all platforms (issue #1492).
-- Show a file type filter list on OS X.
-- Show the file extensions as part of the filter list description on all platforms (e.g. "Image Types (*.png;*.gif;*.jpg)").
-- Rename the CefBrowserHost::RunFileDialog |accept_types| argument to |accept_filters| and expand support for filters that will be displayed as-is in addition to the currently supported mime-type and extension-based filters. For example, a filter value of "Supported Image Types|.png;.gif;.jpg" will display "Supported Image Types (*.png;*.gif;*.jpg)" in the filter drop-down list and accept *.png, *.gif and *.jpg files.
-- Persist the selected filter item index by passing a new |selected_accept_filter| argument to CefBrowserHost::RunFileDialog and returning the newly selected index via the CefRunFileDialogCallback and CefFileDialogCallback callbacks.
-- Rename the CefBrowserHost::RunFileDialog |default_file_name| argument to |default_file_path| and use the directory component, if any, to set the default directory location. If |default_file_path| ends in a trailing path separator it will be treated as a directory without a file name component.
-- Add FILE_DIALOG_OVERWRITEPROMPT_FLAG and FILE_DIALOG_HIDEREADONLY_FLAG values to cef_file_dialog_mode_t for controlling those behaviors where possible.

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@1973 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2015-01-20 18:24:54 +00:00
parent 3eabbb2e7d
commit e50ea8c29f
29 changed files with 1113 additions and 436 deletions

View File

@ -195,13 +195,15 @@ typedef struct _cef_run_file_dialog_callback_t {
cef_base_t base; cef_base_t base;
/// ///
// Called asynchronously after the file dialog is dismissed. If the selection // Called asynchronously after the file dialog is dismissed.
// was successful |file_paths| will be a single value or a list of values // |selected_accept_filter| is the 0-based index of the value selected from
// depending on the dialog mode. If the selection was cancelled |file_paths| // the accept filters array passed to cef_browser_host_t::RunFileDialog.
// will be NULL. // |file_paths| will be a single value or a list of values depending on the
// dialog mode. If the selection was cancelled |file_paths| will be NULL.
/// ///
void (CEF_CALLBACK *cont)(struct _cef_run_file_dialog_callback_t* self, void (CEF_CALLBACK *on_file_dialog_dismissed)(
struct _cef_browser_host_t* browser_host, cef_string_list_t file_paths); struct _cef_run_file_dialog_callback_t* self, int selected_accept_filter,
cef_string_list_t file_paths);
} cef_run_file_dialog_callback_t; } cef_run_file_dialog_callback_t;
@ -315,17 +317,22 @@ typedef struct _cef_browser_host_t {
// Call to run a file chooser dialog. Only a single file chooser dialog may be // Call to run a file chooser dialog. Only a single file chooser dialog may be
// pending at any given time. |mode| represents the type of dialog to display. // pending at any given time. |mode| represents the type of dialog to display.
// |title| to the title to be used for the dialog and may be NULL to show the // |title| to the title to be used for the dialog and may be NULL to show the
// default title ("Open" or "Save" depending on the mode). |default_file_name| // default title ("Open" or "Save" depending on the mode). |default_file_path|
// is the default file name to select in the dialog. |accept_types| is a list // is the path with optional directory and/or file name component that will be
// of valid lower-cased MIME types or file extensions specified in an input // initially selected in the dialog. |accept_filters| are used to restrict the
// element and is used to restrict selectable files to such types. |callback| // selectable file types and may any combination of (a) valid lower-cased MIME
// will be executed after the dialog is dismissed or immediately if another // types (e.g. "text/*" or "image/*"), (b) individual file extensions (e.g.
// dialog is already pending. The dialog will be initiated asynchronously on // ".txt" or ".png"), or (c) combined description and file extension delimited
// the UI thread. // using "|" and ";" (e.g. "Image Types|.png;.gif;.jpg").
// |selected_accept_filter| is the 0-based index of the filter that will be
// selected by default. |callback| will be executed after the dialog is
// dismissed or immediately if another dialog is already pending. The dialog
// will be initiated asynchronously on the UI thread.
/// ///
void (CEF_CALLBACK *run_file_dialog)(struct _cef_browser_host_t* self, void (CEF_CALLBACK *run_file_dialog)(struct _cef_browser_host_t* self,
cef_file_dialog_mode_t mode, const cef_string_t* title, cef_file_dialog_mode_t mode, const cef_string_t* title,
const cef_string_t* default_file_name, cef_string_list_t accept_types, const cef_string_t* default_file_path, cef_string_list_t accept_filters,
int selected_accept_filter,
struct _cef_run_file_dialog_callback_t* callback); struct _cef_run_file_dialog_callback_t* callback);
/// ///

View File

@ -56,12 +56,14 @@ typedef struct _cef_file_dialog_callback_t {
cef_base_t base; cef_base_t base;
/// ///
// Continue the file selection with the specified |file_paths|. This may be a // Continue the file selection. |selected_accept_filter| should be the 0-based
// single value or a list of values depending on the dialog mode. An NULL // index of the value selected from the accept filters array passed to
// cef_dialog_handler_t::OnFileDialog. |file_paths| should be a single value
// or a list of values depending on the dialog mode. An NULL |file_paths|
// value is treated the same as calling cancel(). // value is treated the same as calling cancel().
/// ///
void (CEF_CALLBACK *cont)(struct _cef_file_dialog_callback_t* self, void (CEF_CALLBACK *cont)(struct _cef_file_dialog_callback_t* self,
cef_string_list_t file_paths); int selected_accept_filter, cef_string_list_t file_paths);
/// ///
// Cancel the file selection. // Cancel the file selection.
@ -84,17 +86,21 @@ typedef struct _cef_dialog_handler_t {
// Called to run a file chooser dialog. |mode| represents the type of dialog // Called to run a file chooser dialog. |mode| represents the type of dialog
// to display. |title| to the title to be used for the dialog and may be NULL // to display. |title| to the title to be used for the dialog and may be NULL
// to show the default title ("Open" or "Save" depending on the mode). // to show the default title ("Open" or "Save" depending on the mode).
// |default_file_name| is the default file name to select in the dialog. // |default_file_path| is the path with optional directory and/or file name
// |accept_types| is a list of valid lower-cased MIME types or file extensions // component that should be initially selected in the dialog. |accept_filters|
// specified in an input element and is used to restrict selectable files to // are used to restrict the selectable file types and may any combination of
// such types. To display a custom dialog return true (1) and execute // (a) valid lower-cased MIME types (e.g. "text/*" or "image/*"), (b)
// |callback| either inline or at a later time. To display the default dialog // individual file extensions (e.g. ".txt" or ".png"), or (c) combined
// return false (0). // description and file extension delimited using "|" and ";" (e.g. "Image
// Types|.png;.gif;.jpg"). |selected_accept_filter| is the 0-based index of
// the filter that should be selected by default. To display a custom dialog
// return true (1) and execute |callback| either inline or at a later time. To
// display the default dialog return false (0).
/// ///
int (CEF_CALLBACK *on_file_dialog)(struct _cef_dialog_handler_t* self, int (CEF_CALLBACK *on_file_dialog)(struct _cef_dialog_handler_t* self,
struct _cef_browser_t* browser, cef_file_dialog_mode_t mode, struct _cef_browser_t* browser, cef_file_dialog_mode_t mode,
const cef_string_t* title, const cef_string_t* default_file_name, const cef_string_t* title, const cef_string_t* default_file_path,
cef_string_list_t accept_types, cef_string_list_t accept_filters, int selected_accept_filter,
struct _cef_file_dialog_callback_t* callback); struct _cef_file_dialog_callback_t* callback);
} cef_dialog_handler_t; } cef_dialog_handler_t;

View File

@ -199,14 +199,15 @@ class CefBrowser : public virtual CefBase {
class CefRunFileDialogCallback : public virtual CefBase { class CefRunFileDialogCallback : public virtual CefBase {
public: public:
/// ///
// Called asynchronously after the file dialog is dismissed. If the selection // Called asynchronously after the file dialog is dismissed.
// was successful |file_paths| will be a single value or a list of values // |selected_accept_filter| is the 0-based index of the value selected from
// depending on the dialog mode. If the selection was cancelled |file_paths| // the accept filters array passed to CefBrowserHost::RunFileDialog.
// will be empty. // |file_paths| will be a single value or a list of values depending on the
// dialog mode. If the selection was cancelled |file_paths| will be empty.
/// ///
/*--cef(capi_name=cont)--*/ /*--cef(index_param=selected_accept_filter,optional_param=file_paths)--*/
virtual void OnFileDialogDismissed( virtual void OnFileDialogDismissed(
CefRefPtr<CefBrowserHost> browser_host, int selected_accept_filter,
const std::vector<CefString>& file_paths) =0; const std::vector<CefString>& file_paths) =0;
}; };
@ -355,20 +356,25 @@ class CefBrowserHost : public virtual CefBase {
// Call to run a file chooser dialog. Only a single file chooser dialog may be // Call to run a file chooser dialog. Only a single file chooser dialog may be
// pending at any given time. |mode| represents the type of dialog to display. // pending at any given time. |mode| represents the type of dialog to display.
// |title| to the title to be used for the dialog and may be empty to show the // |title| to the title to be used for the dialog and may be empty to show the
// default title ("Open" or "Save" depending on the mode). |default_file_name| // default title ("Open" or "Save" depending on the mode). |default_file_path|
// is the default file name to select in the dialog. |accept_types| is a list // is the path with optional directory and/or file name component that will be
// of valid lower-cased MIME types or file extensions specified in an input // initially selected in the dialog. |accept_filters| are used to restrict the
// element and is used to restrict selectable files to such types. |callback| // selectable file types and may any combination of (a) valid lower-cased MIME
// will be executed after the dialog is dismissed or immediately if another // types (e.g. "text/*" or "image/*"), (b) individual file extensions (e.g.
// dialog is already pending. The dialog will be initiated asynchronously on // ".txt" or ".png"), or (c) combined description and file extension delimited
// the UI thread. // using "|" and ";" (e.g. "Image Types|.png;.gif;.jpg").
// |selected_accept_filter| is the 0-based index of the filter that will be
// selected by default. |callback| will be executed after the dialog is
// dismissed or immediately if another dialog is already pending. The dialog
// will be initiated asynchronously on the UI thread.
/// ///
/*--cef(optional_param=title,optional_param=default_file_name, /*--cef(optional_param=title,optional_param=default_file_path,
optional_param=accept_types)--*/ optional_param=accept_filters,index_param=selected_accept_filter)--*/
virtual void RunFileDialog(FileDialogMode mode, virtual void RunFileDialog(FileDialogMode mode,
const CefString& title, const CefString& title,
const CefString& default_file_name, const CefString& default_file_path,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) =0; CefRefPtr<CefRunFileDialogCallback> callback) =0;
/// ///

View File

@ -48,13 +48,17 @@
class CefFileDialogCallback : public virtual CefBase { class CefFileDialogCallback : public virtual CefBase {
public: public:
/// ///
// Continue the file selection with the specified |file_paths|. This may be // Continue the file selection. |selected_accept_filter| should be the 0-based
// a single value or a list of values depending on the dialog mode. An empty // index of the value selected from the accept filters array passed to
// value is treated the same as calling Cancel(). // CefDialogHandler::OnFileDialog. |file_paths| should be a single value or a
// list of values depending on the dialog mode. An empty |file_paths| value is
// treated the same as calling Cancel().
/// ///
/*--cef(capi_name=cont)--*/ /*--cef(capi_name=cont,index_param=selected_accept_filter,
virtual void Continue(const std::vector<CefString>& file_paths) =0; optional_param=file_paths)--*/
virtual void Continue(int selected_accept_filter,
const std::vector<CefString>& file_paths) =0;
/// ///
// Cancel the file selection. // Cancel the file selection.
/// ///
@ -76,20 +80,25 @@ class CefDialogHandler : public virtual CefBase {
// Called to run a file chooser dialog. |mode| represents the type of dialog // Called to run a file chooser dialog. |mode| represents the type of dialog
// to display. |title| to the title to be used for the dialog and may be empty // to display. |title| to the title to be used for the dialog and may be empty
// to show the default title ("Open" or "Save" depending on the mode). // to show the default title ("Open" or "Save" depending on the mode).
// |default_file_name| is the default file name to select in the dialog. // |default_file_path| is the path with optional directory and/or file name
// |accept_types| is a list of valid lower-cased MIME types or file extensions // component that should be initially selected in the dialog. |accept_filters|
// specified in an input element and is used to restrict selectable files to // are used to restrict the selectable file types and may any combination of
// such types. To display a custom dialog return true and execute |callback| // (a) valid lower-cased MIME types (e.g. "text/*" or "image/*"),
// either inline or at a later time. To display the default dialog return // (b) individual file extensions (e.g. ".txt" or ".png"), or (c) combined
// false. // description and file extension delimited using "|" and ";" (e.g.
// "Image Types|.png;.gif;.jpg"). |selected_accept_filter| is the 0-based
// index of the filter that should be selected by default. To display a custom
// dialog return true and execute |callback| either inline or at a later time.
// To display the default dialog return false.
/// ///
/*--cef(optional_param=title,optional_param=default_file_name, /*--cef(optional_param=title,optional_param=default_file_path,
optional_param=accept_types)--*/ optional_param=accept_filters,index_param=selected_accept_filter)--*/
virtual bool OnFileDialog(CefRefPtr<CefBrowser> browser, virtual bool OnFileDialog(CefRefPtr<CefBrowser> browser,
FileDialogMode mode, FileDialogMode mode,
const CefString& title, const CefString& title,
const CefString& default_file_name, const CefString& default_file_path,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefFileDialogCallback> callback) { CefRefPtr<CefFileDialogCallback> callback) {
return false; return false;
} }

View File

@ -1720,11 +1720,36 @@ typedef enum {
/// ///
FILE_DIALOG_OPEN_MULTIPLE, FILE_DIALOG_OPEN_MULTIPLE,
///
// Like Open, but selects a folder to open.
///
FILE_DIALOG_OPEN_FOLDER,
/// ///
// Allows picking a nonexistent file, and prompts to overwrite if the file // Allows picking a nonexistent file, and prompts to overwrite if the file
// already exists. // already exists.
/// ///
FILE_DIALOG_SAVE, FILE_DIALOG_SAVE,
///
// General mask defining the bits used for the type values.
///
FILE_DIALOG_TYPE_MASK = 0xFF,
// Qualifiers.
// Any of the type values above can be augmented by one or more qualifiers.
// These qualifiers further define the dialog behavior.
///
// Prompt to overwrite if the user selects an existing file with the Save
// dialog.
///
FILE_DIALOG_OVERWRITEPROMPT_FLAG = 0x01000000,
///
// Do not display read-only files.
///
FILE_DIALOG_HIDEREADONLY_FLAG = 0x02000000,
} cef_file_dialog_mode_t; } cef_file_dialog_mode_t;
/// ///

View File

@ -200,7 +200,8 @@ class CefFileDialogCallbackImpl : public CefFileDialogCallback {
} }
} }
void Continue(const std::vector<CefString>& file_paths) override { void Continue(int selected_accept_filter,
const std::vector<CefString>& file_paths) override {
if (CEF_CURRENTLY_ON_UIT()) { if (CEF_CURRENTLY_ON_UIT()) {
if (!callback_.is_null()) { if (!callback_.is_null()) {
std::vector<base::FilePath> vec; std::vector<base::FilePath> vec;
@ -209,12 +210,13 @@ class CefFileDialogCallbackImpl : public CefFileDialogCallback {
for (; it != file_paths.end(); ++it) for (; it != file_paths.end(); ++it)
vec.push_back(base::FilePath(*it)); vec.push_back(base::FilePath(*it));
} }
callback_.Run(vec); callback_.Run(selected_accept_filter, vec);
callback_.Reset(); callback_.Reset();
} }
} else { } else {
CEF_POST_TASK(CEF_UIT, CEF_POST_TASK(CEF_UIT,
base::Bind(&CefFileDialogCallbackImpl::Continue, this, file_paths)); base::Bind(&CefFileDialogCallbackImpl::Continue, this,
selected_accept_filter, file_paths));
} }
} }
@ -243,7 +245,7 @@ class CefFileDialogCallbackImpl : public CefFileDialogCallback {
const CefBrowserHostImpl::RunFileChooserCallback& callback) { const CefBrowserHostImpl::RunFileChooserCallback& callback) {
CEF_REQUIRE_UIT(); CEF_REQUIRE_UIT();
std::vector<base::FilePath> file_paths; std::vector<base::FilePath> file_paths;
callback.Run(file_paths); callback.Run(0, file_paths);
} }
CefBrowserHostImpl::RunFileChooserCallback callback_; CefBrowserHostImpl::RunFileChooserCallback callback_;
@ -251,32 +253,17 @@ class CefFileDialogCallbackImpl : public CefFileDialogCallback {
IMPLEMENT_REFCOUNTING(CefFileDialogCallbackImpl); IMPLEMENT_REFCOUNTING(CefFileDialogCallbackImpl);
}; };
class CefRunFileDialogCallbackWrapper void RunFileDialogDismissed(
: public base::RefCountedThreadSafe<CefRunFileDialogCallbackWrapper> { CefRefPtr<CefRunFileDialogCallback> callback,
public: int selected_accept_filter,
CefRunFileDialogCallbackWrapper(CefRefPtr<CefBrowserHost> host, const std::vector<base::FilePath>& file_paths) {
CefRefPtr<CefRunFileDialogCallback> callback) std::vector<CefString> paths;
: host_(host), if (file_paths.size() > 0) {
callback_(callback) { for (size_t i = 0; i < file_paths.size(); ++i)
paths.push_back(file_paths[i].value());
} }
callback->OnFileDialogDismissed(selected_accept_filter, paths);
void Callback(const std::vector<base::FilePath>& file_paths) { }
std::vector<CefString> paths;
if (file_paths.size() > 0) {
for (size_t i = 0; i < file_paths.size(); ++i)
paths.push_back(file_paths[i].value());
}
callback_->OnFileDialogDismissed(host_, paths);
}
private:
friend class base::RefCountedThreadSafe<CefRunFileDialogCallbackWrapper>;
~CefRunFileDialogCallbackWrapper() {}
CefRefPtr<CefBrowserHost> host_;
CefRefPtr<CefRunFileDialogCallback> callback_;
};
} // namespace } // namespace
@ -712,38 +699,47 @@ void CefBrowserHostImpl::SetZoomLevel(double zoomLevel) {
void CefBrowserHostImpl::RunFileDialog( void CefBrowserHostImpl::RunFileDialog(
FileDialogMode mode, FileDialogMode mode,
const CefString& title, const CefString& title,
const CefString& default_file_name, const CefString& default_file_path,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) { CefRefPtr<CefRunFileDialogCallback> callback) {
DCHECK(callback.get()); DCHECK(callback.get());
if (!callback.get()) if (!callback.get())
return; return;
content::FileChooserParams params; FileChooserParams params;
switch (mode) { switch (mode & FILE_DIALOG_TYPE_MASK) {
case FILE_DIALOG_OPEN: case FILE_DIALOG_OPEN:
params.mode = content::FileChooserParams::Open; params.mode = content::FileChooserParams::Open;
break; break;
case FILE_DIALOG_OPEN_MULTIPLE: case FILE_DIALOG_OPEN_MULTIPLE:
params.mode = content::FileChooserParams::OpenMultiple; params.mode = content::FileChooserParams::OpenMultiple;
break; break;
case FILE_DIALOG_OPEN_FOLDER:
params.mode = content::FileChooserParams::UploadFolder;
break;
case FILE_DIALOG_SAVE: case FILE_DIALOG_SAVE:
params.mode = content::FileChooserParams::Save; params.mode = content::FileChooserParams::Save;
break; break;
} }
DCHECK_GE(selected_accept_filter, 0);
params.selected_accept_filter = selected_accept_filter;
params.overwriteprompt = !!(mode & FILE_DIALOG_OVERWRITEPROMPT_FLAG);
params.hidereadonly = !!(mode & FILE_DIALOG_HIDEREADONLY_FLAG);
params.title = title; params.title = title;
if (!default_file_name.empty()) if (!default_file_path.empty())
params.default_file_name = base::FilePath(default_file_name); params.default_file_name = base::FilePath(default_file_path);
if (!accept_types.empty()) {
std::vector<CefString>::const_iterator it = accept_types.begin(); if (!accept_filters.empty()) {
for (; it != accept_types.end(); ++it) std::vector<CefString>::const_iterator it = accept_filters.begin();
for (; it != accept_filters.end(); ++it)
params.accept_types.push_back(*it); params.accept_types.push_back(*it);
} }
scoped_refptr<CefRunFileDialogCallbackWrapper> wrapper = RunFileChooser(params, base::Bind(RunFileDialogDismissed, callback));
new CefRunFileDialogCallbackWrapper(this, callback);
RunFileChooser(params,
base::Bind(&CefRunFileDialogCallbackWrapper::Callback, wrapper));
} }
void CefBrowserHostImpl::StartDownload(const CefString& url) { void CefBrowserHostImpl::StartDownload(const CefString& url) {
@ -1773,7 +1769,7 @@ void CefBrowserHostImpl::OnSetFocus(cef_focus_source_t source) {
} }
void CefBrowserHostImpl::RunFileChooser( void CefBrowserHostImpl::RunFileChooser(
const content::FileChooserParams& params, const FileChooserParams& params,
const RunFileChooserCallback& callback) { const RunFileChooserCallback& callback) {
CEF_POST_TASK(CEF_UIT, CEF_POST_TASK(CEF_UIT,
base::Bind(&CefBrowserHostImpl::RunFileChooserOnUIThread, this, params, base::Bind(&CefBrowserHostImpl::RunFileChooserOnUIThread, this, params,
@ -2269,7 +2265,7 @@ void CefBrowserHostImpl::WebContentsCreated(
} }
void CefBrowserHostImpl::DidNavigateMainFramePostCommit( void CefBrowserHostImpl::DidNavigateMainFramePostCommit(
content::WebContents* tab) { content::WebContents* web_contents) {
base::AutoLock lock_scope(state_lock_); base::AutoLock lock_scope(state_lock_);
has_document_ = false; has_document_ = false;
} }
@ -2283,15 +2279,25 @@ content::JavaScriptDialogManager*
} }
void CefBrowserHostImpl::RunFileChooser( void CefBrowserHostImpl::RunFileChooser(
content::WebContents* tab, content::WebContents* web_contents,
const content::FileChooserParams& params) { const content::FileChooserParams& params) {
content::RenderViewHost* render_view_host = tab->GetRenderViewHost(); content::RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
if (!render_view_host) if (!render_view_host)
return; return;
RunFileChooserOnUIThread(params, if (params.mode == content::FileChooserParams::UploadFolder) {
// TODO(cef): Implement the logic necessary for the 'webkitdirectory'
// attribute. See CEF issue #958.
OnRunFileChooserDelegateCallback(
web_contents, params.mode, 0, std::vector<base::FilePath>());
return;
}
FileChooserParams cef_params;
static_cast<content::FileChooserParams&>(cef_params) = params;
RunFileChooserOnUIThread(cef_params,
base::Bind(&CefBrowserHostImpl::OnRunFileChooserDelegateCallback, this, base::Bind(&CefBrowserHostImpl::OnRunFileChooserDelegateCallback, this,
tab, params.mode)); web_contents, params.mode));
} }
bool CefBrowserHostImpl::SetPendingPopupInfo( bool CefBrowserHostImpl::SetPendingPopupInfo(
@ -2931,19 +2937,13 @@ void CefBrowserHostImpl::OnLoadEnd(CefRefPtr<CefFrame> frame,
} }
void CefBrowserHostImpl::RunFileChooserOnUIThread( void CefBrowserHostImpl::RunFileChooserOnUIThread(
const content::FileChooserParams& params, const FileChooserParams& params,
const RunFileChooserCallback& callback) { const RunFileChooserCallback& callback) {
CEF_REQUIRE_UIT(); CEF_REQUIRE_UIT();
if (file_chooser_pending_) { if (file_chooser_pending_) {
// Dismiss the new dialog immediately. // Dismiss the new dialog immediately.
callback.Run(std::vector<base::FilePath>()); callback.Run(0, std::vector<base::FilePath>());
return;
}
if (params.mode == content::FileChooserParams::UploadFolder) {
NOTIMPLEMENTED();
callback.Run(std::vector<base::FilePath>());
return; return;
} }
@ -2958,7 +2958,7 @@ void CefBrowserHostImpl::RunFileChooserOnUIThread(
if (client_.get()) { if (client_.get()) {
CefRefPtr<CefDialogHandler> handler = client_->GetDialogHandler(); CefRefPtr<CefDialogHandler> handler = client_->GetDialogHandler();
if (handler.get()) { if (handler.get()) {
cef_file_dialog_mode_t mode = FILE_DIALOG_OPEN; int mode = FILE_DIALOG_OPEN;
switch (params.mode) { switch (params.mode) {
case content::FileChooserParams::Open: case content::FileChooserParams::Open:
mode = FILE_DIALOG_OPEN; mode = FILE_DIALOG_OPEN;
@ -2966,6 +2966,9 @@ void CefBrowserHostImpl::RunFileChooserOnUIThread(
case content::FileChooserParams::OpenMultiple: case content::FileChooserParams::OpenMultiple:
mode = FILE_DIALOG_OPEN_MULTIPLE; mode = FILE_DIALOG_OPEN_MULTIPLE;
break; break;
case content::FileChooserParams::UploadFolder:
mode = FILE_DIALOG_OPEN_FOLDER;
break;
case content::FileChooserParams::Save: case content::FileChooserParams::Save:
mode = FILE_DIALOG_SAVE; mode = FILE_DIALOG_SAVE;
break; break;
@ -2974,17 +2977,28 @@ void CefBrowserHostImpl::RunFileChooserOnUIThread(
break; break;
} }
std::vector<CefString> accept_types; if (params.overwriteprompt)
std::vector<base::string16>::const_iterator it = mode |= FILE_DIALOG_OVERWRITEPROMPT_FLAG;
params.accept_types.begin(); if (params.hidereadonly)
mode |= FILE_DIALOG_HIDEREADONLY_FLAG;
std::vector<base::string16>::const_iterator it;
std::vector<CefString> accept_filters;
it = params.accept_types.begin();
for (; it != params.accept_types.end(); ++it) for (; it != params.accept_types.end(); ++it)
accept_types.push_back(*it); accept_filters.push_back(*it);
CefRefPtr<CefFileDialogCallbackImpl> callbackImpl( CefRefPtr<CefFileDialogCallbackImpl> callbackImpl(
new CefFileDialogCallbackImpl(host_callback)); new CefFileDialogCallbackImpl(host_callback));
handled = handler->OnFileDialog(this, mode, params.title, handled = handler->OnFileDialog(
params.default_file_name.value(), this,
accept_types, callbackImpl.get()); static_cast<cef_file_dialog_mode_t>(mode),
params.title,
params.default_file_name.value(),
accept_filters,
params.selected_accept_filter,
callbackImpl.get());
if (!handled) { if (!handled) {
if (callbackImpl->IsConnected()) { if (callbackImpl->IsConnected()) {
callbackImpl->Disconnect(); callbackImpl->Disconnect();
@ -3003,22 +3017,25 @@ void CefBrowserHostImpl::RunFileChooserOnUIThread(
void CefBrowserHostImpl::OnRunFileChooserCallback( void CefBrowserHostImpl::OnRunFileChooserCallback(
const RunFileChooserCallback& callback, const RunFileChooserCallback& callback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths) { const std::vector<base::FilePath>& file_paths) {
CEF_REQUIRE_UIT(); CEF_REQUIRE_UIT();
file_chooser_pending_ = false; file_chooser_pending_ = false;
// Execute the callback asynchronously. // Execute the callback asynchronously.
CEF_POST_TASK(CEF_UIT, base::Bind(callback, file_paths)); CEF_POST_TASK(CEF_UIT,
base::Bind(callback, selected_accept_filter, file_paths));
} }
void CefBrowserHostImpl::OnRunFileChooserDelegateCallback( void CefBrowserHostImpl::OnRunFileChooserDelegateCallback(
content::WebContents* tab, content::WebContents* web_contents,
content::FileChooserParams::Mode mode, content::FileChooserParams::Mode mode,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths) { const std::vector<base::FilePath>& file_paths) {
CEF_REQUIRE_UIT(); CEF_REQUIRE_UIT();
content::RenderViewHost* render_view_host = tab->GetRenderViewHost(); content::RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
if (!render_view_host) if (!render_view_host)
return; return;

View File

@ -97,6 +97,18 @@ class CefBrowserHostImpl : public CefBrowserHost,
virtual void OnResponse(const std::string& response) =0; virtual void OnResponse(const std::string& response) =0;
}; };
// Extend content::FileChooserParams with some options unique to CEF.
struct FileChooserParams : public content::FileChooserParams {
// 0-based index of the selected value in |accept_types|.
int selected_accept_filter = 0;
// True if the Save dialog should prompt before overwriting files.
bool overwriteprompt = true;
// True if read-only files should be hidden.
bool hidereadonly = true;
};
~CefBrowserHostImpl() override; ~CefBrowserHostImpl() override;
// Create a new CefBrowserHostImpl instance. // Create a new CefBrowserHostImpl instance.
@ -142,8 +154,9 @@ class CefBrowserHostImpl : public CefBrowserHost,
void RunFileDialog( void RunFileDialog(
FileDialogMode mode, FileDialogMode mode,
const CefString& title, const CefString& title,
const CefString& default_file_name, const CefString& default_file_path,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) override; CefRefPtr<CefRunFileDialogCallback> callback) override;
void StartDownload(const CefString& url) override; void StartDownload(const CefString& url) override;
void Print() override; void Print() override;
@ -307,13 +320,13 @@ class CefBrowserHostImpl : public CefBrowserHost,
void OnSetFocus(cef_focus_source_t source); void OnSetFocus(cef_focus_source_t source);
// The argument vector will be empty if the dialog was cancelled. // The argument vector will be empty if the dialog was cancelled.
typedef base::Callback<void(const std::vector<base::FilePath>&)> typedef base::Callback<void(int, const std::vector<base::FilePath>&)>
RunFileChooserCallback; RunFileChooserCallback;
// Run the file chooser dialog specified by |params|. Only a single dialog may // Run the file chooser dialog specified by |params|. Only a single dialog may
// be pending at any given time. |callback| will be executed asynchronously // be pending at any given time. |callback| will be executed asynchronously
// after the dialog is dismissed or if another dialog is already pending. // after the dialog is dismissed or if another dialog is already pending.
void RunFileChooser(const content::FileChooserParams& params, void RunFileChooser(const FileChooserParams& params,
const RunFileChooserCallback& callback); const RunFileChooserCallback& callback);
// Used when creating a new popup window. // Used when creating a new popup window.
@ -383,11 +396,11 @@ class CefBrowserHostImpl : public CefBrowserHost,
const GURL& target_url, const GURL& target_url,
content::WebContents* new_contents) override; content::WebContents* new_contents) override;
void DidNavigateMainFramePostCommit( void DidNavigateMainFramePostCommit(
content::WebContents* tab) override; content::WebContents* web_contents) override;
content::JavaScriptDialogManager* GetJavaScriptDialogManager( content::JavaScriptDialogManager* GetJavaScriptDialogManager(
content::WebContents* source) override; content::WebContents* source) override;
void RunFileChooser( void RunFileChooser(
content::WebContents* tab, content::WebContents* web_contents,
const content::FileChooserParams& params) override; const content::FileChooserParams& params) override;
void FindReply( void FindReply(
content::WebContents* web_contents, content::WebContents* web_contents,
@ -528,7 +541,7 @@ class CefBrowserHostImpl : public CefBrowserHost,
// Invoke platform specific handling for the external protocol. // Invoke platform specific handling for the external protocol.
void PlatformHandleExternalProtocol(const GURL& url); void PlatformHandleExternalProtocol(const GURL& url);
// Invoke platform specific file chooser dialog. // Invoke platform specific file chooser dialog.
void PlatformRunFileChooser(const content::FileChooserParams& params, void PlatformRunFileChooser(const FileChooserParams& params,
RunFileChooserCallback callback); RunFileChooserCallback callback);
void PlatformTranslateKeyEvent(content::NativeWebKeyboardEvent& native_event, void PlatformTranslateKeyEvent(content::NativeWebKeyboardEvent& native_event,
@ -565,17 +578,19 @@ class CefBrowserHostImpl : public CefBrowserHost,
int http_status_code); int http_status_code);
// Continuation from RunFileChooser. // Continuation from RunFileChooser.
void RunFileChooserOnUIThread(const content::FileChooserParams& params, void RunFileChooserOnUIThread(const FileChooserParams& params,
const RunFileChooserCallback& callback); const RunFileChooserCallback& callback);
// Used with RunFileChooser to clear the |file_chooser_pending_| flag. // Used with RunFileChooser to clear the |file_chooser_pending_| flag.
void OnRunFileChooserCallback(const RunFileChooserCallback& callback, void OnRunFileChooserCallback(const RunFileChooserCallback& callback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths); const std::vector<base::FilePath>& file_paths);
// Used with WebContentsDelegate::RunFileChooser to notify the WebContents. // Used with WebContentsDelegate::RunFileChooser to notify the WebContents.
void OnRunFileChooserDelegateCallback( void OnRunFileChooserDelegateCallback(
content::WebContents* tab, content::WebContents* web_contents,
content::FileChooserParams::Mode mode, content::FileChooserParams::Mode mode,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths); const std::vector<base::FilePath>& file_paths);
void OnDevToolsWebContentsDestroyed(); void OnDevToolsWebContentsDestroyed();

View File

@ -282,11 +282,10 @@ void CefBrowserHostImpl::PlatformHandleKeyboardEvent(
} }
void CefBrowserHostImpl::PlatformRunFileChooser( void CefBrowserHostImpl::PlatformRunFileChooser(
const content::FileChooserParams& params, const FileChooserParams& params,
RunFileChooserCallback callback) { RunFileChooserCallback callback) {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
std::vector<base::FilePath> files; callback.Run(0, std::vector<base::FilePath>());
callback.Run(files);
} }
void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) { void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) {

View File

@ -15,6 +15,7 @@
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/mac/mac_util.h" #include "base/mac/mac_util.h"
#include "base/mac/scoped_nsautorelease_pool.h" #include "base/mac/scoped_nsautorelease_pool.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
@ -23,6 +24,7 @@
#include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/common/file_chooser_params.h" #include "content/public/common/file_chooser_params.h"
#include "grit/cef_strings.h"
#include "grit/ui_strings.h" #include "grit/ui_strings.h"
#include "net/base/mime_util.h" #include "net/base/mime_util.h"
#include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/WebKit/public/web/WebInputEvent.h"
@ -170,33 +172,216 @@
@end @end
namespace {
base::string16 GetDescriptionFromMimeType(const std::string& mime_type) {
// Check for wild card mime types and return an appropriate description.
static const struct {
const char* mime_type;
int string_id;
} kWildCardMimeTypes[] = {
{ "audio", IDS_APP_AUDIO_FILES },
{ "image", IDS_APP_IMAGE_FILES },
{ "text", IDS_APP_TEXT_FILES },
{ "video", IDS_APP_VIDEO_FILES },
};
for (size_t i = 0; i < arraysize(kWildCardMimeTypes); ++i) {
if (mime_type == std::string(kWildCardMimeTypes[i].mime_type) + "/*")
return l10n_util::GetStringUTF16(kWildCardMimeTypes[i].string_id);
}
return base::string16();
}
void AddFilters(NSPopUpButton *button,
const std::vector<base::string16>& accept_filters,
bool include_all_files,
std::vector<std::vector<base::string16> >* all_extensions) {
for (size_t i = 0; i < accept_filters.size(); ++i) {
const base::string16& filter = accept_filters[i];
if (filter.empty())
continue;
std::vector<base::string16> extensions;
base::string16 description;
size_t sep_index = filter.find('|');
if (sep_index != std::string::npos) {
// Treat as a filter of the form "Filter Name|.ext1;.ext2;.ext3".
description = filter.substr(0, sep_index);
std::vector<base::string16> ext;
base::SplitString(filter.substr(sep_index + 1), ';', &ext);
for (size_t x = 0; x < ext.size(); ++x) {
const base::string16& file_ext = ext[x];
if (!file_ext.empty() && file_ext[0] == '.')
extensions.push_back(file_ext);
}
} else if (filter[0] == '.') {
// Treat as an extension beginning with the '.' character.
extensions.push_back(filter);
} else {
// Otherwise convert mime type to one or more extensions.
const std::string& ascii = base::UTF16ToASCII(filter);
std::vector<base::FilePath::StringType> ext;
net::GetExtensionsForMimeType(ascii, &ext);
if (!ext.empty()) {
for (size_t x = 0; x < ext.size(); ++x)
extensions.push_back(base::ASCIIToUTF16("." + ext[x]));
description = GetDescriptionFromMimeType(ascii);
}
}
if (extensions.empty())
continue;
// Don't display a crazy number of extensions since the NSPopUpButton width
// will keep growing.
const size_t kMaxExtensions = 10;
base::string16 ext_str;
for (size_t x = 0; x < std::min(kMaxExtensions, extensions.size()); ++x) {
const base::string16& pattern = base::ASCIIToUTF16("*") + extensions[x];
if (x != 0)
ext_str += base::ASCIIToUTF16(";");
ext_str += pattern;
}
if (extensions.size() > kMaxExtensions)
ext_str += base::ASCIIToUTF16(";...");
if (description.empty()) {
description = ext_str;
} else {
description +=
base::ASCIIToUTF16(" (") + ext_str + base::ASCIIToUTF16(")");
}
[button addItemWithTitle:base::SysUTF16ToNSString(description)];
all_extensions->push_back(extensions);
}
// Add the *.* filter, but only if we have added other filters (otherwise it
// is implied).
if (include_all_files && !all_extensions->empty()) {
[button addItemWithTitle:base::SysUTF8ToNSString("All Files (*)")];
all_extensions->push_back(std::vector<base::string16>());
}
}
} // namespace
// Used to manage the file type filter in the NSSavePanel/NSOpenPanel.
@interface CefFilterDelegate : NSObject {
@private
NSSavePanel* panel_;
std::vector<std::vector<base::string16> > extensions_;
int selected_index_;
}
- (id)initWithPanel:(NSSavePanel*)panel
andAcceptFilters:(const std::vector<base::string16>&)accept_filters
andFilterIndex:(int)index;
- (void)setFilter:(int)index;
- (int)filter;
- (void)filterSelectionChanged:(id)sender;
- (void)setFileExtension;
@end
@implementation CefFilterDelegate
- (id)initWithPanel:(NSSavePanel*)panel
andAcceptFilters:(const std::vector<base::string16>&)accept_filters
andFilterIndex:(int)index {
if (self = [super init]) {
DCHECK(panel);
panel_ = panel;
selected_index_ = 0;
NSPopUpButton *button = [[NSPopUpButton alloc] init];
AddFilters(button, accept_filters, true, &extensions_);
[button sizeToFit];
[button setTarget:self];
[button setAction:@selector(filterSelectionChanged:)];
if (index < static_cast<int>(extensions_.size())) {
[button selectItemAtIndex:index];
[self setFilter:index];
}
[panel_ setAccessoryView:button];
}
return self;
}
// Set the current filter index.
- (void)setFilter:(int)index {
DCHECK(index >= 0 && index < static_cast<int>(extensions_.size()));
selected_index_ = index;
// Set the selectable file types. For open panels this limits the files that
// can be selected. For save panels this applies a default file extenion when
// the dialog is dismissed if none is already provided.
NSMutableArray* acceptArray = nil;
if (!extensions_[index].empty()) {
acceptArray = [[NSMutableArray alloc] init];
for (size_t i = 0; i < extensions_[index].size(); ++i) {
[acceptArray addObject:
base::SysUTF16ToNSString(extensions_[index][i].substr(1))];
}
}
[panel_ setAllowedFileTypes:acceptArray];
if (![panel_ isKindOfClass:[NSOpenPanel class]]) {
// For save panels set the file extension.
[self setFileExtension];
}
}
// Returns the current filter index.
- (int)filter {
return selected_index_;
}
// Called when the selected filter is changed via the NSPopUpButton.
- (void)filterSelectionChanged:(id)sender {
NSPopUpButton *button = (NSPopUpButton*)sender;
[self setFilter:[button indexOfSelectedItem]];
}
// Set the extension on the currently selected file name.
- (void)setFileExtension {
const std::vector<base::string16>& filter = extensions_[selected_index_];
if (filter.empty()) {
// All extensions are allowed so don't change anything.
return;
}
base::FilePath path(base::SysNSStringToUTF8([panel_ nameFieldStringValue]));
// If the file name currently includes an extension from |filter| then don't
// change anything.
base::string16 extension = base::UTF8ToUTF16(path.Extension());
if (!extension.empty()) {
for (size_t i = 0; i < filter.size(); ++i) {
if (filter[i] == extension)
return;
}
}
// Change the extension to the first value in |filter|.
path = path.ReplaceExtension(base::UTF16ToUTF8(filter[0]));
[panel_ setNameFieldStringValue:base::SysUTF8ToNSString(path.value())];
}
@end
namespace { namespace {
// Accept-types to file-types helper. void RunOpenFileDialog(const CefBrowserHostImpl::FileChooserParams& params,
NSMutableArray* GetFileTypesFromAcceptTypes(
const std::vector<base::string16>& accept_types) {
NSMutableArray* acceptArray = [[NSMutableArray alloc] init];
for (size_t i=0; i<accept_types.size(); i++) {
std::string ascii_type = base::UTF16ToASCII(accept_types[i]);
if (ascii_type.length()) {
// Just treat as extension if contains '.' as the first character.
if (ascii_type[0] == '.') {
[acceptArray addObject:base::SysUTF8ToNSString(ascii_type.substr(1))];
} else {
// Otherwise convert mime type to one or more extensions.
std::vector<base::FilePath::StringType> ext;
net::GetExtensionsForMimeType(ascii_type, &ext);
for (size_t x = 0; x < ext.size(); ++x)
[acceptArray addObject:base::SysUTF8ToNSString(ext[x])];
}
}
}
return acceptArray;
}
void RunOpenFileDialog(const content::FileChooserParams& params,
NSView* view, NSView* view,
int* filter_index,
std::vector<base::FilePath>* files) { std::vector<base::FilePath>* files) {
NSOpenPanel* openPanel = [NSOpenPanel openPanel]; NSOpenPanel* openPanel = [NSOpenPanel openPanel];
@ -206,39 +391,51 @@ void RunOpenFileDialog(const content::FileChooserParams& params,
} else { } else {
title = l10n_util::GetStringUTF16( title = l10n_util::GetStringUTF16(
params.mode == content::FileChooserParams::Open ? params.mode == content::FileChooserParams::Open ?
IDS_OPEN_FILE_DIALOG_TITLE : IDS_OPEN_FILES_DIALOG_TITLE); IDS_OPEN_FILE_DIALOG_TITLE :
(params.mode == content::FileChooserParams::OpenMultiple ?
IDS_OPEN_FILES_DIALOG_TITLE : IDS_SELECT_FOLDER_DIALOG_TITLE));
} }
[openPanel setTitle:base::SysUTF16ToNSString(title)]; [openPanel setTitle:base::SysUTF16ToNSString(title)];
// Consider default file name if any. std::string filename, directory;
base::FilePath default_file_name(params.default_file_name); if (!params.default_file_name.empty()) {
if (params.mode == content::FileChooserParams::UploadFolder ||
if (!default_file_name.empty()) { params.default_file_name.EndsWithSeparator()) {
if (!default_file_name.BaseName().empty()) { // The value is only a directory.
NSString* defaultName = base::SysUTF8ToNSString( directory = params.default_file_name.value();
default_file_name.BaseName().value()); } else {
[openPanel setNameFieldStringValue:defaultName]; // The value is a file name and possibly a directory.
} filename = params.default_file_name.BaseName().value();
directory = params.default_file_name.DirName().value();
if (!default_file_name.DirName().empty()) {
NSString* defaultDir = base::SysUTF8ToNSString(
default_file_name.DirName().value());
[openPanel setDirectoryURL:[NSURL fileURLWithPath:defaultDir]];
} }
} }
if (!filename.empty()) {
[openPanel setNameFieldStringValue:base::SysUTF8ToNSString(filename)];
}
if (!directory.empty()) {
[openPanel setDirectoryURL:
[NSURL fileURLWithPath:base::SysUTF8ToNSString(directory)]];
}
// Consider supported file types CefFilterDelegate* filter_delegate = nil;
if (!params.accept_types.empty()) { if (params.mode != content::FileChooserParams::UploadFolder &&
[openPanel setAllowedFileTypes:GetFileTypesFromAcceptTypes( !params.accept_types.empty()) {
params.accept_types)]; // Add the file filter control.
filter_delegate =
[[CefFilterDelegate alloc] initWithPanel:openPanel
andAcceptFilters:params.accept_types
andFilterIndex:*filter_index];
} }
// Further panel configuration. // Further panel configuration.
[openPanel setAllowsOtherFileTypes:YES]; [openPanel setAllowsOtherFileTypes:YES];
[openPanel setAllowsMultipleSelection: [openPanel setAllowsMultipleSelection:
(params.mode == content::FileChooserParams::OpenMultiple)]; (params.mode == content::FileChooserParams::OpenMultiple)];
[openPanel setCanChooseFiles:YES]; [openPanel setCanChooseFiles:
[openPanel setCanChooseDirectories:NO]; (params.mode != content::FileChooserParams::UploadFolder)];
[openPanel setCanChooseDirectories:
(params.mode == content::FileChooserParams::UploadFolder)];
[openPanel setShowsHiddenFiles:!params.hidereadonly];
// Show panel. // Show panel.
[openPanel beginSheetModalForWindow:[view window] completionHandler:nil]; [openPanel beginSheetModalForWindow:[view window] completionHandler:nil];
@ -251,11 +448,16 @@ void RunOpenFileDialog(const content::FileChooserParams& params,
files->push_back(base::FilePath(base::SysNSStringToUTF8([url path]))); files->push_back(base::FilePath(base::SysNSStringToUTF8([url path])));
} }
} }
if (filter_delegate != nil)
*filter_index = [filter_delegate filter];
[NSApp endSheet:openPanel]; [NSApp endSheet:openPanel];
} }
bool RunSaveFileDialog(const content::FileChooserParams& params, bool RunSaveFileDialog(const CefBrowserHostImpl::FileChooserParams& params,
NSView* view, NSView* view,
int* filter_index,
base::FilePath* file) { base::FilePath* file) {
NSSavePanel* savePanel = [NSSavePanel savePanel]; NSSavePanel* savePanel = [NSSavePanel savePanel];
@ -266,30 +468,36 @@ bool RunSaveFileDialog(const content::FileChooserParams& params,
title = l10n_util::GetStringUTF16(IDS_SAVE_AS_DIALOG_TITLE); title = l10n_util::GetStringUTF16(IDS_SAVE_AS_DIALOG_TITLE);
[savePanel setTitle:base::SysUTF16ToNSString(title)]; [savePanel setTitle:base::SysUTF16ToNSString(title)];
// Consider default file name if any. std::string filename, directory;
base::FilePath default_file_name(params.default_file_name); if (!params.default_file_name.empty()) {
if (params.default_file_name.EndsWithSeparator()) {
if (!default_file_name.empty()) { // The value is only a directory.
if (!default_file_name.BaseName().empty()) { directory = params.default_file_name.value();
NSString* defaultName = base::SysUTF8ToNSString( } else {
default_file_name.BaseName().value()); // The value is a file name and possibly a directory.
[savePanel setNameFieldStringValue:defaultName]; filename = params.default_file_name.BaseName().value();
} directory = params.default_file_name.DirName().value();
if (!default_file_name.DirName().empty()) {
NSString* defaultDir = base::SysUTF8ToNSString(
default_file_name.DirName().value());
[savePanel setDirectoryURL:[NSURL fileURLWithPath:defaultDir]];
} }
} }
if (!filename.empty()) {
[savePanel setNameFieldStringValue:base::SysUTF8ToNSString(filename)];
}
if (!directory.empty()) {
[savePanel setDirectoryURL:
[NSURL fileURLWithPath:base::SysUTF8ToNSString(directory)]];
}
// Consider supported file types CefFilterDelegate* filter_delegate = nil;
if (!params.accept_types.empty()) { if (!params.accept_types.empty()) {
[savePanel setAllowedFileTypes:GetFileTypesFromAcceptTypes( // Add the file filter control.
params.accept_types)]; filter_delegate =
[[CefFilterDelegate alloc] initWithPanel:savePanel
andAcceptFilters:params.accept_types
andFilterIndex:*filter_index];
} }
[savePanel setAllowsOtherFileTypes:YES]; [savePanel setAllowsOtherFileTypes:YES];
[savePanel setShowsHiddenFiles:!params.hidereadonly];
bool success = false; bool success = false;
@ -300,6 +508,10 @@ bool RunSaveFileDialog(const content::FileChooserParams& params,
*file = base::FilePath([path UTF8String]); *file = base::FilePath([path UTF8String]);
success = true; success = true;
} }
if (filter_delegate != nil)
*filter_index = [filter_delegate filter];
[NSApp endSheet:savePanel]; [NSApp endSheet:savePanel];
return success; return success;
@ -491,22 +703,26 @@ void CefBrowserHostImpl::PlatformHandleKeyboardEvent(
} }
void CefBrowserHostImpl::PlatformRunFileChooser( void CefBrowserHostImpl::PlatformRunFileChooser(
const content::FileChooserParams& params, const FileChooserParams& params,
RunFileChooserCallback callback) { RunFileChooserCallback callback) {
std::vector<base::FilePath> files; std::vector<base::FilePath> files;
int filter_index = params.selected_accept_filter;
NSView* owner = PlatformGetWindowHandle();
if (params.mode == content::FileChooserParams::Open || if (params.mode == content::FileChooserParams::Open ||
params.mode == content::FileChooserParams::OpenMultiple) { params.mode == content::FileChooserParams::OpenMultiple ||
RunOpenFileDialog(params, PlatformGetWindowHandle(), &files); params.mode == content::FileChooserParams::UploadFolder) {
RunOpenFileDialog(params, owner, &filter_index, &files);
} else if (params.mode == content::FileChooserParams::Save) { } else if (params.mode == content::FileChooserParams::Save) {
base::FilePath file; base::FilePath file;
if (RunSaveFileDialog(params, PlatformGetWindowHandle(), &file)) if (RunSaveFileDialog(params, owner, &filter_index, &file)) {
files.push_back(file); files.push_back(file);
}
} else { } else {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
} }
callback.Run(files); callback.Run(filter_index, files);
} }
void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) { void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) {

View File

@ -8,6 +8,7 @@
#include <commdlg.h> #include <commdlg.h>
#include <dwmapi.h> #include <dwmapi.h>
#include <shellapi.h> #include <shellapi.h>
#include <shlobj.h>
#include <wininet.h> #include <wininet.h>
#include <winspool.h> #include <winspool.h>
@ -19,9 +20,11 @@
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/i18n/case_conversion.h" #include "base/i18n/case_conversion.h"
#include "base/memory/ref_counted_memory.h" #include "base/memory/ref_counted_memory.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h" #include "base/win/registry.h"
#include "base/win/scoped_comptr.h"
#include "base/win/windows_version.h" #include "base/win/windows_version.h"
#include "content/common/cursors/webcursor.h" #include "content/common/cursors/webcursor.h"
#include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/native_web_keyboard_event.h"
@ -140,14 +143,8 @@ std::wstring FormatFilterForExtensions(
ext_name = ext_name.substr(ext_index); ext_name = ext_name.substr(ext_index);
if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) { if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) {
// The extension doesn't exist in the registry. Create a description // The extension doesn't exist in the registry.
// based on the unknown extension type (i.e. if the extension is .qqq,
// the we create a description "QQQ File (.qqq)").
include_all_files = true; include_all_files = true;
desc = l10n_util::GetStringFUTF16(
IDS_APP_SAVEAS_EXTENSION_FORMAT,
base::i18n::ToUpper(base::WideToUTF16(ext_name)),
ext_name);
} }
} }
@ -189,33 +186,54 @@ std::wstring GetDescriptionFromMimeType(const std::string& mime_type) {
return std::wstring(); return std::wstring();
} }
std::wstring GetFilterStringFromAcceptTypes( std::wstring GetFilterString(
const std::vector<base::string16>& accept_types) { const std::vector<base::string16>& accept_filters) {
std::vector<std::wstring> extensions; std::vector<std::wstring> extensions;
std::vector<std::wstring> descriptions; std::vector<std::wstring> descriptions;
for (size_t i = 0; i < accept_types.size(); ++i) { for (size_t i = 0; i < accept_filters.size(); ++i) {
std::string ascii_type = base::UTF16ToASCII(accept_types[i]); const base::string16& filter = accept_filters[i];
if (ascii_type.length()) { if (filter.empty())
// Just treat as extension if contains '.' as the first character. continue;
if (ascii_type[0] == '.') {
extensions.push_back(L"*" + base::ASCIIToUTF16(ascii_type)); size_t sep_index = filter.find('|');
descriptions.push_back(std::wstring()); if (sep_index != base::string16::npos) {
} else { // Treat as a filter of the form "Filter Name|.ext1;.ext2;.ext3".
// Otherwise convert mime type to one or more extensions. const base::string16& desc = filter.substr(0, sep_index);
std::vector<base::FilePath::StringType> ext; std::vector<base::string16> ext;
std::wstring ext_str; base::SplitString(filter.substr(sep_index + 1), ';', &ext);
net::GetExtensionsForMimeType(ascii_type, &ext); std::wstring ext_str;
if (ext.size() > 0) { for (size_t x = 0; x < ext.size(); ++x) {
for (size_t x = 0; x < ext.size(); ++x) { const base::string16& file_ext = ext[x];
if (x != 0) if (!file_ext.empty() && file_ext[0] == '.') {
ext_str += L";"; if (!ext_str.empty())
ext_str += L"*." + ext[x]; ext_str += L";";
} ext_str += L"*" + file_ext;
extensions.push_back(ext_str);
descriptions.push_back(GetDescriptionFromMimeType(ascii_type));
} }
} }
if (!ext_str.empty()) {
extensions.push_back(ext_str);
descriptions.push_back(desc);
}
} else if (filter[0] == L'.') {
// Treat as an extension beginning with the '.' character.
extensions.push_back(L"*" + filter);
descriptions.push_back(std::wstring());
} else {
// Otherwise convert mime type to one or more extensions.
const std::string& ascii = base::UTF16ToASCII(filter);
std::vector<base::FilePath::StringType> ext;
std::wstring ext_str;
net::GetExtensionsForMimeType(ascii, &ext);
if (!ext.empty()) {
for (size_t x = 0; x < ext.size(); ++x) {
if (x != 0)
ext_str += L";";
ext_str += L"*." + ext[x];
}
extensions.push_back(ext_str);
descriptions.push_back(GetDescriptionFromMimeType(ascii));
}
} }
} }
@ -224,8 +242,9 @@ std::wstring GetFilterStringFromAcceptTypes(
// from chrome/browser/views/shell_dialogs_win.cc // from chrome/browser/views/shell_dialogs_win.cc
bool RunOpenFileDialog(const content::FileChooserParams& params, bool RunOpenFileDialog(const CefBrowserHostImpl::FileChooserParams& params,
HWND owner, HWND owner,
int* filter_index,
base::FilePath* path) { base::FilePath* path) {
OPENFILENAME ofn; OPENFILENAME ofn;
@ -235,22 +254,25 @@ bool RunOpenFileDialog(const content::FileChooserParams& params,
ofn.lStructSize = sizeof(ofn); ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = owner; ofn.hwndOwner = owner;
// Consider default file name if any.
base::FilePath default_file_name(params.default_file_name);
wchar_t filename[MAX_PATH] = {0}; wchar_t filename[MAX_PATH] = {0};
ofn.lpstrFile = filename; ofn.lpstrFile = filename;
ofn.nMaxFile = MAX_PATH; ofn.nMaxFile = MAX_PATH;
std::wstring directory; std::wstring directory;
if (!default_file_name.empty()) { if (!params.default_file_name.empty()) {
base::wcslcpy(filename, default_file_name.value().c_str(), if (params.default_file_name.EndsWithSeparator()) {
arraysize(filename)); // The value is only a directory.
directory = params.default_file_name.value();
directory = default_file_name.DirName().value(); } else {
ofn.lpstrInitialDir = directory.c_str(); // The value is a file name and possibly a directory.
base::wcslcpy(filename, params.default_file_name.value().c_str(),
arraysize(filename));
directory = params.default_file_name.DirName().value();
}
} }
if (!directory.empty())
ofn.lpstrInitialDir = directory.c_str();
std::wstring title; std::wstring title;
if (!params.title.empty()) if (!params.title.empty())
@ -264,19 +286,27 @@ bool RunOpenFileDialog(const content::FileChooserParams& params,
// without having to close Chrome first. // without having to close Chrome first.
ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_EXPLORER | ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_EXPLORER |
OFN_ENABLESIZING; OFN_ENABLESIZING;
if (params.hidereadonly)
ofn.Flags |= OFN_HIDEREADONLY;
std::wstring filter = GetFilterStringFromAcceptTypes(params.accept_types); const std::wstring& filter = GetFilterString(params.accept_types);
if (!filter.empty()) if (!filter.empty()) {
ofn.lpstrFilter = filter.c_str(); ofn.lpstrFilter = filter.c_str();
// Indices into |lpstrFilter| start at 1.
ofn.nFilterIndex = *filter_index + 1;
}
bool success = !!GetOpenFileName(&ofn); bool success = !!GetOpenFileName(&ofn);
if (success) if (success) {
*filter_index = ofn.nFilterIndex == 0 ? 0 : ofn.nFilterIndex - 1;
*path = base::FilePath(filename); *path = base::FilePath(filename);
}
return success; return success;
} }
bool RunOpenMultiFileDialog(const content::FileChooserParams& params, bool RunOpenMultiFileDialog(const CefBrowserHostImpl::FileChooserParams& params,
HWND owner, HWND owner,
int* filter_index,
std::vector<base::FilePath>* paths) { std::vector<base::FilePath>* paths) {
OPENFILENAME ofn; OPENFILENAME ofn;
@ -292,6 +322,19 @@ bool RunOpenMultiFileDialog(const content::FileChooserParams& params,
ofn.lpstrFile = filename.get(); ofn.lpstrFile = filename.get();
ofn.nMaxFile = UNICODE_STRING_MAX_CHARS; ofn.nMaxFile = UNICODE_STRING_MAX_CHARS;
std::wstring directory;
if (!params.default_file_name.empty()) {
if (params.default_file_name.EndsWithSeparator()) {
// The value is only a directory.
directory = params.default_file_name.value();
} else {
// The value is a file name and possibly a directory.
directory = params.default_file_name.DirName().value();
}
}
if (!directory.empty())
ofn.lpstrInitialDir = directory.c_str();
std::wstring title; std::wstring title;
if (!params.title.empty()) if (!params.title.empty())
title = params.title; title = params.title;
@ -303,11 +346,16 @@ bool RunOpenMultiFileDialog(const content::FileChooserParams& params,
// We use OFN_NOCHANGEDIR so that the user can rename or delete the directory // We use OFN_NOCHANGEDIR so that the user can rename or delete the directory
// without having to close Chrome first. // without having to close Chrome first.
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER | ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER |
OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING; OFN_ALLOWMULTISELECT | OFN_ENABLESIZING;
if (params.hidereadonly)
ofn.Flags |= OFN_HIDEREADONLY;
std::wstring filter = GetFilterStringFromAcceptTypes(params.accept_types); const std::wstring& filter = GetFilterString(params.accept_types);
if (!filter.empty()) if (!filter.empty()) {
ofn.lpstrFilter = filter.c_str(); ofn.lpstrFilter = filter.c_str();
// Indices into |lpstrFilter| start at 1.
ofn.nFilterIndex = *filter_index + 1;
}
bool success = !!GetOpenFileName(&ofn); bool success = !!GetOpenFileName(&ofn);
@ -334,11 +382,84 @@ bool RunOpenMultiFileDialog(const content::FileChooserParams& params,
} }
} }
} }
if (success)
*filter_index = ofn.nFilterIndex == 0 ? 0 : ofn.nFilterIndex - 1;
return success; return success;
} }
bool RunSaveFileDialog(const content::FileChooserParams& params, // The callback function for when the select folder dialog is opened.
int CALLBACK BrowseCallbackProc(HWND window,
UINT message,
LPARAM parameter,
LPARAM data)
{
if (message == BFFM_INITIALIZED) {
// WParam is TRUE since passing a path.
// data lParam member of the BROWSEINFO structure.
SendMessage(window, BFFM_SETSELECTION, TRUE, (LPARAM)data);
}
return 0;
}
bool RunOpenFolderDialog(const CefBrowserHostImpl::FileChooserParams& params,
HWND owner,
base::FilePath* path) {
wchar_t dir_buffer[MAX_PATH + 1] = {0};
bool result = false;
BROWSEINFO browse_info = {0};
browse_info.hwndOwner = owner;
browse_info.pszDisplayName = dir_buffer;
browse_info.ulFlags = BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
std::wstring title;
if (!params.title.empty())
title = params.title;
else
title = l10n_util::GetStringUTF16(IDS_SELECT_FOLDER_DIALOG_TITLE);
if (!title.empty())
browse_info.lpszTitle = title.c_str();
const std::wstring& file_path = params.default_file_name.value();
if (!file_path.empty()) {
// Highlight the current value.
browse_info.lParam = (LPARAM)file_path.c_str();
browse_info.lpfn = &BrowseCallbackProc;
}
LPITEMIDLIST list = SHBrowseForFolder(&browse_info);
if (list) {
STRRET out_dir_buffer;
ZeroMemory(&out_dir_buffer, sizeof(out_dir_buffer));
out_dir_buffer.uType = STRRET_WSTR;
base::win::ScopedComPtr<IShellFolder> shell_folder;
if (SHGetDesktopFolder(shell_folder.Receive()) == NOERROR) {
HRESULT hr = shell_folder->GetDisplayNameOf(list, SHGDN_FORPARSING,
&out_dir_buffer);
if (SUCCEEDED(hr) && out_dir_buffer.uType == STRRET_WSTR) {
*path = base::FilePath(out_dir_buffer.pOleStr);
CoTaskMemFree(out_dir_buffer.pOleStr);
result = true;
} else {
// Use old way if we don't get what we want.
wchar_t old_out_dir_buffer[MAX_PATH + 1];
if (SHGetPathFromIDList(list, old_out_dir_buffer)) {
*path = base::FilePath(old_out_dir_buffer);
result = true;
}
}
}
CoTaskMemFree(list);
}
return result;
}
bool RunSaveFileDialog(const CefBrowserHostImpl::FileChooserParams& params,
HWND owner, HWND owner,
int* filter_index,
base::FilePath* path) { base::FilePath* path) {
OPENFILENAME ofn; OPENFILENAME ofn;
@ -348,22 +469,25 @@ bool RunSaveFileDialog(const content::FileChooserParams& params,
ofn.lStructSize = sizeof(ofn); ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = owner; ofn.hwndOwner = owner;
// Consider default file name if any.
base::FilePath default_file_name(params.default_file_name);
wchar_t filename[MAX_PATH] = {0}; wchar_t filename[MAX_PATH] = {0};
ofn.lpstrFile = filename; ofn.lpstrFile = filename;
ofn.nMaxFile = MAX_PATH; ofn.nMaxFile = MAX_PATH;
std::wstring directory; std::wstring directory;
if (!default_file_name.empty()) { if (!params.default_file_name.empty()) {
base::wcslcpy(filename, default_file_name.value().c_str(), if (params.default_file_name.EndsWithSeparator()) {
arraysize(filename)); // The value is only a directory.
directory = params.default_file_name.value();
directory = default_file_name.DirName().value(); } else {
ofn.lpstrInitialDir = directory.c_str(); // The value is a file name and possibly a directory.
base::wcslcpy(filename, params.default_file_name.value().c_str(),
arraysize(filename));
directory = params.default_file_name.DirName().value();
}
} }
if (!directory.empty())
ofn.lpstrInitialDir = directory.c_str();
std::wstring title; std::wstring title;
if (!params.title.empty()) if (!params.title.empty())
@ -375,16 +499,25 @@ bool RunSaveFileDialog(const content::FileChooserParams& params,
// We use OFN_NOCHANGEDIR so that the user can rename or delete the directory // We use OFN_NOCHANGEDIR so that the user can rename or delete the directory
// without having to close Chrome first. // without having to close Chrome first.
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLESIZING | ofn.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_NOCHANGEDIR |
OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST; OFN_PATHMUSTEXIST;
if (params.hidereadonly)
ofn.Flags |= OFN_HIDEREADONLY;
if (params.overwriteprompt)
ofn.Flags |= OFN_OVERWRITEPROMPT;
std::wstring filter = GetFilterStringFromAcceptTypes(params.accept_types); const std::wstring& filter = GetFilterString(params.accept_types);
if (!filter.empty()) if (!filter.empty()) {
ofn.lpstrFilter = filter.c_str(); ofn.lpstrFilter = filter.c_str();
// Indices into |lpstrFilter| start at 1.
ofn.nFilterIndex = *filter_index + 1;
}
bool success = !!GetSaveFileName(&ofn); bool success = !!GetSaveFileName(&ofn);
if (success) if (success) {
*filter_index = ofn.nFilterIndex == 0 ? 0 : ofn.nFilterIndex - 1;
*path = base::FilePath(filename); *path = base::FilePath(filename);
}
return success; return success;
} }
@ -782,25 +915,32 @@ void CefBrowserHostImpl::PlatformHandleKeyboardEvent(
} }
void CefBrowserHostImpl::PlatformRunFileChooser( void CefBrowserHostImpl::PlatformRunFileChooser(
const content::FileChooserParams& params, const FileChooserParams& params,
RunFileChooserCallback callback) { RunFileChooserCallback callback) {
int filter_index = params.selected_accept_filter;
std::vector<base::FilePath> files; std::vector<base::FilePath> files;
HWND owner = PlatformGetWindowHandle();
if (params.mode == content::FileChooserParams::Open) { if (params.mode == content::FileChooserParams::Open) {
base::FilePath file; base::FilePath file;
if (RunOpenFileDialog(params, PlatformGetWindowHandle(), &file)) if (RunOpenFileDialog(params, owner, &filter_index, &file))
files.push_back(file); files.push_back(file);
} else if (params.mode == content::FileChooserParams::OpenMultiple) { } else if (params.mode == content::FileChooserParams::OpenMultiple) {
RunOpenMultiFileDialog(params, PlatformGetWindowHandle(), &files); RunOpenMultiFileDialog(params, owner, &filter_index, &files);
} else if (params.mode == content::FileChooserParams::UploadFolder) {
base::FilePath file;
if (RunOpenFolderDialog(params, owner, &file))
files.push_back(file);
} else if (params.mode == content::FileChooserParams::Save) { } else if (params.mode == content::FileChooserParams::Save) {
base::FilePath file; base::FilePath file;
if (RunSaveFileDialog(params, PlatformGetWindowHandle(), &file)) if (RunSaveFileDialog(params, owner, &filter_index, &file))
files.push_back(file); files.push_back(file);
} else { } else {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
} }
callback.Run(files); callback.Run(filter_index, files);
} }
void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) { void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) {

View File

@ -142,7 +142,7 @@ class CefBeforeDownloadCallbackImpl : public CefBeforeDownloadCallback {
if (browser.get()) { if (browser.get()) {
handled = true; handled = true;
content::FileChooserParams params; CefBrowserHostImpl::FileChooserParams params;
params.mode = content::FileChooserParams::Save; params.mode = content::FileChooserParams::Save;
if (!suggested_path.empty()) { if (!suggested_path.empty()) {
params.default_file_name = suggested_path; params.default_file_name = suggested_path;
@ -169,6 +169,7 @@ class CefBeforeDownloadCallbackImpl : public CefBeforeDownloadCallback {
static void ChooseDownloadPathCallback( static void ChooseDownloadPathCallback(
const content::DownloadTargetCallback& callback, const content::DownloadTargetCallback& callback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths) { const std::vector<base::FilePath>& file_paths) {
DCHECK_LE(file_paths.size(), (size_t) 1); DCHECK_LE(file_paths.size(), (size_t) 1);

View File

@ -246,29 +246,34 @@ void CEF_CALLBACK browser_host_set_zoom_level(struct _cef_browser_host_t* self,
void CEF_CALLBACK browser_host_run_file_dialog(struct _cef_browser_host_t* self, void CEF_CALLBACK browser_host_run_file_dialog(struct _cef_browser_host_t* self,
cef_file_dialog_mode_t mode, const cef_string_t* title, cef_file_dialog_mode_t mode, const cef_string_t* title,
const cef_string_t* default_file_name, cef_string_list_t accept_types, const cef_string_t* default_file_path, cef_string_list_t accept_filters,
cef_run_file_dialog_callback_t* callback) { int selected_accept_filter, cef_run_file_dialog_callback_t* callback) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self); DCHECK(self);
if (!self) if (!self)
return; return;
// Verify param: selected_accept_filter; type: simple_byval
DCHECK_GE(selected_accept_filter, 0);
if (selected_accept_filter < 0)
return;
// Verify param: callback; type: refptr_diff // Verify param: callback; type: refptr_diff
DCHECK(callback); DCHECK(callback);
if (!callback) if (!callback)
return; return;
// Unverified params: title, default_file_name, accept_types // Unverified params: title, default_file_path, accept_filters
// Translate param: accept_types; type: string_vec_byref_const // Translate param: accept_filters; type: string_vec_byref_const
std::vector<CefString> accept_typesList; std::vector<CefString> accept_filtersList;
transfer_string_list_contents(accept_types, accept_typesList); transfer_string_list_contents(accept_filters, accept_filtersList);
// Execute // Execute
CefBrowserHostCppToC::Get(self)->RunFileDialog( CefBrowserHostCppToC::Get(self)->RunFileDialog(
mode, mode,
CefString(title), CefString(title),
CefString(default_file_name), CefString(default_file_path),
accept_typesList, accept_filtersList,
selected_accept_filter,
CefRunFileDialogCallbackCToCpp::Wrap(callback)); CefRunFileDialogCallbackCToCpp::Wrap(callback));
} }

View File

@ -21,8 +21,8 @@
int CEF_CALLBACK dialog_handler_on_file_dialog( int CEF_CALLBACK dialog_handler_on_file_dialog(
struct _cef_dialog_handler_t* self, cef_browser_t* browser, struct _cef_dialog_handler_t* self, cef_browser_t* browser,
cef_file_dialog_mode_t mode, const cef_string_t* title, cef_file_dialog_mode_t mode, const cef_string_t* title,
const cef_string_t* default_file_name, cef_string_list_t accept_types, const cef_string_t* default_file_path, cef_string_list_t accept_filters,
cef_file_dialog_callback_t* callback) { int selected_accept_filter, cef_file_dialog_callback_t* callback) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self); DCHECK(self);
@ -32,23 +32,28 @@ int CEF_CALLBACK dialog_handler_on_file_dialog(
DCHECK(browser); DCHECK(browser);
if (!browser) if (!browser)
return 0; return 0;
// Verify param: selected_accept_filter; type: simple_byval
DCHECK_GE(selected_accept_filter, 0);
if (selected_accept_filter < 0)
return 0;
// Verify param: callback; type: refptr_diff // Verify param: callback; type: refptr_diff
DCHECK(callback); DCHECK(callback);
if (!callback) if (!callback)
return 0; return 0;
// Unverified params: title, default_file_name, accept_types // Unverified params: title, default_file_path, accept_filters
// Translate param: accept_types; type: string_vec_byref_const // Translate param: accept_filters; type: string_vec_byref_const
std::vector<CefString> accept_typesList; std::vector<CefString> accept_filtersList;
transfer_string_list_contents(accept_types, accept_typesList); transfer_string_list_contents(accept_filters, accept_filtersList);
// Execute // Execute
bool _retval = CefDialogHandlerCppToC::Get(self)->OnFileDialog( bool _retval = CefDialogHandlerCppToC::Get(self)->OnFileDialog(
CefBrowserCToCpp::Wrap(browser), CefBrowserCToCpp::Wrap(browser),
mode, mode,
CefString(title), CefString(title),
CefString(default_file_name), CefString(default_file_path),
accept_typesList, accept_filtersList,
selected_accept_filter,
CefFileDialogCallbackCToCpp::Wrap(callback)); CefFileDialogCallbackCToCpp::Wrap(callback));
// Return type: bool // Return type: bool

View File

@ -17,16 +17,18 @@
// MEMBER FUNCTIONS - Body may be edited by hand. // MEMBER FUNCTIONS - Body may be edited by hand.
void CEF_CALLBACK file_dialog_callback_cont( void CEF_CALLBACK file_dialog_callback_cont(
struct _cef_file_dialog_callback_t* self, cef_string_list_t file_paths) { struct _cef_file_dialog_callback_t* self, int selected_accept_filter,
cef_string_list_t file_paths) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self); DCHECK(self);
if (!self) if (!self)
return; return;
// Verify param: file_paths; type: string_vec_byref_const // Verify param: selected_accept_filter; type: simple_byval
DCHECK(file_paths); DCHECK_GE(selected_accept_filter, 0);
if (!file_paths) if (selected_accept_filter < 0)
return; return;
// Unverified params: file_paths
// Translate param: file_paths; type: string_vec_byref_const // Translate param: file_paths; type: string_vec_byref_const
std::vector<CefString> file_pathsList; std::vector<CefString> file_pathsList;
@ -34,6 +36,7 @@ void CEF_CALLBACK file_dialog_callback_cont(
// Execute // Execute
CefFileDialogCallbackCppToC::Get(self)->Continue( CefFileDialogCallbackCppToC::Get(self)->Continue(
selected_accept_filter,
file_pathsList); file_pathsList);
} }

View File

@ -11,28 +11,24 @@
// //
#include "libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.h" #include "libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.h"
#include "libcef_dll/ctocpp/browser_host_ctocpp.h"
#include "libcef_dll/transfer_util.h" #include "libcef_dll/transfer_util.h"
// MEMBER FUNCTIONS - Body may be edited by hand. // MEMBER FUNCTIONS - Body may be edited by hand.
void CEF_CALLBACK run_file_dialog_callback_cont( void CEF_CALLBACK run_file_dialog_callback_on_file_dialog_dismissed(
struct _cef_run_file_dialog_callback_t* self, struct _cef_run_file_dialog_callback_t* self, int selected_accept_filter,
struct _cef_browser_host_t* browser_host, cef_string_list_t file_paths) { cef_string_list_t file_paths) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self); DCHECK(self);
if (!self) if (!self)
return; return;
// Verify param: browser_host; type: refptr_diff // Verify param: selected_accept_filter; type: simple_byval
DCHECK(browser_host); DCHECK_GE(selected_accept_filter, 0);
if (!browser_host) if (selected_accept_filter < 0)
return;
// Verify param: file_paths; type: string_vec_byref_const
DCHECK(file_paths);
if (!file_paths)
return; return;
// Unverified params: file_paths
// Translate param: file_paths; type: string_vec_byref_const // Translate param: file_paths; type: string_vec_byref_const
std::vector<CefString> file_pathsList; std::vector<CefString> file_pathsList;
@ -40,7 +36,7 @@ void CEF_CALLBACK run_file_dialog_callback_cont(
// Execute // Execute
CefRunFileDialogCallbackCppToC::Get(self)->OnFileDialogDismissed( CefRunFileDialogCallbackCppToC::Get(self)->OnFileDialogDismissed(
CefBrowserHostCToCpp::Wrap(browser_host), selected_accept_filter,
file_pathsList); file_pathsList);
} }
@ -51,7 +47,8 @@ CefRunFileDialogCallbackCppToC::CefRunFileDialogCallbackCppToC(
CefRunFileDialogCallback* cls) CefRunFileDialogCallback* cls)
: CefCppToC<CefRunFileDialogCallbackCppToC, CefRunFileDialogCallback, : CefCppToC<CefRunFileDialogCallbackCppToC, CefRunFileDialogCallback,
cef_run_file_dialog_callback_t>(cls) { cef_run_file_dialog_callback_t>(cls) {
struct_.struct_.cont = run_file_dialog_callback_cont; struct_.struct_.on_file_dialog_dismissed =
run_file_dialog_callback_on_file_dialog_dismissed;
} }
#ifndef NDEBUG #ifndef NDEBUG

View File

@ -188,37 +188,42 @@ void CefBrowserHostCToCpp::SetZoomLevel(double zoomLevel) {
} }
void CefBrowserHostCToCpp::RunFileDialog(FileDialogMode mode, void CefBrowserHostCToCpp::RunFileDialog(FileDialogMode mode,
const CefString& title, const CefString& default_file_name, const CefString& title, const CefString& default_file_path,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters, int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) { CefRefPtr<CefRunFileDialogCallback> callback) {
if (CEF_MEMBER_MISSING(struct_, run_file_dialog)) if (CEF_MEMBER_MISSING(struct_, run_file_dialog))
return; return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: selected_accept_filter; type: simple_byval
DCHECK_GE(selected_accept_filter, 0);
if (selected_accept_filter < 0)
return;
// Verify param: callback; type: refptr_diff // Verify param: callback; type: refptr_diff
DCHECK(callback.get()); DCHECK(callback.get());
if (!callback.get()) if (!callback.get())
return; return;
// Unverified params: title, default_file_name, accept_types // Unverified params: title, default_file_path, accept_filters
// Translate param: accept_types; type: string_vec_byref_const // Translate param: accept_filters; type: string_vec_byref_const
cef_string_list_t accept_typesList = cef_string_list_alloc(); cef_string_list_t accept_filtersList = cef_string_list_alloc();
DCHECK(accept_typesList); DCHECK(accept_filtersList);
if (accept_typesList) if (accept_filtersList)
transfer_string_list_contents(accept_types, accept_typesList); transfer_string_list_contents(accept_filters, accept_filtersList);
// Execute // Execute
struct_->run_file_dialog(struct_, struct_->run_file_dialog(struct_,
mode, mode,
title.GetStruct(), title.GetStruct(),
default_file_name.GetStruct(), default_file_path.GetStruct(),
accept_typesList, accept_filtersList,
selected_accept_filter,
CefRunFileDialogCallbackCppToC::Wrap(callback)); CefRunFileDialogCallbackCppToC::Wrap(callback));
// Restore param:accept_types; type: string_vec_byref_const // Restore param:accept_filters; type: string_vec_byref_const
if (accept_typesList) if (accept_filtersList)
cef_string_list_free(accept_typesList); cef_string_list_free(accept_filtersList);
} }
void CefBrowserHostCToCpp::StartDownload(const CefString& url) { void CefBrowserHostCToCpp::StartDownload(const CefString& url) {

View File

@ -47,8 +47,8 @@ class CefBrowserHostCToCpp
virtual double GetZoomLevel() OVERRIDE; virtual double GetZoomLevel() OVERRIDE;
virtual void SetZoomLevel(double zoomLevel) OVERRIDE; virtual void SetZoomLevel(double zoomLevel) OVERRIDE;
virtual void RunFileDialog(FileDialogMode mode, const CefString& title, virtual void RunFileDialog(FileDialogMode mode, const CefString& title,
const CefString& default_file_name, const CefString& default_file_path,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters, int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) OVERRIDE; CefRefPtr<CefRunFileDialogCallback> callback) OVERRIDE;
virtual void StartDownload(const CefString& url) OVERRIDE; virtual void StartDownload(const CefString& url) OVERRIDE;
virtual void Print() OVERRIDE; virtual void Print() OVERRIDE;

View File

@ -20,8 +20,8 @@
bool CefDialogHandlerCToCpp::OnFileDialog(CefRefPtr<CefBrowser> browser, bool CefDialogHandlerCToCpp::OnFileDialog(CefRefPtr<CefBrowser> browser,
FileDialogMode mode, const CefString& title, FileDialogMode mode, const CefString& title,
const CefString& default_file_name, const CefString& default_file_path,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters, int selected_accept_filter,
CefRefPtr<CefFileDialogCallback> callback) { CefRefPtr<CefFileDialogCallback> callback) {
if (CEF_MEMBER_MISSING(struct_, on_file_dialog)) if (CEF_MEMBER_MISSING(struct_, on_file_dialog))
return false; return false;
@ -32,30 +32,35 @@ bool CefDialogHandlerCToCpp::OnFileDialog(CefRefPtr<CefBrowser> browser,
DCHECK(browser.get()); DCHECK(browser.get());
if (!browser.get()) if (!browser.get())
return false; return false;
// Verify param: selected_accept_filter; type: simple_byval
DCHECK_GE(selected_accept_filter, 0);
if (selected_accept_filter < 0)
return false;
// Verify param: callback; type: refptr_diff // Verify param: callback; type: refptr_diff
DCHECK(callback.get()); DCHECK(callback.get());
if (!callback.get()) if (!callback.get())
return false; return false;
// Unverified params: title, default_file_name, accept_types // Unverified params: title, default_file_path, accept_filters
// Translate param: accept_types; type: string_vec_byref_const // Translate param: accept_filters; type: string_vec_byref_const
cef_string_list_t accept_typesList = cef_string_list_alloc(); cef_string_list_t accept_filtersList = cef_string_list_alloc();
DCHECK(accept_typesList); DCHECK(accept_filtersList);
if (accept_typesList) if (accept_filtersList)
transfer_string_list_contents(accept_types, accept_typesList); transfer_string_list_contents(accept_filters, accept_filtersList);
// Execute // Execute
int _retval = struct_->on_file_dialog(struct_, int _retval = struct_->on_file_dialog(struct_,
CefBrowserCppToC::Wrap(browser), CefBrowserCppToC::Wrap(browser),
mode, mode,
title.GetStruct(), title.GetStruct(),
default_file_name.GetStruct(), default_file_path.GetStruct(),
accept_typesList, accept_filtersList,
selected_accept_filter,
CefFileDialogCallbackCppToC::Wrap(callback)); CefFileDialogCallbackCppToC::Wrap(callback));
// Restore param:accept_types; type: string_vec_byref_const // Restore param:accept_filters; type: string_vec_byref_const
if (accept_typesList) if (accept_filtersList)
cef_string_list_free(accept_typesList); cef_string_list_free(accept_filtersList);
// Return type: bool // Return type: bool
return _retval?true:false; return _retval?true:false;

View File

@ -35,8 +35,8 @@ class CefDialogHandlerCToCpp
// CefDialogHandler methods // CefDialogHandler methods
bool OnFileDialog(CefRefPtr<CefBrowser> browser, FileDialogMode mode, bool OnFileDialog(CefRefPtr<CefBrowser> browser, FileDialogMode mode,
const CefString& title, const CefString& default_file_name, const CefString& title, const CefString& default_file_path,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters, int selected_accept_filter,
CefRefPtr<CefFileDialogCallback> callback) override; CefRefPtr<CefFileDialogCallback> callback) override;
}; };

View File

@ -16,13 +16,19 @@
// VIRTUAL METHODS - Body may be edited by hand. // VIRTUAL METHODS - Body may be edited by hand.
void CefFileDialogCallbackCToCpp::Continue( void CefFileDialogCallbackCToCpp::Continue(int selected_accept_filter,
const std::vector<CefString>& file_paths) { const std::vector<CefString>& file_paths) {
if (CEF_MEMBER_MISSING(struct_, cont)) if (CEF_MEMBER_MISSING(struct_, cont))
return; return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: selected_accept_filter; type: simple_byval
DCHECK_GE(selected_accept_filter, 0);
if (selected_accept_filter < 0)
return;
// Unverified params: file_paths
// Translate param: file_paths; type: string_vec_byref_const // Translate param: file_paths; type: string_vec_byref_const
cef_string_list_t file_pathsList = cef_string_list_alloc(); cef_string_list_t file_pathsList = cef_string_list_alloc();
DCHECK(file_pathsList); DCHECK(file_pathsList);
@ -31,6 +37,7 @@ void CefFileDialogCallbackCToCpp::Continue(
// Execute // Execute
struct_->cont(struct_, struct_->cont(struct_,
selected_accept_filter,
file_pathsList); file_pathsList);
// Restore param:file_paths; type: string_vec_byref_const // Restore param:file_paths; type: string_vec_byref_const

View File

@ -34,7 +34,8 @@ class CefFileDialogCallbackCToCpp
cef_file_dialog_callback_t>(str) {} cef_file_dialog_callback_t>(str) {}
// CefFileDialogCallback methods // CefFileDialogCallback methods
virtual void Continue(const std::vector<CefString>& file_paths) OVERRIDE; virtual void Continue(int selected_accept_filter,
const std::vector<CefString>& file_paths) OVERRIDE;
virtual void Cancel() OVERRIDE; virtual void Cancel() OVERRIDE;
}; };

View File

@ -10,7 +10,6 @@
// for more information. // for more information.
// //
#include "libcef_dll/cpptoc/browser_host_cpptoc.h"
#include "libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.h" #include "libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.h"
#include "libcef_dll/transfer_util.h" #include "libcef_dll/transfer_util.h"
@ -18,17 +17,17 @@
// VIRTUAL METHODS - Body may be edited by hand. // VIRTUAL METHODS - Body may be edited by hand.
void CefRunFileDialogCallbackCToCpp::OnFileDialogDismissed( void CefRunFileDialogCallbackCToCpp::OnFileDialogDismissed(
CefRefPtr<CefBrowserHost> browser_host, int selected_accept_filter, const std::vector<CefString>& file_paths) {
const std::vector<CefString>& file_paths) { if (CEF_MEMBER_MISSING(struct_, on_file_dialog_dismissed))
if (CEF_MEMBER_MISSING(struct_, cont))
return; return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser_host; type: refptr_diff // Verify param: selected_accept_filter; type: simple_byval
DCHECK(browser_host.get()); DCHECK_GE(selected_accept_filter, 0);
if (!browser_host.get()) if (selected_accept_filter < 0)
return; return;
// Unverified params: file_paths
// Translate param: file_paths; type: string_vec_byref_const // Translate param: file_paths; type: string_vec_byref_const
cef_string_list_t file_pathsList = cef_string_list_alloc(); cef_string_list_t file_pathsList = cef_string_list_alloc();
@ -37,8 +36,8 @@ void CefRunFileDialogCallbackCToCpp::OnFileDialogDismissed(
transfer_string_list_contents(file_paths, file_pathsList); transfer_string_list_contents(file_paths, file_pathsList);
// Execute // Execute
struct_->cont(struct_, struct_->on_file_dialog_dismissed(struct_,
CefBrowserHostCppToC::Wrap(browser_host), selected_accept_filter,
file_pathsList); file_pathsList);
// Restore param:file_paths; type: string_vec_byref_const // Restore param:file_paths; type: string_vec_byref_const

View File

@ -36,7 +36,7 @@ class CefRunFileDialogCallbackCToCpp
cef_run_file_dialog_callback_t>(str) {} cef_run_file_dialog_callback_t>(str) {}
// CefRunFileDialogCallback methods // CefRunFileDialogCallback methods
void OnFileDialogDismissed(CefRefPtr<CefBrowserHost> browser_host, void OnFileDialogDismissed(int selected_accept_filter,
const std::vector<CefString>& file_paths) override; const std::vector<CefString>& file_paths) override;
}; };

View File

@ -195,8 +195,9 @@ bool ClientHandler::OnContextMenuCommand(
bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser, bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser,
FileDialogMode mode, FileDialogMode mode,
const CefString& title, const CefString& title,
const CefString& default_file_name, const CefString& default_file_path,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefFileDialogCallback> callback) { CefRefPtr<CefFileDialogCallback> callback) {
CEF_REQUIRE_UI_THREAD(); CEF_REQUIRE_UI_THREAD();
@ -841,12 +842,16 @@ void ClientHandler::EndTracing() {
// Results in a call to OnFileDialogDismissed. // Results in a call to OnFileDialogDismissed.
handler_->GetBrowser()->GetHost()->RunFileDialog( handler_->GetBrowser()->GetHost()->RunFileDialog(
FILE_DIALOG_SAVE, CefString(), path, std::vector<CefString>(), FILE_DIALOG_SAVE,
CefString(), // title
path,
std::vector<CefString>(), // accept_filters
0, // selected_accept_filter
this); this);
} }
virtual void OnFileDialogDismissed( virtual void OnFileDialogDismissed(
CefRefPtr<CefBrowserHost> browser_host, int selected_accept_filter,
const std::vector<CefString>& file_paths) OVERRIDE { const std::vector<CefString>& file_paths) OVERRIDE {
CEF_REQUIRE_UI_THREAD(); CEF_REQUIRE_UI_THREAD();
if (!file_paths.empty()) { if (!file_paths.empty()) {

View File

@ -111,8 +111,9 @@ class ClientHandler : public CefClient,
virtual bool OnFileDialog(CefRefPtr<CefBrowser> browser, virtual bool OnFileDialog(CefRefPtr<CefBrowser> browser,
FileDialogMode mode, FileDialogMode mode,
const CefString& title, const CefString& title,
const CefString& default_file_name, const CefString& default_file_path,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefFileDialogCallback> callback) OVERRIDE; CefRefPtr<CefFileDialogCallback> callback) OVERRIDE;
// CefDisplayHandler methods // CefDisplayHandler methods

View File

@ -50,52 +50,74 @@ std::string GetDescriptionFromMimeType(const std::string& mime_type) {
return std::string(); return std::string();
} }
void AddFiltersForAcceptTypes(GtkFileChooser* chooser, void AddFilters(GtkFileChooser* chooser,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters,
bool include_all_files) { bool include_all_files,
std::vector<GtkFileFilter*>* filters) {
bool has_filter = false; bool has_filter = false;
for (size_t i = 0; i < accept_types.size(); ++i) { for (size_t i = 0; i < accept_filters.size(); ++i) {
std::string ascii_type = accept_types[i]; const std::string& filter = accept_filters[i];
if (ascii_type.length()) { if (filter.empty())
// Just treat as extension if contains '.' as the first character. continue;
if (ascii_type[0] == '.') {
GtkFileFilter* filter = gtk_file_filter_new();
std::string pattern = "*" + ascii_type;
gtk_file_filter_add_pattern(filter, pattern.c_str());
gtk_file_filter_set_name(filter, pattern.c_str());
gtk_file_chooser_add_filter(chooser, filter);
if (!has_filter)
has_filter = true;
} else {
// Otherwise convert mime type to one or more extensions.
GtkFileFilter* filter = NULL;
std::string description = GetDescriptionFromMimeType(ascii_type);
bool description_from_ext = description.empty();
std::vector<CefString> ext; std::vector<std::string> extensions;
CefGetExtensionsForMimeType(ascii_type, ext); std::string description;
for (size_t x = 0; x < ext.size(); ++x) {
if (!filter)
filter = gtk_file_filter_new();
std::string pattern = "*." + ext[x].ToString();
gtk_file_filter_add_pattern(filter, pattern.c_str());
if (description_from_ext) { size_t sep_index = filter.find('|');
if (x != 0) if (sep_index != std::string::npos) {
description += ";"; // Treat as a filter of the form "Filter Name|.ext1;.ext2;.ext3".
description += pattern; description = filter.substr(0, sep_index);
}
}
if (filter) { const std::string& exts = filter.substr(sep_index + 1);
gtk_file_filter_set_name(filter, description.c_str()); size_t last = 0;
gtk_file_chooser_add_filter(chooser, filter); size_t size = exts.size();
if (!has_filter) for (size_t i = 0; i <= size; ++i) {
has_filter = true; if (i == size || exts[i] == ';') {
std::string ext(exts, last, i - last);
if (!ext.empty() && ext[0] == '.')
extensions.push_back(ext);
last = i + 1;
} }
} }
} else if (filter[0] == '.') {
// Treat as an extension beginning with the '.' character.
extensions.push_back(filter);
} else {
// Otherwise convert mime type to one or more extensions.
description = GetDescriptionFromMimeType(filter);
std::vector<CefString> ext;
CefGetExtensionsForMimeType(filter, ext);
for (size_t x = 0; x < ext.size(); ++x)
extensions.push_back("." + ext[x].ToString());
} }
if (extensions.empty())
continue;
GtkFileFilter* gtk_filter = gtk_file_filter_new();
std::string ext_str;
for (size_t x = 0; x < extensions.size(); ++x) {
const std::string& pattern = "*" + extensions[x];
if (x != 0)
ext_str += ";";
ext_str += pattern;
gtk_file_filter_add_pattern(gtk_filter, pattern.c_str());
}
if (description.empty())
description = ext_str;
else
description += " (" + ext_str + ")";
gtk_file_filter_set_name(gtk_filter, description.c_str());
gtk_file_chooser_add_filter(chooser, gtk_filter);
if (!has_filter)
has_filter = true;
filters->push_back(gtk_filter);
} }
// Add the *.* filter, but only if we have added other filters (otherwise it // Add the *.* filter, but only if we have added other filters (otherwise it
@ -103,7 +125,7 @@ void AddFiltersForAcceptTypes(GtkFileChooser* chooser,
if (include_all_files && has_filter) { if (include_all_files && has_filter) {
GtkFileFilter* filter = gtk_file_filter_new(); GtkFileFilter* filter = gtk_file_filter_new();
gtk_file_filter_add_pattern(filter, "*"); gtk_file_filter_add_pattern(filter, "*");
gtk_file_filter_set_name(filter, "All Files"); gtk_file_filter_set_name(filter, "All Files (*)");
gtk_file_chooser_add_filter(chooser, filter); gtk_file_chooser_add_filter(chooser, filter);
} }
} }
@ -113,17 +135,26 @@ void AddFiltersForAcceptTypes(GtkFileChooser* chooser,
bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser, bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser,
FileDialogMode mode, FileDialogMode mode,
const CefString& title, const CefString& title,
const CefString& default_file_name, const CefString& default_file_path,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefFileDialogCallback> callback) { CefRefPtr<CefFileDialogCallback> callback) {
std::vector<CefString> files; std::vector<CefString> files;
GtkFileChooserAction action; GtkFileChooserAction action;
const gchar* accept_button; const gchar* accept_button;
if (mode == FILE_DIALOG_OPEN || mode == FILE_DIALOG_OPEN_MULTIPLE) {
// Remove any modifier flags.
FileDialogMode mode_type =
static_cast<FileDialogMode>(mode & FILE_DIALOG_TYPE_MASK);
if (mode_type == FILE_DIALOG_OPEN || mode_type == FILE_DIALOG_OPEN_MULTIPLE) {
action = GTK_FILE_CHOOSER_ACTION_OPEN; action = GTK_FILE_CHOOSER_ACTION_OPEN;
accept_button = GTK_STOCK_OPEN; accept_button = GTK_STOCK_OPEN;
} else if (mode == FILE_DIALOG_SAVE) { } else if (mode_type == FILE_DIALOG_OPEN_FOLDER) {
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
accept_button = GTK_STOCK_OPEN;
} else if (mode_type == FILE_DIALOG_SAVE) {
action = GTK_FILE_CHOOSER_ACTION_SAVE; action = GTK_FILE_CHOOSER_ACTION_SAVE;
accept_button = GTK_STOCK_SAVE; accept_button = GTK_STOCK_SAVE;
} else { } else {
@ -131,12 +162,6 @@ bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser,
return false; return false;
} }
std::string base_name;
if (!default_file_name.empty()) {
base_name =
basename(const_cast<char*>(default_file_name.ToString().c_str()));
}
std::string title_str; std::string title_str;
if (!title.empty()) { if (!title.empty()) {
title_str = title; title_str = title;
@ -148,6 +173,9 @@ bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser,
case FILE_DIALOG_OPEN_MULTIPLE: case FILE_DIALOG_OPEN_MULTIPLE:
title_str = "Open Files"; title_str = "Open Files";
break; break;
case FILE_DIALOG_OPEN_FOLDER:
title_str = "Open Folder";
break;
case FILE_DIALOG_SAVE: case FILE_DIALOG_SAVE:
title_str = "Save File"; title_str = "Save File";
break; break;
@ -166,28 +194,39 @@ bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser,
accept_button, GTK_RESPONSE_ACCEPT, accept_button, GTK_RESPONSE_ACCEPT,
NULL); NULL);
if (mode == FILE_DIALOG_OPEN_MULTIPLE) { if (mode_type == FILE_DIALOG_OPEN_MULTIPLE)
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
} else if (mode == FILE_DIALOG_SAVE) {
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), if (mode_type == FILE_DIALOG_SAVE) {
TRUE); gtk_file_chooser_set_do_overwrite_confirmation(
GTK_FILE_CHOOSER(dialog),
!!(mode & FILE_DIALOG_OVERWRITEPROMPT_FLAG));
} }
if (mode == FILE_DIALOG_SAVE && !base_name.empty()) { gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog),
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), !(mode & FILE_DIALOG_HIDEREADONLY_FLAG));
base_name.c_str());
if (!default_file_path.empty()) {
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
default_file_path.ToString().c_str());
} }
AddFiltersForAcceptTypes(GTK_FILE_CHOOSER(dialog), accept_types, true); std::vector<GtkFileFilter*> filters;
AddFilters(GTK_FILE_CHOOSER(dialog), accept_filters, true, &filters);
if (selected_accept_filter < static_cast<int>(filters.size())) {
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog),
filters[selected_accept_filter]);
}
bool success = false; bool success = false;
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
if (mode == FILE_DIALOG_OPEN || mode == FILE_DIALOG_SAVE) { if (mode_type == FILE_DIALOG_OPEN || mode_type == FILE_DIALOG_OPEN_FOLDER ||
mode_type == FILE_DIALOG_SAVE) {
char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
files.push_back(std::string(filename)); files.push_back(std::string(filename));
success = true; success = true;
} else if (mode == FILE_DIALOG_OPEN_MULTIPLE) { } else if (mode_type == FILE_DIALOG_OPEN_MULTIPLE) {
GSList* filenames = GSList* filenames =
gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
if (filenames) { if (filenames) {
@ -203,10 +242,24 @@ bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser,
} }
} }
int filter_index = selected_accept_filter;
if (success) {
GtkFileFilter* selected_filter =
gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog));
if (selected_filter != NULL) {
for (size_t x = 0; x < filters.size(); ++x) {
if (filters[x] == selected_filter) {
filter_index = x;
break;
}
}
}
}
gtk_widget_destroy(dialog); gtk_widget_destroy(dialog);
if (success) if (success)
callback->Continue(files); callback->Continue(filter_index, files);
else else
callback->Cancel(); callback->Cancel();

View File

@ -14,19 +14,63 @@ namespace {
const char kTestUrl[] = "http://tests/dialogs"; const char kTestUrl[] = "http://tests/dialogs";
const char kFileOpenMessageName[] = "DialogTest.FileOpen"; const char kFileOpenMessageName[] = "DialogTest.FileOpen";
const char kFileOpenMultipleMessageName[] = "DialogTest.FileOpenMultiple"; const char kFileOpenMultipleMessageName[] = "DialogTest.FileOpenMultiple";
const char kFileOpenFolderMessageName[] = "DialogTest.FileOpenFolder";
const char kFileSaveMessageName[] = "DialogTest.FileSave"; const char kFileSaveMessageName[] = "DialogTest.FileSave";
#if defined(OS_WIN)
#define PATH_SEP '\\'
#else
#define PATH_SEP '/'
#endif
// Store persistent dialog state information.
class DialogState : public base::RefCountedThreadSafe<DialogState> {
public:
DialogState()
: mode_(FILE_DIALOG_OPEN),
last_selected_filter_(0),
pending_(false) {}
cef_file_dialog_mode_t mode_;
int last_selected_filter_;
CefString last_file_;
bool pending_;
DISALLOW_COPY_AND_ASSIGN(DialogState);
};
// Callback executed when the file dialog is dismissed. // Callback executed when the file dialog is dismissed.
class DialogCallback : public CefRunFileDialogCallback { class DialogCallback : public CefRunFileDialogCallback {
public: public:
explicit DialogCallback( DialogCallback(
CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback) CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback,
: router_callback_(router_callback) { scoped_refptr<DialogState> dialog_state)
: router_callback_(router_callback),
dialog_state_(dialog_state) {
} }
virtual void OnFileDialogDismissed( virtual void OnFileDialogDismissed(
CefRefPtr<CefBrowserHost> browser_host, int last_selected_filter,
const std::vector<CefString>& file_paths) OVERRIDE { const std::vector<CefString>& file_paths) OVERRIDE {
CEF_REQUIRE_UI_THREAD();
DCHECK(dialog_state_->pending_);
if (!file_paths.empty()) {
if (dialog_state_->mode_ != FILE_DIALOG_OPEN_FOLDER)
dialog_state_->last_selected_filter_ = last_selected_filter;
dialog_state_->last_file_ = file_paths[0];
if (dialog_state_->mode_ == FILE_DIALOG_OPEN_FOLDER) {
std::string last_file = dialog_state_->last_file_;
if (last_file[last_file.length() - 1] != PATH_SEP) {
// Add a trailing slash so we know it's a directory. Otherwise, file
// dialogs will think the last path component is a file name.
last_file += PATH_SEP;
dialog_state_->last_file_ = last_file;
}
}
}
// Send a message back to the render process with the list of file paths. // Send a message back to the render process with the list of file paths.
std::string response; std::string response;
for (int i = 0; i < static_cast<int>(file_paths.size()); ++i) { for (int i = 0; i < static_cast<int>(file_paths.size()); ++i) {
@ -37,12 +81,17 @@ class DialogCallback : public CefRunFileDialogCallback {
router_callback_->Success(response); router_callback_->Success(response);
router_callback_ = NULL; router_callback_ = NULL;
dialog_state_->pending_ = false;
dialog_state_ = NULL;
} }
private: private:
CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback_; CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback_;
scoped_refptr<DialogState> dialog_state_;
IMPLEMENT_REFCOUNTING(DialogCallback); IMPLEMENT_REFCOUNTING(DialogCallback);
DISALLOW_COPY_AND_ASSIGN(DialogCallback);
}; };
// Handle messages in the browser process. // Handle messages in the browser process.
@ -57,36 +106,72 @@ class Handler : public CefMessageRouterBrowserSide::Handler {
const CefString& request, const CefString& request,
bool persistent, bool persistent,
CefRefPtr<Callback> callback) OVERRIDE { CefRefPtr<Callback> callback) OVERRIDE {
CEF_REQUIRE_UI_THREAD();
// Only handle messages from the test URL. // Only handle messages from the test URL.
const std::string& url = frame->GetURL(); const std::string& url = frame->GetURL();
if (url.find(kTestUrl) != 0) if (url.find(kTestUrl) != 0)
return false; return false;
// Sample file type filter. if (!dialog_state_.get())
std::vector<CefString> file_types; dialog_state_ = new DialogState;
file_types.push_back("text/*");
file_types.push_back(".log");
file_types.push_back(".patch");
CefRefPtr<DialogCallback> dialog_callback(new DialogCallback(callback)); // Make sure we're only running one dialog at a time.
DCHECK(!dialog_state_->pending_);
std::vector<CefString> accept_filters;
std::string title;
const std::string& message_name = request; const std::string& message_name = request;
if (message_name == kFileOpenMessageName) { if (message_name == kFileOpenMessageName) {
browser->GetHost()->RunFileDialog(FILE_DIALOG_OPEN, "My Open Dialog", dialog_state_->mode_ = FILE_DIALOG_OPEN;
"test.txt", file_types, dialog_callback.get()); title = "My Open Dialog";
} else if (message_name == kFileOpenMultipleMessageName) { } else if (message_name == kFileOpenMultipleMessageName) {
browser->GetHost()->RunFileDialog(FILE_DIALOG_OPEN_MULTIPLE, dialog_state_->mode_ = FILE_DIALOG_OPEN_MULTIPLE;
"My Open Multiple Dialog", CefString(), file_types, title = "My Open Multiple Dialog";
dialog_callback.get()); } else if (message_name == kFileOpenFolderMessageName) {
dialog_state_->mode_ = FILE_DIALOG_OPEN_FOLDER;
title = "My Open Folder Dialog";
} else if (message_name == kFileSaveMessageName) { } else if (message_name == kFileSaveMessageName) {
browser->GetHost()->RunFileDialog(FILE_DIALOG_SAVE, "My Save Dialog", dialog_state_->mode_ = static_cast<cef_file_dialog_mode_t>(
"test.txt", file_types, dialog_callback.get()); FILE_DIALOG_SAVE | FILE_DIALOG_OVERWRITEPROMPT_FLAG);
title = "My Save Dialog";
} else { } else {
NOTREACHED(); NOTREACHED();
return true;
} }
if (dialog_state_->mode_ != FILE_DIALOG_OPEN_FOLDER) {
// Build filters based on mime time.
accept_filters.push_back("text/*");
// Build filters based on file extension.
accept_filters.push_back(".log");
accept_filters.push_back(".patch");
// Add specific filters as-is.
accept_filters.push_back("Document Files|.doc;.odt");
accept_filters.push_back("Image Files|.png;.jpg;.gif");
accept_filters.push_back("PDF Files|.pdf");
}
dialog_state_->pending_ = true;
browser->GetHost()->RunFileDialog(
dialog_state_->mode_,
title,
dialog_state_->last_file_,
accept_filters,
dialog_state_->last_selected_filter_,
new DialogCallback(callback, dialog_state_));
return true; return true;
} }
private:
scoped_refptr<DialogState> dialog_state_;
DISALLOW_COPY_AND_ASSIGN(Handler);
}; };
} // namespace } // namespace

View File

@ -57,6 +57,7 @@ Click a button to show the associated dialog type.
<br/>input type="file": <input type="file" name="pic" accept="text/*,.js,.css,image/*"> <br/>input type="file": <input type="file" name="pic" accept="text/*,.js,.css,image/*">
<br/><input type="button" onclick="show_file_dialog('fo', 'FileOpen');" value="Show File Open"> <span id="fo"></span> <br/><input type="button" onclick="show_file_dialog('fo', 'FileOpen');" value="Show File Open"> <span id="fo"></span>
<br/><input type="button" onclick="show_file_dialog('fom', 'FileOpenMultiple');" value="Show File Open Multiple"> <span id="fom"></span> <br/><input type="button" onclick="show_file_dialog('fom', 'FileOpenMultiple');" value="Show File Open Multiple"> <span id="fom"></span>
<br/><input type="button" onclick="show_file_dialog('fof', 'FileOpenFolder');" value="Show File Open Folder"> <span id="fof"></span>
<br/><input type="button" onclick="show_file_dialog('fs', 'FileSave');" value="Show File Save"> <span id="fs"></span> <br/><input type="button" onclick="show_file_dialog('fs', 'FileSave');" value="Show File Save"> <span id="fs"></span>
<p id="time"></p> <p id="time"></p>
</form> </form>

View File

@ -22,6 +22,7 @@ class DialogTestHandler : public TestHandler {
: mode(dialog_mode), : mode(dialog_mode),
title("Test Title"), title("Test Title"),
default_file_name("Test File Name"), default_file_name("Test File Name"),
selected_accept_filter(1), // Something other than 0 for testing.
callback_async(false), callback_async(false),
callback_cancel(false) { callback_cancel(false) {
accept_types.push_back("text/*"); accept_types.push_back("text/*");
@ -33,6 +34,7 @@ class DialogTestHandler : public TestHandler {
CefString title; CefString title;
CefString default_file_name; CefString default_file_name;
std::vector<CefString> accept_types; std::vector<CefString> accept_types;
int selected_accept_filter;
bool callback_async; // True if the callback should execute asynchronously. bool callback_async; // True if the callback should execute asynchronously.
bool callback_cancel; // True if the callback should cancel. bool callback_cancel; // True if the callback should cancel.
@ -46,17 +48,18 @@ class DialogTestHandler : public TestHandler {
} }
void OnFileDialogDismissed( void OnFileDialogDismissed(
CefRefPtr<CefBrowserHost> browser_host, int selected_accept_filter,
const std::vector<CefString>& file_paths) override { const std::vector<CefString>& file_paths) override {
handler_->got_onfiledialogdismissed_.yes(); handler_->got_onfiledialogdismissed_.yes();
std::string url = browser_host->GetBrowser()->GetMainFrame()->GetURL(); if (handler_->config_.callback_cancel) {
EXPECT_STREQ(kTestUrl, url.c_str()); EXPECT_EQ(0, selected_accept_filter);
if (handler_->config_.callback_cancel)
EXPECT_TRUE(file_paths.empty()); EXPECT_TRUE(file_paths.empty());
else } else {
EXPECT_EQ(handler_->config_.selected_accept_filter,
selected_accept_filter);
TestStringVectorEqual(handler_->config_.callback_paths, file_paths); TestStringVectorEqual(handler_->config_.callback_paths, file_paths);
}
handler_->DestroyTest(); handler_->DestroyTest();
handler_ = NULL; handler_ = NULL;
@ -89,6 +92,7 @@ class DialogTestHandler : public TestHandler {
config_.title, config_.title,
config_.default_file_name, config_.default_file_name,
config_.accept_types, config_.accept_types,
config_.selected_accept_filter,
new Callback(this)); new Callback(this));
} }
@ -96,7 +100,8 @@ class DialogTestHandler : public TestHandler {
if (config_.callback_cancel) if (config_.callback_cancel)
callback->Cancel(); callback->Cancel();
else else
callback->Continue(config_.callback_paths); callback->Continue(config_.selected_accept_filter,
config_.callback_paths);
} }
// CefDialogHandler // CefDialogHandler
@ -106,6 +111,7 @@ class DialogTestHandler : public TestHandler {
const CefString& title, const CefString& title,
const CefString& default_file_name, const CefString& default_file_name,
const std::vector<CefString>& accept_types, const std::vector<CefString>& accept_types,
int selected_accept_filter,
CefRefPtr<CefFileDialogCallback> callback) override { CefRefPtr<CefFileDialogCallback> callback) override {
got_onfiledialog_.yes(); got_onfiledialog_.yes();
@ -157,6 +163,22 @@ TEST(DialogTest, FileEmptyParams) {
ReleaseAndWaitForDestructor(handler); ReleaseAndWaitForDestructor(handler);
} }
TEST(DialogTest, FileAdditionalFlags) {
DialogTestHandler::TestConfig config(
static_cast<cef_file_dialog_mode_t>(FILE_DIALOG_OPEN |
FILE_DIALOG_HIDEREADONLY_FLAG |
FILE_DIALOG_OVERWRITEPROMPT_FLAG));
config.title.clear();
config.default_file_name.clear();
config.accept_types.clear();
config.callback_async = false;
config.callback_cancel = false;
CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(DialogTest, FileOpen) { TEST(DialogTest, FileOpen) {
DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN); DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN);
config.callback_async = false; config.callback_async = false;
@ -243,6 +265,48 @@ TEST(DialogTest, FileOpenMultipleAsyncCancel) {
ReleaseAndWaitForDestructor(handler); ReleaseAndWaitForDestructor(handler);
} }
TEST(DialogTest, FileOpenFolder) {
DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_FOLDER);
config.callback_async = false;
config.callback_cancel = false;
config.callback_paths.push_back("/path/to/folder");
CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(DialogTest, FileOpenFolderCancel) {
DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_FOLDER);
config.callback_async = false;
config.callback_cancel = true;
CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(DialogTest, FileOpenFolderAsync) {
DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_FOLDER);
config.callback_async = true;
config.callback_cancel = false;
config.callback_paths.push_back("/path/to/folder");
CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(DialogTest, FileOpenFolderAsyncCancel) {
DialogTestHandler::TestConfig config(FILE_DIALOG_OPEN_FOLDER);
config.callback_async = false;
config.callback_cancel = true;
CefRefPtr<DialogTestHandler> handler = new DialogTestHandler(config);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
TEST(DialogTest, FileSave) { TEST(DialogTest, FileSave) {
DialogTestHandler::TestConfig config(FILE_DIALOG_SAVE); DialogTestHandler::TestConfig config(FILE_DIALOG_SAVE);
config.callback_async = false; config.callback_async = false;