mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
- Factor platform-specific code out of CefBrowserHostImpl (issue #1749).
- Introduce native/ and osr/ folders for native (non-platform-agnostic) and osr (windowless) code respectively. - Introduce CefBrowserPlatformDelegate for abstracting platform-specific implementations of browser host functionality. - Move dialog and menu code to separate manager and platform-specific runner implementations exposed via CefBrowserPlatformDelegate. - Standardize focus-handling behavior between windowed and windowless implementations. CefFocusHandler::OnSetFocus() will now also be called for osr focus changes. - Support multiple simultaneous popups (issue #1289).
This commit is contained in:
396
libcef/browser/file_dialog_manager.cc
Normal file
396
libcef/browser/file_dialog_manager.cc
Normal file
@@ -0,0 +1,396 @@
|
||||
// Copyright (c) 2012 The Chromium Embedded Framework Authors.
|
||||
// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "libcef/browser/file_dialog_manager.h"
|
||||
|
||||
#include "include/cef_dialog_handler.h"
|
||||
#include "libcef/browser/browser_host_impl.h"
|
||||
#include "libcef/browser/thread_util.h"
|
||||
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/common/file_chooser_file_info.h"
|
||||
#include "net/base/directory_lister.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class CefFileDialogCallbackImpl : public CefFileDialogCallback {
|
||||
public:
|
||||
explicit CefFileDialogCallbackImpl(
|
||||
const CefFileDialogRunner::RunFileChooserCallback& callback)
|
||||
: callback_(callback) {
|
||||
}
|
||||
~CefFileDialogCallbackImpl() override {
|
||||
if (!callback_.is_null()) {
|
||||
// The callback is still pending. Cancel it now.
|
||||
if (CEF_CURRENTLY_ON_UIT()) {
|
||||
CancelNow(callback_);
|
||||
} else {
|
||||
CEF_POST_TASK(CEF_UIT,
|
||||
base::Bind(&CefFileDialogCallbackImpl::CancelNow, callback_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (!file_paths.empty()) {
|
||||
std::vector<CefString>::const_iterator it = file_paths.begin();
|
||||
for (; it != file_paths.end(); ++it)
|
||||
vec.push_back(base::FilePath(*it));
|
||||
}
|
||||
callback_.Run(selected_accept_filter, vec);
|
||||
callback_.Reset();
|
||||
}
|
||||
} else {
|
||||
CEF_POST_TASK(CEF_UIT,
|
||||
base::Bind(&CefFileDialogCallbackImpl::Continue, this,
|
||||
selected_accept_filter, file_paths));
|
||||
}
|
||||
}
|
||||
|
||||
void Cancel() override {
|
||||
if (CEF_CURRENTLY_ON_UIT()) {
|
||||
if (!callback_.is_null()) {
|
||||
CancelNow(callback_);
|
||||
callback_.Reset();
|
||||
}
|
||||
} else {
|
||||
CEF_POST_TASK(CEF_UIT,
|
||||
base::Bind(&CefFileDialogCallbackImpl::Cancel, this));
|
||||
}
|
||||
}
|
||||
|
||||
bool IsConnected() {
|
||||
return !callback_.is_null();
|
||||
}
|
||||
|
||||
void Disconnect() {
|
||||
callback_.Reset();
|
||||
}
|
||||
|
||||
private:
|
||||
static void CancelNow(
|
||||
const CefFileDialogRunner::RunFileChooserCallback& callback) {
|
||||
CEF_REQUIRE_UIT();
|
||||
std::vector<base::FilePath> file_paths;
|
||||
callback.Run(0, file_paths);
|
||||
}
|
||||
|
||||
CefFileDialogRunner::RunFileChooserCallback callback_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(CefFileDialogCallbackImpl);
|
||||
};
|
||||
|
||||
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());
|
||||
}
|
||||
callback->OnFileDialogDismissed(selected_accept_filter, paths);
|
||||
}
|
||||
|
||||
class UploadFolderHelper :
|
||||
public net::DirectoryLister::DirectoryListerDelegate {
|
||||
public:
|
||||
explicit UploadFolderHelper(
|
||||
const CefFileDialogRunner::RunFileChooserCallback& callback)
|
||||
: callback_(callback) {
|
||||
}
|
||||
|
||||
~UploadFolderHelper() override {
|
||||
if (!callback_.is_null()) {
|
||||
if (CEF_CURRENTLY_ON_UIT()) {
|
||||
CancelNow(callback_);
|
||||
} else {
|
||||
CEF_POST_TASK(CEF_UIT,
|
||||
base::Bind(&UploadFolderHelper::CancelNow, callback_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnListFile(
|
||||
const net::DirectoryLister::DirectoryListerData& data) override {
|
||||
CEF_REQUIRE_UIT();
|
||||
if (!data.info.IsDirectory())
|
||||
select_files_.push_back(data.path);
|
||||
}
|
||||
|
||||
void OnListDone(int error) override {
|
||||
CEF_REQUIRE_UIT();
|
||||
if (!callback_.is_null()) {
|
||||
callback_.Run(0, select_files_);
|
||||
callback_.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static void CancelNow(
|
||||
const CefFileDialogRunner::RunFileChooserCallback& callback) {
|
||||
CEF_REQUIRE_UIT();
|
||||
std::vector<base::FilePath> file_paths;
|
||||
callback.Run(0, file_paths);
|
||||
}
|
||||
|
||||
CefFileDialogRunner::RunFileChooserCallback callback_;
|
||||
std::vector<base::FilePath> select_files_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(UploadFolderHelper);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
CefFileDialogManager::CefFileDialogManager(
|
||||
CefBrowserHostImpl* browser,
|
||||
scoped_ptr<CefFileDialogRunner> runner)
|
||||
: content::WebContentsObserver(browser->web_contents()),
|
||||
browser_(browser),
|
||||
runner_(runner.Pass()),
|
||||
file_chooser_pending_(false),
|
||||
weak_ptr_factory_(this) {
|
||||
DCHECK(web_contents());
|
||||
}
|
||||
|
||||
CefFileDialogManager::~CefFileDialogManager() {
|
||||
}
|
||||
|
||||
void CefFileDialogManager::Destroy() {
|
||||
DCHECK(!file_chooser_pending_);
|
||||
runner_.reset(NULL);
|
||||
}
|
||||
|
||||
void CefFileDialogManager::RunFileDialog(
|
||||
cef_file_dialog_mode_t mode,
|
||||
const CefString& title,
|
||||
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;
|
||||
|
||||
CefFileDialogRunner::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_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);
|
||||
}
|
||||
|
||||
RunFileChooser(params, base::Bind(RunFileDialogDismissed, callback));
|
||||
}
|
||||
|
||||
void CefFileDialogManager::RunFileChooser(
|
||||
content::WebContents* web_contents,
|
||||
const content::FileChooserParams& params) {
|
||||
CEF_REQUIRE_UIT();
|
||||
DCHECK_EQ(web_contents, this->web_contents());
|
||||
|
||||
content::RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
|
||||
if (!render_view_host)
|
||||
return;
|
||||
|
||||
CefFileDialogRunner::FileChooserParams cef_params;
|
||||
static_cast<content::FileChooserParams&>(cef_params) = params;
|
||||
|
||||
if (lister_) {
|
||||
// Cancel the previous upload folder run.
|
||||
lister_->Cancel();
|
||||
lister_.reset();
|
||||
}
|
||||
|
||||
if (params.mode == content::FileChooserParams::UploadFolder) {
|
||||
RunFileChooser(cef_params,
|
||||
base::Bind(
|
||||
&CefFileDialogManager::OnRunFileChooserUploadFolderDelegateCallback,
|
||||
weak_ptr_factory_.GetWeakPtr(), params.mode));
|
||||
return;
|
||||
}
|
||||
|
||||
RunFileChooser(cef_params,
|
||||
base::Bind(&CefFileDialogManager::OnRunFileChooserDelegateCallback,
|
||||
weak_ptr_factory_.GetWeakPtr(), params.mode));
|
||||
}
|
||||
|
||||
void CefFileDialogManager::RunFileChooser(
|
||||
const CefFileDialogRunner::FileChooserParams& params,
|
||||
const CefFileDialogRunner::RunFileChooserCallback& callback) {
|
||||
CEF_REQUIRE_UIT();
|
||||
|
||||
if (file_chooser_pending_) {
|
||||
// Dismiss the new dialog immediately.
|
||||
callback.Run(0, std::vector<base::FilePath>());
|
||||
return;
|
||||
}
|
||||
|
||||
file_chooser_pending_ = true;
|
||||
|
||||
// Ensure that the |file_chooser_pending_| flag is cleared.
|
||||
const CefFileDialogRunner::RunFileChooserCallback& host_callback =
|
||||
base::Bind(&CefFileDialogManager::OnRunFileChooserCallback,
|
||||
weak_ptr_factory_.GetWeakPtr(), callback);
|
||||
|
||||
bool handled = false;
|
||||
|
||||
if (browser_->client().get()) {
|
||||
CefRefPtr<CefDialogHandler> handler =
|
||||
browser_->client()->GetDialogHandler();
|
||||
if (handler.get()) {
|
||||
int mode = FILE_DIALOG_OPEN;
|
||||
switch (params.mode) {
|
||||
case content::FileChooserParams::Open:
|
||||
mode = FILE_DIALOG_OPEN;
|
||||
break;
|
||||
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;
|
||||
default:
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
|
||||
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_filters.push_back(*it);
|
||||
|
||||
CefRefPtr<CefFileDialogCallbackImpl> callbackImpl(
|
||||
new CefFileDialogCallbackImpl(host_callback));
|
||||
handled = handler->OnFileDialog(
|
||||
browser_,
|
||||
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();
|
||||
} else {
|
||||
// User executed the callback even though they returned false.
|
||||
NOTREACHED();
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
if (runner_.get()) {
|
||||
runner_->Run(browser_, params, host_callback);
|
||||
} else {
|
||||
LOG(WARNING) << "No file dialog runner available for this platform";
|
||||
host_callback.Run(0, std::vector<base::FilePath>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CefFileDialogManager::OnRunFileChooserCallback(
|
||||
const CefFileDialogRunner::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, selected_accept_filter, file_paths));
|
||||
}
|
||||
|
||||
void CefFileDialogManager::OnRunFileChooserUploadFolderDelegateCallback(
|
||||
const content::FileChooserParams::Mode mode,
|
||||
int selected_accept_filter,
|
||||
const std::vector<base::FilePath>& file_paths) {
|
||||
CEF_REQUIRE_UIT();
|
||||
DCHECK_EQ(mode, content::FileChooserParams::UploadFolder);
|
||||
|
||||
if (file_paths.size() == 0) {
|
||||
// Client canceled the file chooser.
|
||||
OnRunFileChooserDelegateCallback(mode, selected_accept_filter, file_paths);
|
||||
} else {
|
||||
lister_.reset(new net::DirectoryLister(
|
||||
file_paths[0],
|
||||
net::DirectoryLister::NO_SORT,
|
||||
new UploadFolderHelper(
|
||||
base::Bind(&CefFileDialogManager::OnRunFileChooserDelegateCallback,
|
||||
weak_ptr_factory_.GetWeakPtr(), mode))));
|
||||
lister_->Start();
|
||||
}
|
||||
}
|
||||
|
||||
void CefFileDialogManager::OnRunFileChooserDelegateCallback(
|
||||
content::FileChooserParams::Mode mode,
|
||||
int selected_accept_filter,
|
||||
const std::vector<base::FilePath>& file_paths) {
|
||||
CEF_REQUIRE_UIT();
|
||||
|
||||
if (lister_.get())
|
||||
lister_.reset();
|
||||
|
||||
if (!web_contents())
|
||||
return;
|
||||
|
||||
content::RenderViewHost* render_view_host =
|
||||
web_contents()->GetRenderViewHost();
|
||||
if (!render_view_host)
|
||||
return;
|
||||
|
||||
// Convert FilePath list to SelectedFileInfo list.
|
||||
std::vector<content::FileChooserFileInfo> selected_files;
|
||||
for (size_t i = 0; i < file_paths.size(); ++i) {
|
||||
content::FileChooserFileInfo info;
|
||||
info.file_path = file_paths[i];
|
||||
selected_files.push_back(info);
|
||||
}
|
||||
|
||||
// Notify our RenderViewHost in all cases.
|
||||
render_view_host->FilesSelectedInChooser(selected_files, mode);
|
||||
}
|
Reference in New Issue
Block a user