- 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;
///
// Called asynchronously after the file dialog is dismissed. If the selection
// was successful |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.
// Called asynchronously after the file dialog is dismissed.
// |selected_accept_filter| is the 0-based index of the value selected from
// the accept filters array passed to cef_browser_host_t::RunFileDialog.
// |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,
struct _cef_browser_host_t* browser_host, cef_string_list_t file_paths);
void (CEF_CALLBACK *on_file_dialog_dismissed)(
struct _cef_run_file_dialog_callback_t* self, int selected_accept_filter,
cef_string_list_t file_paths);
} 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
// 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
// default title ("Open" or "Save" depending on the mode). |default_file_name|
// is the default file name to select in the dialog. |accept_types| is a list
// of valid lower-cased MIME types or file extensions specified in an input
// element and is used to restrict selectable files to such types. |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.
// default title ("Open" or "Save" depending on the mode). |default_file_path|
// is the path with optional directory and/or file name component that will be
// initially selected in the dialog. |accept_filters| are used to restrict the
// selectable file types and may any combination of (a) valid lower-cased MIME
// types (e.g. "text/*" or "image/*"), (b) individual file extensions (e.g.
// ".txt" or ".png"), or (c) combined 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 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,
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);
///

View File

@ -56,12 +56,14 @@ typedef struct _cef_file_dialog_callback_t {
cef_base_t base;
///
// Continue the file selection with the specified |file_paths|. This may be a
// single value or a list of values depending on the dialog mode. An NULL
// Continue the file selection. |selected_accept_filter| should be the 0-based
// 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().
///
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.
@ -84,17 +86,21 @@ typedef struct _cef_dialog_handler_t {
// 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 show the default title ("Open" or "Save" depending on the mode).
// |default_file_name| is the default file name to select in the dialog.
// |accept_types| is a list of valid lower-cased MIME types or file extensions
// specified in an input element and is used to restrict selectable files to
// such types. 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).
// |default_file_path| is the path with optional directory and/or file name
// component that should be initially selected in the dialog. |accept_filters|
// are used to restrict the selectable file types and may any combination of
// (a) valid lower-cased MIME types (e.g. "text/*" or "image/*"), (b)
// individual file extensions (e.g. ".txt" or ".png"), or (c) combined
// 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,
struct _cef_browser_t* browser, 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* title, const cef_string_t* default_file_path,
cef_string_list_t accept_filters, int selected_accept_filter,
struct _cef_file_dialog_callback_t* callback);
} cef_dialog_handler_t;

View File

@ -199,14 +199,15 @@ class CefBrowser : public virtual CefBase {
class CefRunFileDialogCallback : public virtual CefBase {
public:
///
// Called asynchronously after the file dialog is dismissed. If the selection
// was successful |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.
// Called asynchronously after the file dialog is dismissed.
// |selected_accept_filter| is the 0-based index of the value selected from
// the accept filters array passed to CefBrowserHost::RunFileDialog.
// |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(
CefRefPtr<CefBrowserHost> browser_host,
int selected_accept_filter,
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
// 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
// default title ("Open" or "Save" depending on the mode). |default_file_name|
// is the default file name to select in the dialog. |accept_types| is a list
// of valid lower-cased MIME types or file extensions specified in an input
// element and is used to restrict selectable files to such types. |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.
// default title ("Open" or "Save" depending on the mode). |default_file_path|
// is the path with optional directory and/or file name component that will be
// initially selected in the dialog. |accept_filters| are used to restrict the
// selectable file types and may any combination of (a) valid lower-cased MIME
// types (e.g. "text/*" or "image/*"), (b) individual file extensions (e.g.
// ".txt" or ".png"), or (c) combined 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 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,
optional_param=accept_types)--*/
/*--cef(optional_param=title,optional_param=default_file_path,
optional_param=accept_filters,index_param=selected_accept_filter)--*/
virtual void RunFileDialog(FileDialogMode mode,
const CefString& title,
const CefString& default_file_name,
const std::vector<CefString>& accept_types,
const CefString& default_file_path,
const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) =0;
///

View File

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

View File

@ -1720,11 +1720,36 @@ typedef enum {
///
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
// already exists.
///
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;
///

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

View File

@ -97,6 +97,18 @@ class CefBrowserHostImpl : public CefBrowserHost,
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;
// Create a new CefBrowserHostImpl instance.
@ -142,8 +154,9 @@ class CefBrowserHostImpl : public CefBrowserHost,
void RunFileDialog(
FileDialogMode mode,
const CefString& title,
const CefString& default_file_name,
const std::vector<CefString>& accept_types,
const CefString& default_file_path,
const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) override;
void StartDownload(const CefString& url) override;
void Print() override;
@ -307,13 +320,13 @@ class CefBrowserHostImpl : public CefBrowserHost,
void OnSetFocus(cef_focus_source_t source);
// 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;
// Run the file chooser dialog specified by |params|. Only a single dialog may
// be pending at any given time. |callback| will be executed asynchronously
// 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);
// Used when creating a new popup window.
@ -383,11 +396,11 @@ class CefBrowserHostImpl : public CefBrowserHost,
const GURL& target_url,
content::WebContents* new_contents) override;
void DidNavigateMainFramePostCommit(
content::WebContents* tab) override;
content::WebContents* web_contents) override;
content::JavaScriptDialogManager* GetJavaScriptDialogManager(
content::WebContents* source) override;
void RunFileChooser(
content::WebContents* tab,
content::WebContents* web_contents,
const content::FileChooserParams& params) override;
void FindReply(
content::WebContents* web_contents,
@ -528,7 +541,7 @@ class CefBrowserHostImpl : public CefBrowserHost,
// Invoke platform specific handling for the external protocol.
void PlatformHandleExternalProtocol(const GURL& url);
// Invoke platform specific file chooser dialog.
void PlatformRunFileChooser(const content::FileChooserParams& params,
void PlatformRunFileChooser(const FileChooserParams& params,
RunFileChooserCallback callback);
void PlatformTranslateKeyEvent(content::NativeWebKeyboardEvent& native_event,
@ -565,17 +578,19 @@ class CefBrowserHostImpl : public CefBrowserHost,
int http_status_code);
// Continuation from RunFileChooser.
void RunFileChooserOnUIThread(const content::FileChooserParams& params,
void RunFileChooserOnUIThread(const FileChooserParams& params,
const RunFileChooserCallback& callback);
// Used with RunFileChooser to clear the |file_chooser_pending_| flag.
void OnRunFileChooserCallback(const RunFileChooserCallback& callback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths);
// Used with WebContentsDelegate::RunFileChooser to notify the WebContents.
void OnRunFileChooserDelegateCallback(
content::WebContents* tab,
content::WebContents* web_contents,
content::FileChooserParams::Mode mode,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths);
void OnDevToolsWebContentsDestroyed();

View File

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

View File

@ -15,6 +15,7 @@
#include "base/files/file_util.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_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/web_contents.h"
#include "content/public/common/file_chooser_params.h"
#include "grit/cef_strings.h"
#include "grit/ui_strings.h"
#include "net/base/mime_util.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
@ -170,33 +172,216 @@
@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 {
// Accept-types to file-types helper.
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,
void RunOpenFileDialog(const CefBrowserHostImpl::FileChooserParams& params,
NSView* view,
int* filter_index,
std::vector<base::FilePath>* files) {
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
@ -206,39 +391,51 @@ void RunOpenFileDialog(const content::FileChooserParams& params,
} else {
title = l10n_util::GetStringUTF16(
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)];
// Consider default file name if any.
base::FilePath default_file_name(params.default_file_name);
if (!default_file_name.empty()) {
if (!default_file_name.BaseName().empty()) {
NSString* defaultName = base::SysUTF8ToNSString(
default_file_name.BaseName().value());
[openPanel setNameFieldStringValue:defaultName];
}
if (!default_file_name.DirName().empty()) {
NSString* defaultDir = base::SysUTF8ToNSString(
default_file_name.DirName().value());
[openPanel setDirectoryURL:[NSURL fileURLWithPath:defaultDir]];
std::string filename, directory;
if (!params.default_file_name.empty()) {
if (params.mode == content::FileChooserParams::UploadFolder ||
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.
filename = params.default_file_name.BaseName().value();
directory = params.default_file_name.DirName().value();
}
}
if (!filename.empty()) {
[openPanel setNameFieldStringValue:base::SysUTF8ToNSString(filename)];
}
if (!directory.empty()) {
[openPanel setDirectoryURL:
[NSURL fileURLWithPath:base::SysUTF8ToNSString(directory)]];
}
// Consider supported file types
if (!params.accept_types.empty()) {
[openPanel setAllowedFileTypes:GetFileTypesFromAcceptTypes(
params.accept_types)];
CefFilterDelegate* filter_delegate = nil;
if (params.mode != content::FileChooserParams::UploadFolder &&
!params.accept_types.empty()) {
// Add the file filter control.
filter_delegate =
[[CefFilterDelegate alloc] initWithPanel:openPanel
andAcceptFilters:params.accept_types
andFilterIndex:*filter_index];
}
// Further panel configuration.
[openPanel setAllowsOtherFileTypes:YES];
[openPanel setAllowsMultipleSelection:
(params.mode == content::FileChooserParams::OpenMultiple)];
[openPanel setCanChooseFiles:YES];
[openPanel setCanChooseDirectories:NO];
[openPanel setCanChooseFiles:
(params.mode != content::FileChooserParams::UploadFolder)];
[openPanel setCanChooseDirectories:
(params.mode == content::FileChooserParams::UploadFolder)];
[openPanel setShowsHiddenFiles:!params.hidereadonly];
// Show panel.
[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])));
}
}
if (filter_delegate != nil)
*filter_index = [filter_delegate filter];
[NSApp endSheet:openPanel];
}
bool RunSaveFileDialog(const content::FileChooserParams& params,
bool RunSaveFileDialog(const CefBrowserHostImpl::FileChooserParams& params,
NSView* view,
int* filter_index,
base::FilePath* file) {
NSSavePanel* savePanel = [NSSavePanel savePanel];
@ -266,30 +468,36 @@ bool RunSaveFileDialog(const content::FileChooserParams& params,
title = l10n_util::GetStringUTF16(IDS_SAVE_AS_DIALOG_TITLE);
[savePanel setTitle:base::SysUTF16ToNSString(title)];
// Consider default file name if any.
base::FilePath default_file_name(params.default_file_name);
if (!default_file_name.empty()) {
if (!default_file_name.BaseName().empty()) {
NSString* defaultName = base::SysUTF8ToNSString(
default_file_name.BaseName().value());
[savePanel setNameFieldStringValue:defaultName];
}
if (!default_file_name.DirName().empty()) {
NSString* defaultDir = base::SysUTF8ToNSString(
default_file_name.DirName().value());
[savePanel setDirectoryURL:[NSURL fileURLWithPath:defaultDir]];
std::string filename, 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.
filename = params.default_file_name.BaseName().value();
directory = params.default_file_name.DirName().value();
}
}
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()) {
[savePanel setAllowedFileTypes:GetFileTypesFromAcceptTypes(
params.accept_types)];
// Add the file filter control.
filter_delegate =
[[CefFilterDelegate alloc] initWithPanel:savePanel
andAcceptFilters:params.accept_types
andFilterIndex:*filter_index];
}
[savePanel setAllowsOtherFileTypes:YES];
[savePanel setShowsHiddenFiles:!params.hidereadonly];
bool success = false;
@ -300,6 +508,10 @@ bool RunSaveFileDialog(const content::FileChooserParams& params,
*file = base::FilePath([path UTF8String]);
success = true;
}
if (filter_delegate != nil)
*filter_index = [filter_delegate filter];
[NSApp endSheet:savePanel];
return success;
@ -491,22 +703,26 @@ void CefBrowserHostImpl::PlatformHandleKeyboardEvent(
}
void CefBrowserHostImpl::PlatformRunFileChooser(
const content::FileChooserParams& params,
const FileChooserParams& params,
RunFileChooserCallback callback) {
std::vector<base::FilePath> files;
int filter_index = params.selected_accept_filter;
NSView* owner = PlatformGetWindowHandle();
if (params.mode == content::FileChooserParams::Open ||
params.mode == content::FileChooserParams::OpenMultiple) {
RunOpenFileDialog(params, PlatformGetWindowHandle(), &files);
params.mode == content::FileChooserParams::OpenMultiple ||
params.mode == content::FileChooserParams::UploadFolder) {
RunOpenFileDialog(params, owner, &filter_index, &files);
} else if (params.mode == content::FileChooserParams::Save) {
base::FilePath file;
if (RunSaveFileDialog(params, PlatformGetWindowHandle(), &file))
if (RunSaveFileDialog(params, owner, &filter_index, &file)) {
files.push_back(file);
}
} else {
NOTIMPLEMENTED();
}
callback.Run(files);
callback.Run(filter_index, files);
}
void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) {

View File

@ -8,6 +8,7 @@
#include <commdlg.h>
#include <dwmapi.h>
#include <shellapi.h>
#include <shlobj.h>
#include <wininet.h>
#include <winspool.h>
@ -19,9 +20,11 @@
#include "base/files/file_util.h"
#include "base/i18n/case_conversion.h"
#include "base/memory/ref_counted_memory.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "base/win/scoped_comptr.h"
#include "base/win/windows_version.h"
#include "content/common/cursors/webcursor.h"
#include "content/public/browser/native_web_keyboard_event.h"
@ -140,14 +143,8 @@ std::wstring FormatFilterForExtensions(
ext_name = ext_name.substr(ext_index);
if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) {
// The extension doesn't exist in the registry. Create a description
// based on the unknown extension type (i.e. if the extension is .qqq,
// the we create a description "QQQ File (.qqq)").
// The extension doesn't exist in the registry.
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();
}
std::wstring GetFilterStringFromAcceptTypes(
const std::vector<base::string16>& accept_types) {
std::wstring GetFilterString(
const std::vector<base::string16>& accept_filters) {
std::vector<std::wstring> extensions;
std::vector<std::wstring> descriptions;
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] == '.') {
extensions.push_back(L"*" + base::ASCIIToUTF16(ascii_type));
descriptions.push_back(std::wstring());
} else {
// Otherwise convert mime type to one or more extensions.
std::vector<base::FilePath::StringType> ext;
std::wstring ext_str;
net::GetExtensionsForMimeType(ascii_type, &ext);
if (ext.size() > 0) {
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_type));
for (size_t i = 0; i < accept_filters.size(); ++i) {
const base::string16& filter = accept_filters[i];
if (filter.empty())
continue;
size_t sep_index = filter.find('|');
if (sep_index != base::string16::npos) {
// Treat as a filter of the form "Filter Name|.ext1;.ext2;.ext3".
const base::string16& desc = filter.substr(0, sep_index);
std::vector<base::string16> ext;
base::SplitString(filter.substr(sep_index + 1), ';', &ext);
std::wstring ext_str;
for (size_t x = 0; x < ext.size(); ++x) {
const base::string16& file_ext = ext[x];
if (!file_ext.empty() && file_ext[0] == '.') {
if (!ext_str.empty())
ext_str += L";";
ext_str += L"*" + file_ext;
}
}
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
bool RunOpenFileDialog(const content::FileChooserParams& params,
bool RunOpenFileDialog(const CefBrowserHostImpl::FileChooserParams& params,
HWND owner,
int* filter_index,
base::FilePath* path) {
OPENFILENAME ofn;
@ -235,22 +254,25 @@ bool RunOpenFileDialog(const content::FileChooserParams& params,
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = owner;
// Consider default file name if any.
base::FilePath default_file_name(params.default_file_name);
wchar_t filename[MAX_PATH] = {0};
ofn.lpstrFile = filename;
ofn.nMaxFile = MAX_PATH;
std::wstring directory;
if (!default_file_name.empty()) {
base::wcslcpy(filename, default_file_name.value().c_str(),
arraysize(filename));
directory = default_file_name.DirName().value();
ofn.lpstrInitialDir = directory.c_str();
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.
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;
if (!params.title.empty())
@ -264,19 +286,27 @@ bool RunOpenFileDialog(const content::FileChooserParams& params,
// without having to close Chrome first.
ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_EXPLORER |
OFN_ENABLESIZING;
if (params.hidereadonly)
ofn.Flags |= OFN_HIDEREADONLY;
std::wstring filter = GetFilterStringFromAcceptTypes(params.accept_types);
if (!filter.empty())
const std::wstring& filter = GetFilterString(params.accept_types);
if (!filter.empty()) {
ofn.lpstrFilter = filter.c_str();
// Indices into |lpstrFilter| start at 1.
ofn.nFilterIndex = *filter_index + 1;
}
bool success = !!GetOpenFileName(&ofn);
if (success)
if (success) {
*filter_index = ofn.nFilterIndex == 0 ? 0 : ofn.nFilterIndex - 1;
*path = base::FilePath(filename);
}
return success;
}
bool RunOpenMultiFileDialog(const content::FileChooserParams& params,
bool RunOpenMultiFileDialog(const CefBrowserHostImpl::FileChooserParams& params,
HWND owner,
int* filter_index,
std::vector<base::FilePath>* paths) {
OPENFILENAME ofn;
@ -292,6 +322,19 @@ bool RunOpenMultiFileDialog(const content::FileChooserParams& params,
ofn.lpstrFile = filename.get();
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;
if (!params.title.empty())
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
// without having to close Chrome first.
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);
if (!filter.empty())
const std::wstring& filter = GetFilterString(params.accept_types);
if (!filter.empty()) {
ofn.lpstrFilter = filter.c_str();
// Indices into |lpstrFilter| start at 1.
ofn.nFilterIndex = *filter_index + 1;
}
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;
}
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,
int* filter_index,
base::FilePath* path) {
OPENFILENAME ofn;
@ -348,22 +469,25 @@ bool RunSaveFileDialog(const content::FileChooserParams& params,
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = owner;
// Consider default file name if any.
base::FilePath default_file_name(params.default_file_name);
wchar_t filename[MAX_PATH] = {0};
ofn.lpstrFile = filename;
ofn.nMaxFile = MAX_PATH;
std::wstring directory;
if (!default_file_name.empty()) {
base::wcslcpy(filename, default_file_name.value().c_str(),
arraysize(filename));
directory = default_file_name.DirName().value();
ofn.lpstrInitialDir = directory.c_str();
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.
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;
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
// without having to close Chrome first.
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLESIZING |
OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST;
ofn.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_NOCHANGEDIR |
OFN_PATHMUSTEXIST;
if (params.hidereadonly)
ofn.Flags |= OFN_HIDEREADONLY;
if (params.overwriteprompt)
ofn.Flags |= OFN_OVERWRITEPROMPT;
std::wstring filter = GetFilterStringFromAcceptTypes(params.accept_types);
if (!filter.empty())
const std::wstring& filter = GetFilterString(params.accept_types);
if (!filter.empty()) {
ofn.lpstrFilter = filter.c_str();
// Indices into |lpstrFilter| start at 1.
ofn.nFilterIndex = *filter_index + 1;
}
bool success = !!GetSaveFileName(&ofn);
if (success)
if (success) {
*filter_index = ofn.nFilterIndex == 0 ? 0 : ofn.nFilterIndex - 1;
*path = base::FilePath(filename);
}
return success;
}
@ -782,25 +915,32 @@ void CefBrowserHostImpl::PlatformHandleKeyboardEvent(
}
void CefBrowserHostImpl::PlatformRunFileChooser(
const content::FileChooserParams& params,
const FileChooserParams& params,
RunFileChooserCallback callback) {
int filter_index = params.selected_accept_filter;
std::vector<base::FilePath> files;
HWND owner = PlatformGetWindowHandle();
if (params.mode == content::FileChooserParams::Open) {
base::FilePath file;
if (RunOpenFileDialog(params, PlatformGetWindowHandle(), &file))
if (RunOpenFileDialog(params, owner, &filter_index, &file))
files.push_back(file);
} 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) {
base::FilePath file;
if (RunSaveFileDialog(params, PlatformGetWindowHandle(), &file))
if (RunSaveFileDialog(params, owner, &filter_index, &file))
files.push_back(file);
} else {
NOTIMPLEMENTED();
}
callback.Run(files);
callback.Run(filter_index, files);
}
void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) {

View File

@ -142,7 +142,7 @@ class CefBeforeDownloadCallbackImpl : public CefBeforeDownloadCallback {
if (browser.get()) {
handled = true;
content::FileChooserParams params;
CefBrowserHostImpl::FileChooserParams params;
params.mode = content::FileChooserParams::Save;
if (!suggested_path.empty()) {
params.default_file_name = suggested_path;
@ -169,6 +169,7 @@ class CefBeforeDownloadCallbackImpl : public CefBeforeDownloadCallback {
static void ChooseDownloadPathCallback(
const content::DownloadTargetCallback& callback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths) {
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,
cef_file_dialog_mode_t mode, const cef_string_t* title,
const cef_string_t* default_file_name, cef_string_list_t accept_types,
cef_run_file_dialog_callback_t* callback) {
const cef_string_t* default_file_path, cef_string_list_t accept_filters,
int selected_accept_filter, cef_run_file_dialog_callback_t* callback) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
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
DCHECK(callback);
if (!callback)
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
std::vector<CefString> accept_typesList;
transfer_string_list_contents(accept_types, accept_typesList);
// Translate param: accept_filters; type: string_vec_byref_const
std::vector<CefString> accept_filtersList;
transfer_string_list_contents(accept_filters, accept_filtersList);
// Execute
CefBrowserHostCppToC::Get(self)->RunFileDialog(
mode,
CefString(title),
CefString(default_file_name),
accept_typesList,
CefString(default_file_path),
accept_filtersList,
selected_accept_filter,
CefRunFileDialogCallbackCToCpp::Wrap(callback));
}

View File

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

View File

@ -17,16 +17,18 @@
// MEMBER FUNCTIONS - Body may be edited by hand.
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
DCHECK(self);
if (!self)
return;
// Verify param: file_paths; type: string_vec_byref_const
DCHECK(file_paths);
if (!file_paths)
// 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
std::vector<CefString> file_pathsList;
@ -34,6 +36,7 @@ void CEF_CALLBACK file_dialog_callback_cont(
// Execute
CefFileDialogCallbackCppToC::Get(self)->Continue(
selected_accept_filter,
file_pathsList);
}

View File

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

View File

@ -188,37 +188,42 @@ void CefBrowserHostCToCpp::SetZoomLevel(double zoomLevel) {
}
void CefBrowserHostCToCpp::RunFileDialog(FileDialogMode mode,
const CefString& title, const CefString& default_file_name,
const std::vector<CefString>& accept_types,
const CefString& title, const CefString& default_file_path,
const std::vector<CefString>& accept_filters, int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) {
if (CEF_MEMBER_MISSING(struct_, run_file_dialog))
return;
// 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
DCHECK(callback.get());
if (!callback.get())
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
cef_string_list_t accept_typesList = cef_string_list_alloc();
DCHECK(accept_typesList);
if (accept_typesList)
transfer_string_list_contents(accept_types, accept_typesList);
// Translate param: accept_filters; type: string_vec_byref_const
cef_string_list_t accept_filtersList = cef_string_list_alloc();
DCHECK(accept_filtersList);
if (accept_filtersList)
transfer_string_list_contents(accept_filters, accept_filtersList);
// Execute
struct_->run_file_dialog(struct_,
mode,
title.GetStruct(),
default_file_name.GetStruct(),
accept_typesList,
default_file_path.GetStruct(),
accept_filtersList,
selected_accept_filter,
CefRunFileDialogCallbackCppToC::Wrap(callback));
// Restore param:accept_types; type: string_vec_byref_const
if (accept_typesList)
cef_string_list_free(accept_typesList);
// Restore param:accept_filters; type: string_vec_byref_const
if (accept_filtersList)
cef_string_list_free(accept_filtersList);
}
void CefBrowserHostCToCpp::StartDownload(const CefString& url) {

View File

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

View File

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

View File

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

View File

@ -16,13 +16,19 @@
// VIRTUAL METHODS - Body may be edited by hand.
void CefFileDialogCallbackCToCpp::Continue(
void CefFileDialogCallbackCToCpp::Continue(int selected_accept_filter,
const std::vector<CefString>& file_paths) {
if (CEF_MEMBER_MISSING(struct_, cont))
return;
// 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
cef_string_list_t file_pathsList = cef_string_list_alloc();
DCHECK(file_pathsList);
@ -31,6 +37,7 @@ void CefFileDialogCallbackCToCpp::Continue(
// Execute
struct_->cont(struct_,
selected_accept_filter,
file_pathsList);
// Restore param:file_paths; type: string_vec_byref_const

View File

@ -34,7 +34,8 @@ class CefFileDialogCallbackCToCpp
cef_file_dialog_callback_t>(str) {}
// 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;
};

View File

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

View File

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

View File

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

View File

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

View File

@ -50,52 +50,74 @@ std::string GetDescriptionFromMimeType(const std::string& mime_type) {
return std::string();
}
void AddFiltersForAcceptTypes(GtkFileChooser* chooser,
const std::vector<CefString>& accept_types,
bool include_all_files) {
void AddFilters(GtkFileChooser* chooser,
const std::vector<CefString>& accept_filters,
bool include_all_files,
std::vector<GtkFileFilter*>* filters) {
bool has_filter = false;
for (size_t i = 0; i < accept_types.size(); ++i) {
std::string ascii_type = accept_types[i];
if (ascii_type.length()) {
// Just treat as extension if contains '.' as the first character.
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();
for (size_t i = 0; i < accept_filters.size(); ++i) {
const std::string& filter = accept_filters[i];
if (filter.empty())
continue;
std::vector<CefString> ext;
CefGetExtensionsForMimeType(ascii_type, ext);
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());
std::vector<std::string> extensions;
std::string description;
if (description_from_ext) {
if (x != 0)
description += ";";
description += pattern;
}
}
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);
if (filter) {
gtk_file_filter_set_name(filter, description.c_str());
gtk_file_chooser_add_filter(chooser, filter);
if (!has_filter)
has_filter = true;
const std::string& exts = filter.substr(sep_index + 1);
size_t last = 0;
size_t size = exts.size();
for (size_t i = 0; i <= size; ++i) {
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
@ -103,7 +125,7 @@ void AddFiltersForAcceptTypes(GtkFileChooser* chooser,
if (include_all_files && has_filter) {
GtkFileFilter* filter = gtk_file_filter_new();
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);
}
}
@ -113,17 +135,26 @@ void AddFiltersForAcceptTypes(GtkFileChooser* chooser,
bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser,
FileDialogMode mode,
const CefString& title,
const CefString& default_file_name,
const std::vector<CefString>& accept_types,
const CefString& default_file_path,
const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefFileDialogCallback> callback) {
std::vector<CefString> files;
GtkFileChooserAction action;
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;
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;
accept_button = GTK_STOCK_SAVE;
} else {
@ -131,12 +162,6 @@ bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser,
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;
if (!title.empty()) {
title_str = title;
@ -148,6 +173,9 @@ bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser,
case FILE_DIALOG_OPEN_MULTIPLE:
title_str = "Open Files";
break;
case FILE_DIALOG_OPEN_FOLDER:
title_str = "Open Folder";
break;
case FILE_DIALOG_SAVE:
title_str = "Save File";
break;
@ -166,28 +194,39 @@ bool ClientHandler::OnFileDialog(CefRefPtr<CefBrowser> browser,
accept_button, GTK_RESPONSE_ACCEPT,
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);
} else if (mode == FILE_DIALOG_SAVE) {
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),
TRUE);
if (mode_type == FILE_DIALOG_SAVE) {
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_current_name(GTK_FILE_CHOOSER(dialog),
base_name.c_str());
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog),
!(mode & FILE_DIALOG_HIDEREADONLY_FLAG));
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;
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));
files.push_back(std::string(filename));
success = true;
} else if (mode == FILE_DIALOG_OPEN_MULTIPLE) {
} else if (mode_type == FILE_DIALOG_OPEN_MULTIPLE) {
GSList* filenames =
gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
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);
if (success)
callback->Continue(files);
callback->Continue(filter_index, files);
else
callback->Cancel();

View File

@ -14,19 +14,63 @@ namespace {
const char kTestUrl[] = "http://tests/dialogs";
const char kFileOpenMessageName[] = "DialogTest.FileOpen";
const char kFileOpenMultipleMessageName[] = "DialogTest.FileOpenMultiple";
const char kFileOpenFolderMessageName[] = "DialogTest.FileOpenFolder";
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.
class DialogCallback : public CefRunFileDialogCallback {
public:
explicit DialogCallback(
CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback)
: router_callback_(router_callback) {
DialogCallback(
CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback,
scoped_refptr<DialogState> dialog_state)
: router_callback_(router_callback),
dialog_state_(dialog_state) {
}
virtual void OnFileDialogDismissed(
CefRefPtr<CefBrowserHost> browser_host,
int last_selected_filter,
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.
std::string response;
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_ = NULL;
dialog_state_->pending_ = false;
dialog_state_ = NULL;
}
private:
CefRefPtr<CefMessageRouterBrowserSide::Callback> router_callback_;
scoped_refptr<DialogState> dialog_state_;
IMPLEMENT_REFCOUNTING(DialogCallback);
DISALLOW_COPY_AND_ASSIGN(DialogCallback);
};
// Handle messages in the browser process.
@ -57,36 +106,72 @@ class Handler : public CefMessageRouterBrowserSide::Handler {
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) OVERRIDE {
CEF_REQUIRE_UI_THREAD();
// Only handle messages from the test URL.
const std::string& url = frame->GetURL();
if (url.find(kTestUrl) != 0)
return false;
// Sample file type filter.
std::vector<CefString> file_types;
file_types.push_back("text/*");
file_types.push_back(".log");
file_types.push_back(".patch");
if (!dialog_state_.get())
dialog_state_ = new DialogState;
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;
if (message_name == kFileOpenMessageName) {
browser->GetHost()->RunFileDialog(FILE_DIALOG_OPEN, "My Open Dialog",
"test.txt", file_types, dialog_callback.get());
dialog_state_->mode_ = FILE_DIALOG_OPEN;
title = "My Open Dialog";
} else if (message_name == kFileOpenMultipleMessageName) {
browser->GetHost()->RunFileDialog(FILE_DIALOG_OPEN_MULTIPLE,
"My Open Multiple Dialog", CefString(), file_types,
dialog_callback.get());
dialog_state_->mode_ = FILE_DIALOG_OPEN_MULTIPLE;
title = "My Open Multiple Dialog";
} else if (message_name == kFileOpenFolderMessageName) {
dialog_state_->mode_ = FILE_DIALOG_OPEN_FOLDER;
title = "My Open Folder Dialog";
} else if (message_name == kFileSaveMessageName) {
browser->GetHost()->RunFileDialog(FILE_DIALOG_SAVE, "My Save Dialog",
"test.txt", file_types, dialog_callback.get());
dialog_state_->mode_ = static_cast<cef_file_dialog_mode_t>(
FILE_DIALOG_SAVE | FILE_DIALOG_OVERWRITEPROMPT_FLAG);
title = "My Save Dialog";
} else {
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;
}
private:
scoped_refptr<DialogState> dialog_state_;
DISALLOW_COPY_AND_ASSIGN(Handler);
};
} // 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="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('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>
<p id="time"></p>
</form>

View File

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