Use Chrome file dialogs on all platforms and runtimes (fixes issue #3314)

All file dialogs irrespective of source, platform and runtime will now be
routed through CefFileDialogManager and trigger CefDialogHandler callbacks
(see issue #3293).

Adds Chrome runtime support for CefBrowserHost::RunFileDialog and
CefDialogHandler callbacks.

Adds Alloy runtime support for internal GTK file and print dialogs on Linux
subject to the following limitations:

1. Internal GTK implementation:
   - Cannot be used with multi-threaded-message-loop because Chromium's
     internal GTK implementation is not thread-safe (does not use GDK threads).
   - Dialogs will not be modal to application windows when used with off-screen
     rendering due to lack of access to the client's top-level GtkWindow.
2. Cefclient CefDialogHandler implementation:
   - Cannot be used with Views because it requires a top-level GtkWindow.

Due to the above limitations no dialog implementation is currently provided for
Views + multi-threaded-message-loop on Linux. In cases where both
implementations are supported the cefclient version is now behind an optional
`--use-client-dialogs` command-line flag.

Expressly forbids multiple simultaneous file dialogs with the internal platform
implementation which uses modal dialogs. CefDialogHandler will still be notified
and can optionally handle each request without a modal dialog (see issue #3154).

Removes some RunFileDialog parameters that are not supported by the Chrome file
dialog implementation (selected_accept_filter parameter, cef_file_dialog_mode_t
overwrite/read-only flags).
This commit is contained in:
Marshall Greenblatt
2022-04-15 15:55:23 -04:00
parent edef01f579
commit 2ea7459a89
75 changed files with 1566 additions and 2231 deletions

View File

@@ -32,6 +32,7 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "chrome/browser/file_select_helper.h"
#include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
#include "content/browser/gpu/compositor_util.h"
#include "content/public/browser/desktop_media_id.h"
@@ -348,27 +349,6 @@ void AlloyBrowserHostImpl::SetZoomLevel(double zoomLevel) {
}
}
void AlloyBrowserHostImpl::RunFileDialog(
FileDialogMode mode,
const CefString& title,
const CefString& default_file_path,
const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&AlloyBrowserHostImpl::RunFileDialog, this,
mode, title, default_file_path, accept_filters,
selected_accept_filter, callback));
return;
}
EnsureFileDialogManager();
file_dialog_manager_->RunFileDialog(mode, title, default_file_path,
accept_filters, selected_accept_filter,
callback);
}
void AlloyBrowserHostImpl::Print() {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT, base::BindOnce(&AlloyBrowserHostImpl::Print, this));
@@ -666,8 +646,6 @@ void AlloyBrowserHostImpl::DestroyBrowser() {
OnBeforeClose();
// Destroy any platform constructs first.
if (file_dialog_manager_.get())
file_dialog_manager_->Destroy();
if (javascript_dialog_manager_.get())
javascript_dialog_manager_->Destroy();
if (menu_manager_.get())
@@ -750,13 +728,6 @@ void AlloyBrowserHostImpl::OnSetFocus(cef_focus_source_t source) {
platform_delegate_->SetFocus(true);
}
void AlloyBrowserHostImpl::RunFileChooser(
const CefFileDialogRunner::FileChooserParams& params,
CefFileDialogRunner::RunFileChooserCallback callback) {
EnsureFileDialogManager();
file_dialog_manager_->RunFileChooser(params, std::move(callback));
}
void AlloyBrowserHostImpl::EnterFullscreenModeForTab(
content::RenderFrameHost* requesting_frame,
const blink::mojom::FullscreenOptions& options) {
@@ -1278,8 +1249,9 @@ void AlloyBrowserHostImpl::RunFileChooser(
content::RenderFrameHost* render_frame_host,
scoped_refptr<content::FileSelectListener> listener,
const blink::mojom::FileChooserParams& params) {
EnsureFileDialogManager();
file_dialog_manager_->RunFileChooser(listener, params);
// This will eventually call into CefFileDialogManager.
FileSelectHelper::RunFileChooser(render_frame_host, std::move(listener),
params);
}
bool AlloyBrowserHostImpl::HandleContextMenu(
@@ -1575,11 +1547,3 @@ void AlloyBrowserHostImpl::UpdateDragCursor(
if (platform_delegate_)
platform_delegate_->UpdateDragCursor(operation);
}
void AlloyBrowserHostImpl::EnsureFileDialogManager() {
CEF_REQUIRE_UIT();
if (!file_dialog_manager_.get() && platform_delegate_) {
file_dialog_manager_.reset(new CefFileDialogManager(
this, platform_delegate_->CreateFileDialogRunner()));
}
}

View File

@@ -16,7 +16,6 @@
#include "include/cef_frame.h"
#include "libcef/browser/browser_host_base.h"
#include "libcef/browser/browser_info.h"
#include "libcef/browser/file_dialog_manager.h"
#include "libcef/browser/frame_host_impl.h"
#include "libcef/browser/javascript_dialog_manager.h"
#include "libcef/browser/menu_manager.h"
@@ -83,12 +82,6 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase,
CefWindowHandle GetOpenerWindowHandle() override;
double GetZoomLevel() override;
void SetZoomLevel(double zoomLevel) override;
void RunFileDialog(FileDialogMode mode,
const CefString& title,
const CefString& default_file_path,
const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) override;
void Print() override;
void PrintToPDF(const CefString& path,
const CefPdfPrintSettings& settings,
@@ -178,12 +171,6 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase,
void OnSetFocus(cef_focus_source_t source) override;
// 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 CefFileDialogRunner::FileChooserParams& params,
CefFileDialogRunner::RunFileChooserCallback callback);
bool HandleContextMenu(content::WebContents* web_contents,
const content::ContextMenuParams& params);
@@ -329,9 +316,6 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase,
// Give the platform delegate an opportunity to create the host window.
bool CreateHostWindow();
// Create the CefFileDialogManager if it doesn't already exist.
void EnsureFileDialogManager();
void StartAudioCapturer();
void OnRecentlyAudibleTimerFired();
@@ -349,9 +333,6 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase,
// on the UI thread.
bool window_destroyed_ = false;
// Used for creating and managing file dialogs.
std::unique_ptr<CefFileDialogManager> file_dialog_manager_;
// Used for creating and managing JavaScript dialogs.
std::unique_ptr<CefJavaScriptDialogManager> javascript_dialog_manager_;

View File

@@ -13,6 +13,7 @@
#include "libcef/browser/context.h"
#include "libcef/browser/devtools/devtools_manager_delegate.h"
#include "libcef/browser/extensions/extension_system_factory.h"
#include "libcef/browser/file_dialog_runner.h"
#include "libcef/browser/net/chrome_scheme_handler.h"
#include "libcef/browser/printing/constrained_window_views_client.h"
#include "libcef/browser/thread_util.h"
@@ -75,11 +76,23 @@
#if BUILDFLAG(IS_LINUX)
#include "base/path_service.h"
#include "chrome/browser/themes/theme_service_aura_linux.h"
#include "chrome/browser/ui/views/theme_profile_key.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/grit/chromium_strings.h"
#include "components/os_crypt/key_storage_config_linux.h"
#include "libcef/browser/printing/print_dialog_linux.h"
#include "ui/base/cursor/cursor_factory.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/linux/fake_input_method_context_factory.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/linux/linux_ui_delegate.h"
#include "ui/ozone/public/ozone_platform.h"
#include "ui/views/linux_ui/linux_ui.h"
#if BUILDFLAG(USE_GTK)
#include "ui/gtk/gtk_ui_factory.h"
#endif
#endif // BUILDFLAG(IS_LINUX)
#if BUILDFLAG(ENABLE_MEDIA_FOUNDATION_WIDEVINE_CDM)
@@ -90,6 +103,75 @@
#include "chrome/browser/component_updater/widevine_cdm_component_installer.h"
#endif
namespace {
#if BUILDFLAG(IS_LINUX)
std::unique_ptr<views::LinuxUI> BuildLinuxUI() {
// We can't use GtkUi in combination with multi-threaded-message-loop because
// Chromium's GTK implementation doesn't use GDK threads.
if (!!CefContext::Get()->settings().multi_threaded_message_loop)
return nullptr;
// If the ozone backend hasn't provided a LinuxUiDelegate, don't try to create
// a LinuxUi instance as this may result in a crash in toolkit initialization.
if (!ui::LinuxUiDelegate::GetInstance())
return nullptr;
// GtkUi is the only LinuxUI implementation for now.
#if BUILDFLAG(USE_GTK)
return BuildGtkUi();
#else
return nullptr;
#endif
}
// Based on chrome_browser_main_extra_parts_views_linux.cc
void ToolkitInitializedLinux() {
if (auto linux_ui = BuildLinuxUI()) {
linux_ui->SetUseSystemThemeCallback(
base::BindRepeating([](aura::Window* window) {
if (!window)
return true;
return ThemeServiceAuraLinux::ShouldUseSystemThemeForProfile(
GetThemeProfileForWindow(window));
}));
linux_ui->Initialize();
views::LinuxUI::SetInstance(std::move(linux_ui));
// Cursor theme changes are tracked by LinuxUI (via a CursorThemeManager
// implementation). Start observing them once it's initialized.
ui::CursorFactory::GetInstance()->ObserveThemeChanges();
} else {
// In case if GTK is not used, input method factory won't be set for X11 and
// Ozone/X11. Set a fake one instead to avoid crashing browser later.
DCHECK(!ui::LinuxInputMethodContextFactory::instance());
// Try to create input method through Ozone so that the backend has a chance
// to set factory by itself.
ui::OzonePlatform::GetInstance()->CreateInputMethod(
nullptr, gfx::kNullAcceleratedWidget);
}
// If factory is not set, set a fake instance.
if (!ui::LinuxInputMethodContextFactory::instance()) {
ui::LinuxInputMethodContextFactory::SetInstance(
new ui::FakeInputMethodContextFactory());
}
auto create_print_dialog_func =
printing::PrintingContextLinux::SetCreatePrintDialogFunction(
&CefPrintDialogLinux::CreatePrintDialog);
auto pdf_paper_size_func =
printing::PrintingContextLinux::SetPdfPaperSizeFunction(
&CefPrintDialogLinux::GetPdfPaperSize);
CefPrintDialogLinux::SetDefaultPrintingContextFuncs(create_print_dialog_func,
pdf_paper_size_func);
}
#endif // BUILDFLAG(IS_LINUX)
} // namespace
AlloyBrowserMainParts::AlloyBrowserMainParts(
content::MainFunctionParams parameters)
: BrowserMainParts(), parameters_(std::move(parameters)) {}
@@ -98,16 +180,6 @@ AlloyBrowserMainParts::~AlloyBrowserMainParts() {
constrained_window::SetConstrainedWindowViewsClient(nullptr);
}
int AlloyBrowserMainParts::PreEarlyInitialization() {
#if defined(USE_AURA) && BUILDFLAG(IS_LINUX)
// TODO(linux): Consider using a real input method or
// views::LinuxUI::SetInstance.
ui::InitializeInputMethodForTesting();
#endif
return content::RESULT_CODE_NORMAL_EXIT;
}
void AlloyBrowserMainParts::ToolkitInitialized() {
SetConstrainedWindowViewsClient(CreateCefConstrainedWindowViewsClient());
#if defined(USE_AURA)
@@ -122,6 +194,10 @@ void AlloyBrowserMainParts::ToolkitInitialized() {
#else
views_delegate_ = std::make_unique<views::DesktopTestViewsDelegate>();
#endif
#if BUILDFLAG(IS_LINUX)
ToolkitInitializedLinux();
#endif
}
void AlloyBrowserMainParts::PreCreateMainMessageLoop() {
@@ -148,11 +224,6 @@ void AlloyBrowserMainParts::PreCreateMainMessageLoop() {
void AlloyBrowserMainParts::PostCreateMainMessageLoop() {
#if BUILDFLAG(IS_LINUX)
printing::PrintingContextLinux::SetCreatePrintDialogFunction(
&CefPrintDialogLinux::CreatePrintDialog);
printing::PrintingContextLinux::SetPdfPaperSizeFunction(
&CefPrintDialogLinux::GetPdfPaperSize);
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
@@ -241,6 +312,7 @@ int AlloyBrowserMainParts::PreMainMessageLoopRun() {
PluginFinder::GetInstance()->Init();
scheme::RegisterWebUIControllerFactory();
file_dialog_runner::RegisterFactory();
#if BUILDFLAG(ENABLE_MEDIA_FOUNDATION_WIDEVINE_CDM) || \
BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)

View File

@@ -42,7 +42,6 @@ class AlloyBrowserMainParts : public content::BrowserMainParts {
~AlloyBrowserMainParts() override;
int PreEarlyInitialization() override;
void ToolkitInitialized() override;
void PreCreateMainMessageLoop() override;
void PostCreateMainMessageLoop() override;

View File

@@ -68,6 +68,7 @@
#include "chrome/browser/profiles/renderer_updater_factory.h"
#include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h"
#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/common/chrome_content_client.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
@@ -576,6 +577,12 @@ void AlloyContentBrowserClient::GetAdditionalViewSourceSchemes(
additional_schemes->push_back(extensions::kExtensionScheme);
}
std::unique_ptr<ui::SelectFilePolicy>
AlloyContentBrowserClient::CreateSelectFilePolicy(
content::WebContents* web_contents) {
return std::make_unique<ChromeSelectFilePolicy>(web_contents);
}
void AlloyContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem(
std::vector<std::string>* additional_allowed_schemes) {
ContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem(

View File

@@ -58,6 +58,8 @@ class AlloyContentBrowserClient : public content::ContentBrowserClient {
std::vector<std::string>* additional_schemes) override;
void GetAdditionalViewSourceSchemes(
std::vector<std::string>* additional_schemes) override;
std::unique_ptr<ui::SelectFilePolicy> CreateSelectFilePolicy(
content::WebContents* web_contents) override;
void GetAdditionalAllowedSchemesForFileSystem(
std::vector<std::string>* additional_allowed_schemes) override;
bool IsWebUIAllowedToMakeNetworkRequests(const url::Origin& origin) override;

View File

@@ -1,49 +0,0 @@
// Copyright 2021 The Chromium Embedded Framework 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/alloy/alloy_dialog_util.h"
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
#include "libcef/browser/extensions/browser_extensions_util.h"
#include "libcef/browser/file_dialog_runner.h"
#include "base/strings/utf_string_conversions.h"
namespace alloy {
void RunFileChooser(content::WebContents* web_contents,
const blink::mojom::FileChooserParams& params,
RunFileChooserCallback callback) {
CefRefPtr<AlloyBrowserHostImpl> browser = static_cast<AlloyBrowserHostImpl*>(
extensions::GetOwnerBrowserForHost(web_contents->GetRenderViewHost(),
nullptr)
.get());
if (!browser) {
LOG(ERROR) << "Failed to identify browser; canceling file dialog";
std::move(callback).Run(-1, {});
return;
}
CefFileDialogRunner::FileChooserParams cef_params;
cef_params.mode = params.mode;
cef_params.default_file_name = params.default_file_name;
cef_params.accept_types = params.accept_types;
browser->RunFileChooser(cef_params, std::move(callback));
}
// Based on net/base/filename_util_internal.cc FilePathToString16().
std::u16string FilePathTypeToString16(const base::FilePath::StringType& str) {
std::u16string result;
#if BUILDFLAG(IS_WIN)
result.assign(str.begin(), str.end());
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
if (!str.empty()) {
base::UTF8ToUTF16(str.c_str(), str.size(), &result);
}
#endif
return result;
}
} // namespace alloy

View File

@@ -1,37 +0,0 @@
// Copyright 2021 The Chromium Embedded Framework Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_ALLOY_ALLOY_DIALOG_UTIL_H_
#define CEF_LIBCEF_BROWSER_ALLOY_ALLOY_DIALOG_UTIL_H_
#pragma once
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/files/file_path.h"
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
namespace content {
class WebContents;
}
namespace alloy {
// The argument vector will be empty if the dialog was canceled.
using RunFileChooserCallback =
base::OnceCallback<void(int /*selected_accept_filter*/,
const std::vector<base::FilePath>& /*file_paths*/)>;
// Display the file chooser dialog. Execute |callback| on completion.
// Called from patched chrome/ files.
void RunFileChooser(content::WebContents* web_contents,
const blink::mojom::FileChooserParams& params,
RunFileChooserCallback callback);
std::u16string FilePathTypeToString16(const base::FilePath::StringType& str);
} // namespace alloy
#endif // CEF_LIBCEF_BROWSER_ALLOY_ALLOY_DIALOG_UTIL_H_

View File

@@ -129,13 +129,12 @@ base::Time ChromeProfileAlloy::GetStartTime() const {
}
base::FilePath ChromeProfileAlloy::last_selected_directory() {
NOTREACHED();
return base::FilePath();
return last_selected_directory_;
}
void ChromeProfileAlloy::set_last_selected_directory(
const base::FilePath& path) {
NOTREACHED();
last_selected_directory_ = path;
}
GURL ChromeProfileAlloy::GetHomePage() {

View File

@@ -8,6 +8,7 @@
#ifndef CEF_LIBCEF_BROWSER_ALLOY_CHROME_PROFILE_ALLOY_H_
#define CEF_LIBCEF_BROWSER_ALLOY_CHROME_PROFILE_ALLOY_H_
#include "base/files/file_path.h"
#include "chrome/browser/profiles/profile.h"
// This file provides a stub implementation of Chrome's Profile object for use
@@ -54,6 +55,7 @@ class ChromeProfileAlloy : public Profile {
private:
std::unique_ptr<variations::VariationsClient> variations_client_;
base::FilePath last_selected_directory_;
};
#endif // CEF_LIBCEF_BROWSER_ALLOY_CHROME_PROFILE_ALLOY_H_

View File

@@ -20,11 +20,14 @@
#include "chrome/browser/spellchecker/spellcheck_service.h"
#include "components/favicon/core/favicon_url.h"
#include "components/spellcheck/common/spellcheck_features.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/download_request_utils.h"
#include "content/public/browser/file_select_listener.h"
#include "content/public/browser/navigation_entry.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/shell_dialogs/select_file_policy.h"
#if BUILDFLAG(IS_MAC)
#include "components/spellcheck/browser/spellcheck_platform.h"
@@ -208,6 +211,31 @@ void CefBrowserHostBase::SetFocusInternal(bool focus) {
platform_delegate_->SetFocus(false);
}
void CefBrowserHostBase::RunFileDialog(
FileDialogMode mode,
const CefString& title,
const CefString& default_file_path,
const std::vector<CefString>& accept_filters,
CefRefPtr<CefRunFileDialogCallback> callback) {
DCHECK(callback);
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostBase::RunFileDialog,
this, mode, title, default_file_path,
accept_filters, callback));
return;
}
if (!callback || !EnsureFileDialogManager()) {
LOG(ERROR) << "File dialog canceled due to invalid state.";
if (callback)
callback->OnFileDialogDismissed({});
return;
}
file_dialog_manager_->RunFileDialog(mode, title, default_file_path,
accept_filters, callback);
}
void CefBrowserHostBase::StartDownload(const CefString& url) {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(
@@ -805,6 +833,45 @@ void CefBrowserHostBase::ViewText(const std::string& text) {
platform_delegate_->ViewText(text);
}
void CefBrowserHostBase::RunFileChooserForBrowser(
const blink::mojom::FileChooserParams& params,
CefFileDialogManager::RunFileChooserCallback callback) {
if (!EnsureFileDialogManager()) {
LOG(ERROR) << "File dialog canceled due to invalid state.";
std::move(callback).Run({});
return;
}
file_dialog_manager_->RunFileChooser(params, std::move(callback));
}
void CefBrowserHostBase::RunSelectFile(
ui::SelectFileDialog::Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy,
ui::SelectFileDialog::Type type,
const std::u16string& title,
const base::FilePath& default_path,
const ui::SelectFileDialog::FileTypeInfo* file_types,
int file_type_index,
const base::FilePath::StringType& default_extension,
gfx::NativeWindow owning_window,
void* params) {
if (!EnsureFileDialogManager()) {
LOG(ERROR) << "File dialog canceled due to invalid state.";
listener->FileSelectionCanceled(params);
return;
}
file_dialog_manager_->RunSelectFile(listener, std::move(policy), type, title,
default_path, file_types, file_type_index,
default_extension, owning_window, params);
}
void CefBrowserHostBase::SelectFileListenerDestroyed(
ui::SelectFileDialog::Listener* listener) {
if (file_dialog_manager_) {
file_dialog_manager_->SelectFileListenerDestroyed(listener);
}
}
bool CefBrowserHostBase::MaybeAllowNavigation(
content::RenderFrameHost* opener,
bool is_guest_view,
@@ -833,6 +900,13 @@ void CefBrowserHostBase::OnBeforeClose() {
void CefBrowserHostBase::OnBrowserDestroyed() {
CEF_REQUIRE_UIT();
// Destroy any platform constructs.
if (file_dialog_manager_) {
file_dialog_manager_->Destroy();
file_dialog_manager_.reset();
}
for (auto& observer : observers_)
observer.OnBrowserDestroyed(this);
}
@@ -878,13 +952,36 @@ CefRefPtr<CefBrowserView> CefBrowserHostBase::GetBrowserView() const {
return nullptr;
}
gfx::NativeWindow CefBrowserHostBase::GetTopLevelNativeWindow() const {
CEF_REQUIRE_UIT();
// Windowless browsers always return nullptr from GetTopLevelNativeWindow().
if (!IsWindowless()) {
auto web_contents = GetWebContents();
if (web_contents) {
return web_contents->GetTopLevelNativeWindow();
}
}
return gfx::NativeWindow();
}
bool CefBrowserHostBase::IsFocused() const {
CEF_REQUIRE_UIT();
auto web_contents = GetWebContents();
if (web_contents) {
return static_cast<content::RenderFrameHostImpl*>(
web_contents->GetMainFrame())
->IsFocused();
}
return false;
}
bool CefBrowserHostBase::EnsureDevToolsManager() {
CEF_REQUIRE_UIT();
if (!contents_delegate_->web_contents())
return false;
if (!devtools_manager_) {
devtools_manager_.reset(new CefDevToolsManager(this));
devtools_manager_ = std::make_unique<CefDevToolsManager>(this);
}
return true;
}
@@ -904,3 +1001,14 @@ void CefBrowserHostBase::InitializeDevToolsRegistrationOnUIThread(
return;
devtools_manager_->InitializeRegistrationOnUIThread(registration);
}
bool CefBrowserHostBase::EnsureFileDialogManager() {
CEF_REQUIRE_UIT();
if (!contents_delegate_->web_contents())
return false;
if (!file_dialog_manager_) {
file_dialog_manager_ = std::make_unique<CefFileDialogManager>(this);
}
return true;
}

View File

@@ -13,6 +13,7 @@
#include "libcef/browser/browser_info.h"
#include "libcef/browser/browser_platform_delegate.h"
#include "libcef/browser/devtools/devtools_manager.h"
#include "libcef/browser/file_dialog_manager.h"
#include "libcef/browser/frame_host_impl.h"
#include "libcef/browser/request_context_impl.h"
@@ -156,6 +157,11 @@ class CefBrowserHostBase : public CefBrowserHost,
CefRefPtr<CefRequestContext> GetRequestContext() override;
bool HasView() override;
void SetFocus(bool focus) override;
void RunFileDialog(FileDialogMode mode,
const CefString& title,
const CefString& default_file_path,
const std::vector<CefString>& accept_filters,
CefRefPtr<CefRunFileDialogCallback> callback) override;
void StartDownload(const CefString& url) override;
void DownloadImage(const CefString& image_url,
bool is_favicon,
@@ -234,6 +240,22 @@ class CefBrowserHostBase : public CefBrowserHost,
virtual void OnSetFocus(cef_focus_source_t source) = 0;
void ViewText(const std::string& text);
// Calls CefFileDialogManager methods.
void RunFileChooserForBrowser(
const blink::mojom::FileChooserParams& params,
CefFileDialogManager::RunFileChooserCallback callback);
void RunSelectFile(ui::SelectFileDialog::Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy,
ui::SelectFileDialog::Type type,
const std::u16string& title,
const base::FilePath& default_path,
const ui::SelectFileDialog::FileTypeInfo* file_types,
int file_type_index,
const base::FilePath::StringType& default_extension,
gfx::NativeWindow owning_window,
void* params);
void SelectFileListenerDestroyed(ui::SelectFileDialog::Listener* listener);
// Called from CefBrowserInfoManager::MaybeAllowNavigation.
virtual bool MaybeAllowNavigation(content::RenderFrameHost* opener,
bool is_guest_view,
@@ -269,13 +291,27 @@ class CefBrowserHostBase : public CefBrowserHost,
}
// Returns the Widget owner for the browser window. Only used with windowed
// rendering.
// browsers.
views::Widget* GetWindowWidget() const;
// Returns the BrowserView associated with this browser. Only used with Views-
// based browsers.
CefRefPtr<CefBrowserView> GetBrowserView() const;
// Returns the top-level native window for this browser. With windowed
// browsers this will be an aura::Window* on Aura platforms (Windows/Linux)
// and an NSWindow wrapper object from native_widget_types.h on MacOS. With
// windowless browsers this method will always return an empty value.
gfx::NativeWindow GetTopLevelNativeWindow() const;
// Returns true if this browser is currently focused. A browser is considered
// focused when the top-level RenderFrameHost is in the parent chain of the
// currently focused RFH within the frame tree. In addition, its associated
// RenderWidgetHost must also be focused. With windowed browsers only one
// browser should be focused at a time. With windowless browsers this relies
// on the client to properly configure focus state.
bool IsFocused() const;
protected:
bool EnsureDevToolsManager();
void InitializeDevToolsRegistrationOnUIThread(
@@ -286,6 +322,9 @@ class CefBrowserHostBase : public CefBrowserHost,
void SetFocusInternal(bool focus);
// Create the CefFileDialogManager if it doesn't already exist.
bool EnsureFileDialogManager();
// Thread-safe members.
CefBrowserSettings settings_;
CefRefPtr<CefClient> client_;
@@ -301,6 +340,9 @@ class CefBrowserHostBase : public CefBrowserHost,
// Only accessed on the UI thread.
base::ObserverList<Observer> observers_;
// Used for creating and managing file dialogs.
std::unique_ptr<CefFileDialogManager> file_dialog_manager_;
// Volatile state accessed from multiple threads. All access must be protected
// by |state_lock_|.
base::Lock state_lock_;

View File

@@ -223,12 +223,6 @@ CefEventHandle CefBrowserPlatformDelegate::GetEventHandle(
return kNullEventHandle;
}
std::unique_ptr<CefFileDialogRunner>
CefBrowserPlatformDelegate::CreateFileDialogRunner() {
NOTIMPLEMENTED();
return nullptr;
}
std::unique_ptr<CefJavaScriptDialogRunner>
CefBrowserPlatformDelegate::CreateJavaScriptDialogRunner() {
NOTIMPLEMENTED();

View File

@@ -63,7 +63,6 @@ class Widget;
struct CefBrowserCreateParams;
class CefBrowserHostBase;
class CefFileDialogRunner;
class CefJavaScriptDialogRunner;
class CefMenuRunner;
@@ -266,9 +265,6 @@ class CefBrowserPlatformDelegate {
virtual CefEventHandle GetEventHandle(
const content::NativeWebKeyboardEvent& event) const;
// Create the platform-specific file dialog runner.
virtual std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner();
// Create the platform-specific JavaScript dialog runner.
virtual std::unique_ptr<CefJavaScriptDialogRunner>
CreateJavaScriptDialogRunner();

View File

@@ -186,17 +186,6 @@ void ChromeBrowserHostImpl::SetZoomLevel(double zoomLevel) {
NOTIMPLEMENTED();
}
void ChromeBrowserHostImpl::RunFileDialog(
FileDialogMode mode,
const CefString& title,
const CefString& default_file_path,
const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) {
NOTIMPLEMENTED();
callback->OnFileDialogDismissed(0, {});
}
void ChromeBrowserHostImpl::Print() {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT, base::BindOnce(&ChromeBrowserHostImpl::Print, this));

View File

@@ -66,12 +66,6 @@ class ChromeBrowserHostImpl : public CefBrowserHostBase {
CefWindowHandle GetOpenerWindowHandle() override;
double GetZoomLevel() override;
void SetZoomLevel(double zoomLevel) override;
void RunFileDialog(FileDialogMode mode,
const CefString& title,
const CefString& default_file_path,
const std::vector<CefString>& accept_filters,
int selected_accept_filter,
CefRefPtr<CefRunFileDialogCallback> callback) override;
void Print() override;
void PrintToPDF(const CefString& path,
const CefPdfPrintSettings& settings,

View File

@@ -6,6 +6,7 @@
#include "libcef/browser/chrome/chrome_context_menu_handler.h"
#include "libcef/browser/context.h"
#include "libcef/browser/file_dialog_runner.h"
#include "libcef/browser/net/chrome_scheme_handler.h"
#include "base/task/post_task.h"
@@ -41,4 +42,5 @@ void ChromeBrowserMainExtraPartsCef::PreMainMessageLoopRun() {
scheme::RegisterWebUIControllerFactory();
context_menu::RegisterMenuCreatedCallback();
file_dialog_runner::RegisterFactory();
}

View File

@@ -106,7 +106,7 @@ void CefDevToolsFileManager::Save(const std::string& url,
}
}
CefFileDialogRunner::FileChooserParams params;
blink::mojom::FileChooserParams params;
params.mode = blink::mojom::FileChooserParams::Mode::kSave;
if (!initial_path.empty()) {
params.default_file_name = initial_path;
@@ -115,7 +115,7 @@ void CefDevToolsFileManager::Save(const std::string& url,
}
}
browser_impl_->RunFileChooser(
browser_impl_->RunFileChooserForBrowser(
params,
base::BindOnce(&CefDevToolsFileManager::SaveAsDialogDismissed,
weak_factory_.GetWeakPtr(), url, content,
@@ -127,7 +127,6 @@ void CefDevToolsFileManager::SaveAsDialogDismissed(
const std::string& content,
SaveCallback saveCallback,
CancelCallback cancelCallback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths) {
if (file_paths.size() == 1) {
SaveAsFileSelected(url, content, std::move(saveCallback), file_paths[0]);

View File

@@ -49,7 +49,6 @@ class CefDevToolsFileManager {
const std::string& content,
SaveCallback saveCallback,
CancelCallback cancelCallback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths);
void SaveAsFileSelected(const std::string& url,
const std::string& content,

View File

@@ -136,7 +136,7 @@ class CefBeforeDownloadCallbackImpl : public CefBeforeDownloadCallback {
if (browser.get()) {
handled = true;
CefFileDialogRunner::FileChooserParams params;
blink::mojom::FileChooserParams params;
params.mode = blink::mojom::FileChooserParams::Mode::kSave;
if (!suggested_path.empty()) {
params.default_file_name = suggested_path;
@@ -146,7 +146,7 @@ class CefBeforeDownloadCallbackImpl : public CefBeforeDownloadCallback {
}
}
browser->RunFileChooser(
browser->RunFileChooserForBrowser(
params,
base::BindOnce(
&CefBeforeDownloadCallbackImpl::ChooseDownloadPathCallback,
@@ -166,7 +166,6 @@ class CefBeforeDownloadCallbackImpl : public CefBeforeDownloadCallback {
static void ChooseDownloadPathCallback(
content::DownloadTargetCallback callback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths) {
DCHECK_LE(file_paths.size(), (size_t)1);

View File

@@ -4,18 +4,14 @@
#include "libcef/browser/extensions/api/file_system/cef_file_system_delegate.h"
#include "libcef/browser/alloy/alloy_dialog_util.h"
#include "apps/saved_files_service.h"
#include "base/callback.h"
#include "base/callback_forward.h"
#include "base/files/file_path.h"
#include "chrome/browser/extensions/api/file_system/file_entry_picker.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/api/file_system.h"
#include "extensions/common/extension.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
using blink::mojom::FileChooserParams;
namespace extensions {
namespace cef {
@@ -46,61 +42,17 @@ bool CefFileSystemDelegate::ShowSelectFileDialog(
return false;
}
absl::optional<FileChooserParams::Mode> mode;
switch (type) {
case ui::SelectFileDialog::Type::SELECT_UPLOAD_FOLDER:
mode = FileChooserParams::Mode::kUploadFolder;
break;
case ui::SelectFileDialog::Type::SELECT_SAVEAS_FILE:
mode = FileChooserParams::Mode::kSave;
break;
case ui::SelectFileDialog::Type::SELECT_OPEN_FILE:
mode = FileChooserParams::Mode::kOpen;
break;
case ui::SelectFileDialog::Type::SELECT_OPEN_MULTI_FILE:
mode = FileChooserParams::Mode::kOpenMultiple;
break;
default:
NOTIMPLEMENTED();
return false;
}
FileChooserParams params;
params.mode = *mode;
params.default_file_name = default_path;
if (file_types) {
// A list of allowed extensions. For example, it might be
// { { "htm", "html" }, { "txt" } }
for (auto& vec : file_types->extensions) {
for (auto& ext : vec) {
params.accept_types.push_back(
alloy::FilePathTypeToString16(FILE_PATH_LITERAL(".") + ext));
}
}
}
alloy::RunFileChooser(
web_contents, params,
base::BindOnce(&CefFileSystemDelegate::FileDialogDismissed,
weak_ptr_factory_.GetWeakPtr(),
std::move(files_selected_callback),
std::move(file_selection_canceled_callback)));
// The file picker will hold a reference to the ExtensionFunction
// instance, preventing its destruction (and subsequent sending of the
// function response) until the user has selected a file or cancelled the
// picker. At that point, the picker will delete itself, which will also free
// the function instance.
new FileEntryPicker(web_contents, default_path, *file_types, type,
std::move(files_selected_callback),
std::move(file_selection_canceled_callback));
return true;
}
void CefFileSystemDelegate::FileDialogDismissed(
FileSystemDelegate::FilesSelectedCallback files_selected_callback,
base::OnceClosure file_selection_canceled_callback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths) {
if (!file_paths.empty()) {
std::move(files_selected_callback).Run(file_paths);
} else {
std::move(file_selection_canceled_callback).Run();
}
}
void CefFileSystemDelegate::ConfirmSensitiveDirectoryAccess(
bool has_write_permission,
const std::u16string& app_name,

View File

@@ -10,7 +10,6 @@
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "extensions/browser/api/execute_code_function.h"
#include "extensions/browser/api/file_system/file_system_delegate.h"
#include "extensions/browser/extension_function.h"
@@ -51,15 +50,6 @@ class CefFileSystemDelegate : public FileSystemDelegate {
int GetDescriptionIdForAcceptType(const std::string& accept_type) override;
SavedFilesServiceInterface* GetSavedFilesService(
content::BrowserContext* browser_context) override;
private:
void FileDialogDismissed(
FileSystemDelegate::FilesSelectedCallback files_selected_callback,
base::OnceClosure file_selection_canceled_callback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths);
base::WeakPtrFactory<CefFileSystemDelegate> weak_ptr_factory_{this};
};
} // namespace cef

View File

@@ -102,11 +102,6 @@ CefEventHandle CefBrowserPlatformDelegateBackground::GetEventHandle(
return native_delegate_->GetEventHandle(event);
}
std::unique_ptr<CefFileDialogRunner>
CefBrowserPlatformDelegateBackground::CreateFileDialogRunner() {
return native_delegate_->CreateFileDialogRunner();
}
std::unique_ptr<CefJavaScriptDialogRunner>
CefBrowserPlatformDelegateBackground::CreateJavaScriptDialogRunner() {
return native_delegate_->CreateJavaScriptDialogRunner();

View File

@@ -40,7 +40,6 @@ class CefBrowserPlatformDelegateBackground
const content::NativeWebKeyboardEvent& event) override;
CefEventHandle GetEventHandle(
const content::NativeWebKeyboardEvent& event) const override;
std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner() override;
std::unique_ptr<CefJavaScriptDialogRunner> CreateJavaScriptDialogRunner()
override;
std::unique_ptr<CefMenuRunner> CreateMenuRunner() override;

View File

@@ -8,18 +8,22 @@
#include <utility>
#include "include/cef_dialog_handler.h"
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
#include "libcef/browser/browser_host_base.h"
#include "libcef/browser/context.h"
#include "libcef/browser/thread_util.h"
#include "chrome/browser/file_select_helper.h"
#include "content/public/browser/file_select_listener.h"
#include "content/public/browser/render_frame_host.h"
#include "net/base/directory_lister.h"
#include "ui/shell_dialogs/select_file_policy.h"
using blink::mojom::FileChooserParams;
namespace {
class CefFileDialogCallbackImpl : public CefFileDialogCallback {
public:
using CallbackType = CefFileDialogRunner::RunFileChooserCallback;
using CallbackType = CefFileDialogManager::RunFileChooserCallback;
explicit CefFileDialogCallbackImpl(CallbackType callback)
: callback_(std::move(callback)) {}
@@ -37,8 +41,7 @@ class CefFileDialogCallbackImpl : public CefFileDialogCallback {
}
}
void Continue(int selected_accept_filter,
const std::vector<CefString>& file_paths) override {
void Continue(const std::vector<CefString>& file_paths) override {
if (CEF_CURRENTLY_ON_UIT()) {
if (!callback_.is_null()) {
std::vector<base::FilePath> vec;
@@ -47,12 +50,12 @@ class CefFileDialogCallbackImpl : public CefFileDialogCallback {
for (; it != file_paths.end(); ++it)
vec.push_back(base::FilePath(*it));
}
std::move(callback_).Run(selected_accept_filter, vec);
std::move(callback_).Run(vec);
}
} else {
CEF_POST_TASK(CEF_UIT,
base::BindOnce(&CefFileDialogCallbackImpl::Continue, this,
selected_accept_filter, file_paths));
file_paths));
}
}
@@ -73,7 +76,7 @@ class CefFileDialogCallbackImpl : public CefFileDialogCallback {
static void CancelNow(CallbackType callback) {
CEF_REQUIRE_UIT();
std::vector<base::FilePath> file_paths;
std::move(callback).Run(0, file_paths);
std::move(callback).Run(file_paths);
}
CallbackType callback_;
@@ -82,77 +85,210 @@ class CefFileDialogCallbackImpl : public CefFileDialogCallback {
};
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);
callback->OnFileDialogDismissed(paths);
}
class UploadFolderHelper
: public net::DirectoryLister::DirectoryListerDelegate {
public:
explicit UploadFolderHelper(
CefFileDialogRunner::RunFileChooserCallback callback)
: callback_(std::move(callback)) {}
// Based on net/base/filename_util_internal.cc FilePathToString16().
std::u16string FilePathTypeToString16(const base::FilePath::StringType& str) {
std::u16string result;
#if BUILDFLAG(IS_WIN)
result.assign(str.begin(), str.end());
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
if (!str.empty()) {
base::UTF8ToUTF16(str.c_str(), str.size(), &result);
}
#endif
return result;
}
UploadFolderHelper(const UploadFolderHelper&) = delete;
UploadFolderHelper& operator=(const UploadFolderHelper&) = delete;
FileChooserParams SelectFileToFileChooserParams(
ui::SelectFileDialog::Type type,
const std::u16string& title,
const base::FilePath& default_path,
const ui::SelectFileDialog::FileTypeInfo* file_types) {
FileChooserParams params;
~UploadFolderHelper() override {
if (!callback_.is_null()) {
if (CEF_CURRENTLY_ON_UIT()) {
CancelNow(std::move(callback_));
} else {
CEF_POST_TASK(CEF_UIT, base::BindOnce(&UploadFolderHelper::CancelNow,
std::move(callback_)));
absl::optional<FileChooserParams::Mode> mode;
switch (type) {
case ui::SelectFileDialog::Type::SELECT_UPLOAD_FOLDER:
mode = FileChooserParams::Mode::kUploadFolder;
break;
case ui::SelectFileDialog::Type::SELECT_SAVEAS_FILE:
mode = FileChooserParams::Mode::kSave;
break;
case ui::SelectFileDialog::Type::SELECT_OPEN_FILE:
mode = FileChooserParams::Mode::kOpen;
break;
case ui::SelectFileDialog::Type::SELECT_OPEN_MULTI_FILE:
mode = FileChooserParams::Mode::kOpenMultiple;
break;
default:
NOTIMPLEMENTED();
return params;
}
params.mode = *mode;
params.title = title;
params.default_file_name = default_path;
// Note that this translation will lose any mime-type based filters that
// may have existed in the original FileChooserParams::accept_types if this
// dialog was created via FileSelectHelper::RunFileChooser.
if (file_types) {
// A list of allowed extensions. For example, it might be
// { { "htm", "html" }, { "txt" } }
for (auto& vec : file_types->extensions) {
for (auto& ext : vec) {
params.accept_types.push_back(
FilePathTypeToString16(FILE_PATH_LITERAL(".") + ext));
}
}
}
void OnListFile(
const net::DirectoryLister::DirectoryListerData& data) override {
CEF_REQUIRE_UIT();
if (!data.info.IsDirectory())
select_files_.push_back(data.path);
}
return params;
}
void OnListDone(int error) override {
CEF_REQUIRE_UIT();
if (!callback_.is_null()) {
std::move(callback_).Run(0, select_files_);
}
}
class CefFileSelectListener : public content::FileSelectListener {
public:
using CallbackType = CefFileDialogManager::RunFileChooserCallback;
explicit CefFileSelectListener(CallbackType callback)
: callback_(std::move(callback)) {}
private:
static void CancelNow(CefFileDialogRunner::RunFileChooserCallback callback) {
CEF_REQUIRE_UIT();
std::vector<base::FilePath> file_paths;
std::move(callback).Run(0, file_paths);
~CefFileSelectListener() override = default;
void FileSelected(std::vector<blink::mojom::FileChooserFileInfoPtr> files,
const base::FilePath& base_dir,
FileChooserParams::Mode mode) override {
std::vector<base::FilePath> paths;
if (mode == FileChooserParams::Mode::kUploadFolder) {
if (!base_dir.empty()) {
paths.push_back(base_dir);
}
} else if (!files.empty()) {
for (auto& file : files) {
if (file->is_native_file()) {
paths.push_back(file->get_native_file()->file_path);
} else {
NOTIMPLEMENTED();
}
}
}
std::move(callback_).Run(paths);
}
CefFileDialogRunner::RunFileChooserCallback callback_;
std::vector<base::FilePath> select_files_;
void FileSelectionCanceled() override { std::move(callback_).Run({}); }
CallbackType callback_;
};
} // namespace
CefFileDialogManager::CefFileDialogManager(
AlloyBrowserHostImpl* browser,
std::unique_ptr<CefFileDialogRunner> runner)
: browser_(browser),
runner_(std::move(runner)),
file_chooser_pending_(false),
weak_ptr_factory_(this) {}
class CefSelectFileDialogListener : public ui::SelectFileDialog::Listener {
public:
CefSelectFileDialogListener(ui::SelectFileDialog::Listener* listener,
void* params,
base::OnceClosure callback)
: listener_(listener), params_(params), callback_(std::move(callback)) {}
CefFileDialogManager::~CefFileDialogManager() {}
CefSelectFileDialogListener(const CefSelectFileDialogListener&) = delete;
CefSelectFileDialogListener& operator=(const CefSelectFileDialogListener&) =
delete;
void Cancel(bool listener_destroyed) {
if (executing_) {
// We're likely still on the stack. Do nothing and wait for Destroy().
return;
}
if (listener_destroyed) {
// Don't execute the listener.
Destroy();
} else {
FileSelectionCanceled(params_);
}
}
ui::SelectFileDialog::Listener* listener() const { return listener_; }
private:
~CefSelectFileDialogListener() override = default;
void FileSelected(const base::FilePath& path,
int index,
void* params) override {
DCHECK_EQ(params, params_);
executing_ = true;
listener_->FileSelected(path, index, params);
Destroy();
}
void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file,
int index,
void* params) override {
DCHECK_EQ(params, params_);
executing_ = true;
listener_->FileSelectedWithExtraInfo(file, index, params);
Destroy();
}
void MultiFilesSelected(const std::vector<base::FilePath>& files,
void* params) override {
DCHECK_EQ(params, params_);
executing_ = true;
listener_->MultiFilesSelected(files, params);
Destroy();
}
void MultiFilesSelectedWithExtraInfo(
const std::vector<ui::SelectedFileInfo>& files,
void* params) override {
DCHECK_EQ(params, params_);
executing_ = true;
listener_->MultiFilesSelectedWithExtraInfo(files, params);
Destroy();
}
void FileSelectionCanceled(void* params) override {
DCHECK_EQ(params, params_);
executing_ = true;
listener_->FileSelectionCanceled(params);
Destroy();
}
void Destroy() {
std::move(callback_).Run();
delete this;
}
ui::SelectFileDialog::Listener* const listener_;
void* const params_;
base::OnceClosure callback_;
// Used to avoid re-entrancy from Cancel().
bool executing_ = false;
};
CefFileDialogManager::CefFileDialogManager(CefBrowserHostBase* browser)
: browser_(browser) {}
CefFileDialogManager::~CefFileDialogManager() = default;
void CefFileDialogManager::Destroy() {
DCHECK(!file_chooser_pending_);
runner_.reset(nullptr);
if (dialog_listener_) {
// Cancel the listener and delete related objects.
SelectFileDoneByListenerCallback(/*listener_destroyed=*/false);
}
DCHECK(!dialog_);
DCHECK(!dialog_listener_);
DCHECK(active_listeners_.empty());
}
void CefFileDialogManager::RunFileDialog(
@@ -160,14 +296,13 @@ void CefFileDialogManager::RunFileDialog(
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) {
blink::mojom::FileChooserParams params;
switch (mode) {
case FILE_DIALOG_OPEN:
params.mode = blink::mojom::FileChooserParams::Mode::kOpen;
break;
@@ -182,12 +317,6 @@ void CefFileDialogManager::RunFileDialog(
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);
@@ -202,55 +331,145 @@ void CefFileDialogManager::RunFileDialog(
}
void CefFileDialogManager::RunFileChooser(
scoped_refptr<content::FileSelectListener> listener,
const blink::mojom::FileChooserParams& params) {
const blink::mojom::FileChooserParams& params,
RunFileChooserCallback callback) {
CEF_REQUIRE_UIT();
CefFileDialogRunner::FileChooserParams cef_params;
static_cast<blink::mojom::FileChooserParams&>(cef_params) = params;
CefFileDialogRunner::RunFileChooserCallback callback;
if (params.mode == blink::mojom::FileChooserParams::Mode::kUploadFolder) {
callback = base::BindOnce(
&CefFileDialogManager::OnRunFileChooserUploadFolderDelegateCallback,
weak_ptr_factory_.GetWeakPtr(), params.mode, listener);
} else {
callback =
base::BindOnce(&CefFileDialogManager::OnRunFileChooserDelegateCallback,
weak_ptr_factory_.GetWeakPtr(), params.mode, listener);
}
RunFileChooserInternal(cef_params, std::move(callback));
}
void CefFileDialogManager::RunFileChooser(
const CefFileDialogRunner::FileChooserParams& params,
CefFileDialogRunner::RunFileChooserCallback callback) {
CefFileDialogRunner::RunFileChooserCallback host_callback =
base::BindOnce(&CefFileDialogManager::OnRunFileChooserCallback,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
RunFileChooserInternal(params, std::move(host_callback));
}
void CefFileDialogManager::RunFileChooserInternal(
const CefFileDialogRunner::FileChooserParams& params,
CefFileDialogRunner::RunFileChooserCallback callback) {
CEF_REQUIRE_UIT();
if (file_chooser_pending_) {
// Dismiss the new dialog immediately.
std::move(callback).Run(0, std::vector<base::FilePath>());
// Execute the delegate with the most exact version of |params|. If not
// handled here there will be another call to the delegate from RunSelectFile.
// It might be better to execute the delegate only the single time here, but
// we don't currently have sufficient state in RunSelectFile to know that the
// delegate has already been executed.
callback = MaybeRunDelegate(params, std::move(callback));
if (callback.is_null()) {
// The delegate kept the callback.
return;
}
file_chooser_pending_ = true;
FileChooserParams new_params = params;
bool handled = false;
// Make sure we get native files in CefFileSelectListener.
new_params.need_local_path = true;
if (browser_->client().get()) {
CefRefPtr<CefDialogHandler> handler =
browser_->client()->GetDialogHandler();
if (handler.get()) {
// Requirements of FileSelectHelper.
if (params.mode != FileChooserParams::Mode::kSave) {
new_params.default_file_name = base::FilePath();
} else {
new_params.default_file_name = new_params.default_file_name.BaseName();
}
// FileSelectHelper is usually only used for renderer-initiated dialogs via
// WebContentsDelegate::RunFileChooser. We choose to use it here instead of
// calling ui::SelectFileDialog::Create directly because it provides some nice
// functionality related to default dialog settings and filter list
// generation. We customize the behavior slightly for non-renderer-initiated
// dialogs by passing the |run_from_cef=true| flag. FileSelectHelper uses
// ui::SelectFileDialog::Create internally and that call will be intercepted
// by CefSelectFileDialogFactory, resulting in call to RunSelectFile below.
// See related comments on CefSelectFileDialogFactory.
FileSelectHelper::RunFileChooser(
browser_->GetWebContents()->GetMainFrame(),
base::MakeRefCounted<CefFileSelectListener>(std::move(callback)),
new_params, /*run_from_cef=*/true);
}
void CefFileDialogManager::RunSelectFile(
ui::SelectFileDialog::Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy,
ui::SelectFileDialog::Type type,
const std::u16string& title,
const base::FilePath& default_path,
const ui::SelectFileDialog::FileTypeInfo* file_types,
int file_type_index,
const base::FilePath::StringType& default_extension,
gfx::NativeWindow owning_window,
void* params) {
CEF_REQUIRE_UIT();
active_listeners_.insert(listener);
// This will not be an exact representation of the original params.
auto chooser_params =
SelectFileToFileChooserParams(type, title, default_path, file_types);
auto callback =
base::BindOnce(&CefFileDialogManager::SelectFileDoneByDelegateCallback,
weak_ptr_factory_.GetWeakPtr(), base::Unretained(listener),
base::Unretained(params));
callback = MaybeRunDelegate(chooser_params, std::move(callback));
if (callback.is_null()) {
// The delegate kept the callback.
return;
}
if (dialog_) {
LOG(ERROR) << "Multiple simultaneous dialogs are not supported; "
"canceling the file dialog";
std::move(callback).Run({});
return;
}
#if BUILDFLAG(IS_LINUX)
// We can't use GtkUi in combination with multi-threaded-message-loop because
// Chromium's GTK implementation doesn't use GDK threads.
if (!!CefContext::Get()->settings().multi_threaded_message_loop) {
LOG(ERROR) << "Default dialog implementation is not available; "
"canceling the file dialog";
std::move(callback).Run({});
return;
}
#endif
// |callback| is no longer used at this point.
callback.Reset();
DCHECK(!dialog_listener_);
// This object will delete itself.
dialog_listener_ = new CefSelectFileDialogListener(
listener, params,
base::BindOnce(&CefFileDialogManager::SelectFileDoneByListenerCallback,
weak_ptr_factory_.GetWeakPtr(),
/*listener_destroyed=*/false));
// This call will not be intercepted by CefSelectFileDialogFactory due to the
// |run_from_cef=true| flag.
// See related comments on CefSelectFileDialogFactory.
dialog_ = ui::SelectFileDialog::Create(dialog_listener_, std::move(policy),
/*run_from_cef=*/true);
// With windowless rendering use the parent handle specified by the client.
if (browser_->IsWindowless()) {
DCHECK(!owning_window);
dialog_->set_owning_widget(browser_->GetWindowHandle());
}
dialog_->SelectFile(type, title, default_path, file_types, file_type_index,
default_extension, owning_window, params);
}
void CefFileDialogManager::SelectFileListenerDestroyed(
ui::SelectFileDialog::Listener* listener) {
CEF_REQUIRE_UIT();
DCHECK(listener);
// This notification will arrive from whomever owns |listener|, so we don't
// want to execute any |listener| methods after this point.
if (dialog_listener_ && listener == dialog_listener_->listener()) {
// Cancel the currently active dialog.
SelectFileDoneByListenerCallback(/*listener_destroyed=*/true);
} else {
// Any future SelectFileDoneByDelegateCallback call for |listener| becomes a
// no-op.
active_listeners_.erase(listener);
}
}
CefFileDialogManager::RunFileChooserCallback
CefFileDialogManager::MaybeRunDelegate(
const blink::mojom::FileChooserParams& params,
RunFileChooserCallback callback) {
if (auto client = browser_->client()) {
if (auto handler = browser_->client()->GetDialogHandler()) {
int mode = FILE_DIALOG_OPEN;
switch (params.mode) {
case blink::mojom::FileChooserParams::Mode::kOpen:
@@ -270,11 +489,6 @@ void CefFileDialogManager::RunFileChooserInternal(
break;
}
if (params.overwriteprompt)
mode |= FILE_DIALOG_OVERWRITEPROMPT_FLAG;
if (params.hidereadonly)
mode |= FILE_DIALOG_HIDEREADONLY_FLAG;
std::vector<std::u16string>::const_iterator it;
std::vector<CefString> accept_filters;
@@ -284,10 +498,9 @@ void CefFileDialogManager::RunFileChooserInternal(
CefRefPtr<CefFileDialogCallbackImpl> callbackImpl(
new CefFileDialogCallbackImpl(std::move(callback)));
handled = handler->OnFileDialog(
const bool 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());
params.default_file_name.value(), accept_filters, callbackImpl.get());
if (!handled) {
// May return nullptr if the client has already executed the callback.
callback = callbackImpl->Disconnect();
@@ -295,82 +508,57 @@ void CefFileDialogManager::RunFileChooserInternal(
}
}
if (!handled && !callback.is_null()) {
if (runner_.get()) {
runner_->Run(browser_, params, std::move(callback));
} else {
LOG(WARNING) << "No file dialog runner available for this platform";
std::move(callback).Run(0, std::vector<base::FilePath>());
}
}
return callback;
}
void CefFileDialogManager::OnRunFileChooserCallback(
CefFileDialogRunner::RunFileChooserCallback callback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths) {
void CefFileDialogManager::SelectFileDoneByDelegateCallback(
ui::SelectFileDialog::Listener* listener,
void* params,
const std::vector<base::FilePath>& paths) {
CEF_REQUIRE_UIT();
Cleanup();
// The listener may already be gone. This can occur if the client holds a
// RunFileChooserCallback past the call to SelectFileListenerDestroyed().
if (active_listeners_.find(listener) == active_listeners_.end())
return;
// Execute the callback asynchronously.
CEF_POST_TASK(CEF_UIT, base::BindOnce(std::move(callback),
selected_accept_filter, file_paths));
}
active_listeners_.erase(listener);
void CefFileDialogManager::OnRunFileChooserUploadFolderDelegateCallback(
const blink::mojom::FileChooserParams::Mode mode,
scoped_refptr<content::FileSelectListener> listener,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths) {
CEF_REQUIRE_UIT();
DCHECK_EQ(mode, blink::mojom::FileChooserParams::Mode::kUploadFolder);
if (file_paths.size() == 0) {
// Client canceled the file chooser.
OnRunFileChooserDelegateCallback(mode, listener, selected_accept_filter,
file_paths);
if (paths.empty()) {
listener->FileSelectionCanceled(params);
} else if (paths.size() == 1) {
listener->FileSelected(paths[0], /*index=*/0, params);
} else {
lister_.reset(new net::DirectoryLister(
file_paths[0], net::DirectoryLister::NO_SORT_RECURSIVE,
new UploadFolderHelper(base::BindOnce(
&CefFileDialogManager::OnRunFileChooserDelegateCallback,
weak_ptr_factory_.GetWeakPtr(), mode, listener))));
lister_->Start();
listener->MultiFilesSelected(paths, params);
}
// |listener| is likely deleted at this point.
}
void CefFileDialogManager::OnRunFileChooserDelegateCallback(
blink::mojom::FileChooserParams::Mode mode,
scoped_refptr<content::FileSelectListener> listener,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths) {
void CefFileDialogManager::SelectFileDoneByListenerCallback(
bool listener_destroyed) {
CEF_REQUIRE_UIT();
base::FilePath base_dir;
std::vector<blink::mojom::FileChooserFileInfoPtr> selected_files;
// Avoid re-entrancy of this method. CefSelectFileDialogListener callbacks to
// the delegated listener may result in an immediate call to
// SelectFileListenerDestroyed() while |dialog_listener_| is still on the
// stack, followed by another execution from
// CefSelectFileDialogListener::Destroy(). Similarly, the below call to
// Cancel() may trigger another execution from
// CefSelectFileDialogListener::Destroy().
if (!dialog_listener_)
return;
if (!file_paths.empty()) {
if (mode == blink::mojom::FileChooserParams::Mode::kUploadFolder) {
base_dir = file_paths[0].DirName();
}
DCHECK(dialog_);
DCHECK(dialog_listener_);
// Convert FilePath list to SelectedFileInfo list.
for (size_t i = 0; i < file_paths.size(); ++i) {
auto info = blink::mojom::FileChooserFileInfo::NewNativeFile(
blink::mojom::NativeFileInfo::New(file_paths[i], std::u16string()));
selected_files.push_back(std::move(info));
}
}
active_listeners_.erase(dialog_listener_->listener());
listener->FileSelected(std::move(selected_files), base_dir, mode);
// Clear |dialog_listener_| before calling Cancel() to avoid re-entrancy.
auto dialog_listener = dialog_listener_;
dialog_listener_ = nullptr;
dialog_listener->Cancel(listener_destroyed);
Cleanup();
}
void CefFileDialogManager::Cleanup() {
if (lister_)
lister_.reset();
file_chooser_pending_ = false;
// There should be no further listener callbacks after this call.
dialog_->ListenerDestroyed();
dialog_ = nullptr;
}

View File

@@ -7,28 +7,25 @@
#define CEF_LIBCEF_BROWSER_FILE_DIALOG_MANAGER_H_
#pragma once
#include "include/cef_browser.h"
#include "libcef/browser/file_dialog_runner.h"
#include <memory>
#include <set>
#include "base/memory/weak_ptr.h"
#include "content/public/browser/web_contents_observer.h"
#include "include/cef_browser.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
#include "ui/shell_dialogs/select_file_dialog.h"
namespace content {
class FileSelectListener;
class WebContents;
} // namespace content
namespace net {
class DirectoryLister;
}
class AlloyBrowserHostImpl;
class CefBrowserHostBase;
class CefSelectFileDialogListener;
class CefFileDialogManager {
public:
// |runner| may be NULL if the platform doesn't implement dialogs.
CefFileDialogManager(AlloyBrowserHostImpl* browser,
std::unique_ptr<CefFileDialogRunner> runner);
explicit CefFileDialogManager(CefBrowserHostBase* browser);
CefFileDialogManager(const CefFileDialogManager&) = delete;
CefFileDialogManager& operator=(const CefFileDialogManager&) = delete;
@@ -38,70 +35,65 @@ class CefFileDialogManager {
// Delete the runner to free any platform constructs.
void Destroy();
// Called from AlloyBrowserHostImpl::RunFileChooser.
// See CefBrowserHost::RunFileDialog documentation.
// Run a file dialog with the specified parameters. See
// CefBrowserHost::RunFileDialog for usage documentation. This method should
// be called via CefBrowserHostBase::RunFileDialog.
void 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);
// Called from AlloyBrowserHostImpl::RunFileChooser.
// See WebContentsDelegate::RunFileChooser documentation.
void RunFileChooser(scoped_refptr<content::FileSelectListener> listener,
const blink::mojom::FileChooserParams& params);
// The argument vector will be empty if the dialog was canceled.
using RunFileChooserCallback =
base::OnceCallback<void(const std::vector<base::FilePath>&)>;
// 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 CefFileDialogRunner::FileChooserParams& params,
CefFileDialogRunner::RunFileChooserCallback callback);
// Run the file dialog specified by |params|. |callback| will be executed
// synchronously or asynchronously after the dialog is dismissed. This method
// should be called via CefBrowserHostBase::RunFileChooser.
void RunFileChooser(const blink::mojom::FileChooserParams& params,
RunFileChooserCallback callback);
// Run a ui::SelectFileDialog with the specified parameters. See
// ui::SelectFileDialog for usage documentation. This method should be called
// via CefBrowserHostBase::RunSelectFile. It will be called for all file
// dialogs after interception via CefSelectFileDialog::SelectFileImpl.
void RunSelectFile(ui::SelectFileDialog::Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy,
ui::SelectFileDialog::Type type,
const std::u16string& title,
const base::FilePath& default_path,
const ui::SelectFileDialog::FileTypeInfo* file_types,
int file_type_index,
const base::FilePath::StringType& default_extension,
gfx::NativeWindow owning_window,
void* params);
// Must be called when the |listener| passed to RunSelectFile is destroyed.
void SelectFileListenerDestroyed(ui::SelectFileDialog::Listener* listener);
private:
void RunFileChooserInternal(
const CefFileDialogRunner::FileChooserParams& params,
CefFileDialogRunner::RunFileChooserCallback callback);
[[nodiscard]] RunFileChooserCallback MaybeRunDelegate(
const blink::mojom::FileChooserParams& params,
RunFileChooserCallback callback);
// Used with the RunFileChooser variant where the caller specifies a callback
// (no associated RenderFrameHost).
void OnRunFileChooserCallback(
CefFileDialogRunner::RunFileChooserCallback callback,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths);
void SelectFileDoneByDelegateCallback(
ui::SelectFileDialog::Listener* listener,
void* params,
const std::vector<base::FilePath>& paths);
void SelectFileDoneByListenerCallback(bool listener_destroyed);
// Used with WebContentsDelegate::RunFileChooser when mode is
// blink::mojom::FileChooserParams::Mode::kUploadFolder.
void OnRunFileChooserUploadFolderDelegateCallback(
const blink::mojom::FileChooserParams::Mode mode,
scoped_refptr<content::FileSelectListener> listener,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths);
// CefBrowserHostBase pointer is guaranteed to outlive this object.
CefBrowserHostBase* const browser_;
// Used with WebContentsDelegate::RunFileChooser to notify the
// RenderFrameHost.
void OnRunFileChooserDelegateCallback(
blink::mojom::FileChooserParams::Mode mode,
scoped_refptr<content::FileSelectListener> listener,
int selected_accept_filter,
const std::vector<base::FilePath>& file_paths);
// Used when running a platform dialog via RunSelectFile.
scoped_refptr<ui::SelectFileDialog> dialog_;
CefSelectFileDialogListener* dialog_listener_ = nullptr;
// Clean up state associated with the last run.
void Cleanup();
// List of all currently active listeners.
std::set<ui::SelectFileDialog::Listener*> active_listeners_;
// AlloyBrowserHostImpl pointer is guaranteed to outlive this object.
AlloyBrowserHostImpl* browser_;
std::unique_ptr<CefFileDialogRunner> runner_;
// True if a file chooser is currently pending.
bool file_chooser_pending_;
// Used for asynchronously listing directory contents.
std::unique_ptr<net::DirectoryLister> lister_;
// Must be the last member.
base::WeakPtrFactory<CefFileDialogManager> weak_ptr_factory_;
base::WeakPtrFactory<CefFileDialogManager> weak_ptr_factory_{this};
};
#endif // CEF_LIBCEF_BROWSER_JAVASCRIPT_DIALOG_MANAGER_H_

View File

@@ -0,0 +1,189 @@
// Copyright (c) 2022 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_runner.h"
#include "libcef/browser/browser_host_base.h"
#include "libcef/browser/browser_info_manager.h"
#include "libcef/browser/extensions/browser_extensions_util.h"
#include "base/memory/singleton.h"
#include "chrome/browser/file_select_helper.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "ui/shell_dialogs/select_file_dialog_factory.h"
#include "ui/shell_dialogs/select_file_policy.h"
using blink::mojom::FileChooserParams;
namespace {
// Creation of a file dialog can be triggered via various code paths, but they
// all eventually result in a call to ui::SelectFileDialog::Create. We intercept
// that call with CefSelectFileDialogFactory and redirect it to
// CefFileDialogManager::RunSelectFile. After triggering the CefDialogHandler
// callbacks that method calls ui::SelectFileDialog::Create again with
// |run_from_cef=false| to trigger creation of the default platform dialog.
class CefSelectFileDialogFactory final : public ui::SelectFileDialogFactory {
public:
CefSelectFileDialogFactory(const CefSelectFileDialogFactory&) = delete;
CefSelectFileDialogFactory& operator=(const CefSelectFileDialogFactory&) =
delete;
static CefSelectFileDialogFactory* GetInstance() {
// Leaky because there is no useful cleanup to do.
return base::Singleton<
CefSelectFileDialogFactory,
base::LeakySingletonTraits<CefSelectFileDialogFactory>>::get();
}
ui::SelectFileDialog* Create(
ui::SelectFileDialog::Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy) override;
bool IsCefFactory() const override { return true; }
private:
friend struct base::DefaultSingletonTraits<CefSelectFileDialogFactory>;
CefSelectFileDialogFactory() { ui::SelectFileDialog::SetFactory(this); }
};
CefRefPtr<CefBrowserHostBase> GetBrowserForTopLevelNativeWindow(
gfx::NativeWindow owning_window) {
DCHECK(owning_window);
for (const auto& browser_info :
CefBrowserInfoManager::GetInstance()->GetBrowserInfoList()) {
if (auto browser = browser_info->browser()) {
if (browser->GetTopLevelNativeWindow() == owning_window)
return browser;
}
}
return nullptr;
}
CefRefPtr<CefBrowserHostBase> GetLikelyFocusedBrowser() {
for (const auto& browser_info :
CefBrowserInfoManager::GetInstance()->GetBrowserInfoList()) {
if (auto browser = browser_info->browser()) {
if (browser->IsFocused())
return browser;
}
}
return nullptr;
}
// Delegates the running of the dialog to CefFileDialogManager.
class CefSelectFileDialog final : public ui::SelectFileDialog {
public:
CefSelectFileDialog(ui::SelectFileDialog::Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy)
: ui::SelectFileDialog(listener, std::move(policy)) {
DCHECK(listener_);
}
CefSelectFileDialog(const CefSelectFileDialog&) = delete;
CefSelectFileDialog& operator=(const CefSelectFileDialog&) = delete;
void SelectFileImpl(Type type,
const std::u16string& title,
const base::FilePath& default_path,
const FileTypeInfo* file_types,
int file_type_index,
const base::FilePath::StringType& default_extension,
gfx::NativeWindow owning_window,
void* params) override {
// Try to determine the associated browser (with decreasing levels of
// confidence).
// 1. Browser associated with the SelectFilePolicy. This is the most
// reliable mechanism if specified at the SelectFileDialog::Create call
// site.
if (select_file_policy_) {
auto chrome_policy =
static_cast<ChromeSelectFilePolicy*>(select_file_policy_.get());
auto web_contents = chrome_policy->source_contents();
if (web_contents) {
browser_ = extensions::GetOwnerBrowserForHost(
web_contents->GetRenderViewHost(), nullptr);
}
if (!browser_) {
LOG(WARNING) << "No browser associated with SelectFilePolicy";
}
}
// 2. Browser associated with the top-level native window (owning_window).
// This should be reliable with windowed browsers. However, |owning_window|
// will always be nullptr with windowless browsers.
if (!browser_ && owning_window) {
browser_ = GetBrowserForTopLevelNativeWindow(owning_window);
if (!browser_) {
LOG(WARNING) << "No browser associated with top-level native window";
}
}
// 3. Browser most likely to be focused. This may be somewhat iffy with
// windowless browsers as there is no guarantee that the client has only
// one browser focused at a time.
if (!browser_) {
browser_ = GetLikelyFocusedBrowser();
if (!browser_) {
LOG(WARNING) << "No likely focused browser";
}
}
if (!browser_) {
LOG(ERROR)
<< "Failed to identify associated browser; canceling the file dialog";
listener_->FileSelectionCanceled(params);
return;
}
owning_window_ = owning_window;
has_multiple_file_choices_ =
file_types ? file_types->extensions.size() > 1 : true;
browser_->RunSelectFile(listener_, std::move(select_file_policy_), type,
title, default_path, file_types, file_type_index,
default_extension, owning_window, params);
}
bool IsRunning(gfx::NativeWindow owning_window) const override {
return owning_window == owning_window_;
}
void ListenerDestroyed() override {
if (browser_)
browser_->SelectFileListenerDestroyed(listener_);
listener_ = nullptr;
}
bool HasMultipleFileTypeChoicesImpl() override {
return has_multiple_file_choices_;
}
private:
gfx::NativeWindow owning_window_ = nullptr;
bool has_multiple_file_choices_ = false;
CefRefPtr<CefBrowserHostBase> browser_;
};
ui::SelectFileDialog* CefSelectFileDialogFactory::Create(
ui::SelectFileDialog::Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy) {
return new CefSelectFileDialog(listener, std::move(policy));
}
} // namespace
namespace file_dialog_runner {
void RegisterFactory() {
// Implicitly registers on creation.
CefSelectFileDialogFactory::GetInstance();
}
} // namespace file_dialog_runner

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2012 The Chromium Embedded Framework Authors.
// Copyright (c) 2022 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.
@@ -7,46 +7,11 @@
#define CEF_LIBCEF_BROWSER_FILE_DIALOG_RUNNER_H_
#pragma once
#include <vector>
namespace file_dialog_runner {
#include "base/callback.h"
#include "base/files/file_path.h"
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
// One-time registration on startup.
void RegisterFactory();
class AlloyBrowserHostImpl;
class CefFileDialogRunner {
public:
CefFileDialogRunner(const CefFileDialogRunner&) = delete;
CefFileDialogRunner& operator=(const CefFileDialogRunner&) = delete;
// Extend blink::mojom::FileChooserParams with some options unique to CEF.
struct FileChooserParams : public blink::mojom::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;
};
// The argument vector will be empty if the dialog was canceled.
using RunFileChooserCallback =
base::OnceCallback<void(int, const std::vector<base::FilePath>&)>;
// Display the file chooser dialog. Execute |callback| on completion.
virtual void Run(AlloyBrowserHostImpl* browser,
const FileChooserParams& params,
RunFileChooserCallback callback) = 0;
protected:
// Allow deletion via std::unique_ptr only.
friend std::default_delete<CefFileDialogRunner>;
CefFileDialogRunner() = default;
virtual ~CefFileDialogRunner() = default;
};
} // namespace file_dialog_runner
#endif // CEF_LIBCEF_BROWSER_FILE_DIALOG_RUNNER_H_

View File

@@ -40,7 +40,6 @@ class CefBrowserPlatformDelegateNativeMac
const content::NativeWebKeyboardEvent& event) override;
CefEventHandle GetEventHandle(
const content::NativeWebKeyboardEvent& event) const override;
std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner() override;
std::unique_ptr<CefJavaScriptDialogRunner> CreateJavaScriptDialogRunner()
override;
std::unique_ptr<CefMenuRunner> CreateMenuRunner() override;

View File

@@ -9,7 +9,6 @@
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
#include "libcef/browser/context.h"
#include "libcef/browser/native/file_dialog_runner_mac.h"
#include "libcef/browser/native/javascript_dialog_runner_mac.h"
#include "libcef/browser/native/menu_runner_mac.h"
#include "libcef/browser/thread_util.h"
@@ -363,11 +362,6 @@ CefEventHandle CefBrowserPlatformDelegateNativeMac::GetEventHandle(
return event.os_event;
}
std::unique_ptr<CefFileDialogRunner>
CefBrowserPlatformDelegateNativeMac::CreateFileDialogRunner() {
return base::WrapUnique(new CefFileDialogRunnerMac);
}
std::unique_ptr<CefJavaScriptDialogRunner>
CefBrowserPlatformDelegateNativeMac::CreateJavaScriptDialogRunner() {
return base::WrapUnique(new CefJavaScriptDialogRunnerMac);

View File

@@ -10,7 +10,6 @@
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
#include "libcef/browser/context.h"
#include "libcef/browser/native/file_dialog_runner_win.h"
#include "libcef/browser/native/javascript_dialog_runner_win.h"
#include "libcef/browser/native/menu_runner_win.h"
#include "libcef/browser/native/window_delegate_view.h"
@@ -447,11 +446,6 @@ CefEventHandle CefBrowserPlatformDelegateNativeWin::GetEventHandle(
const_cast<CHROME_MSG*>(&event.os_event->native_event()));
}
std::unique_ptr<CefFileDialogRunner>
CefBrowserPlatformDelegateNativeWin::CreateFileDialogRunner() {
return base::WrapUnique(new CefFileDialogRunnerWin);
}
std::unique_ptr<CefJavaScriptDialogRunner>
CefBrowserPlatformDelegateNativeWin::CreateJavaScriptDialogRunner() {
return base::WrapUnique(new CefJavaScriptDialogRunnerWin);

View File

@@ -34,7 +34,6 @@ class CefBrowserPlatformDelegateNativeWin
const content::NativeWebKeyboardEvent& event) override;
CefEventHandle GetEventHandle(
const content::NativeWebKeyboardEvent& event) const override;
std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner() override;
std::unique_ptr<CefJavaScriptDialogRunner> CreateJavaScriptDialogRunner()
override;
std::unique_ptr<CefMenuRunner> CreateMenuRunner() override;

View File

@@ -1,41 +0,0 @@
// 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.
#ifndef CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_MAC_H_
#define CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_MAC_H_
#pragma once
#include "libcef/browser/file_dialog_runner.h"
#include "base/memory/weak_ptr.h"
@class NSView;
class CefFileDialogRunnerMac : public CefFileDialogRunner {
public:
CefFileDialogRunnerMac();
// CefFileDialogRunner methods:
void Run(AlloyBrowserHostImpl* browser,
const FileChooserParams& params,
RunFileChooserCallback callback) override;
private:
static void RunOpenFileDialog(
base::WeakPtr<CefFileDialogRunnerMac> weak_this,
const CefFileDialogRunner::FileChooserParams& params,
NSView* view,
int filter_index);
static void RunSaveFileDialog(
base::WeakPtr<CefFileDialogRunnerMac> weak_this,
const CefFileDialogRunner::FileChooserParams& params,
NSView* view,
int filter_index);
CefFileDialogRunner::RunFileChooserCallback callback_;
base::WeakPtrFactory<CefFileDialogRunnerMac> weak_ptr_factory_;
};
#endif // CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_MAC_H_

View File

@@ -1,405 +0,0 @@
// 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/native/file_dialog_runner_mac.h"
#import <Cocoa/Cocoa.h>
#import <CoreServices/CoreServices.h>
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
#include "base/mac/mac_util.h"
#include "base/stl_util.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"
#include "base/threading/thread_restrictions.h"
#include "cef/grit/cef_strings.h"
#include "chrome/grit/generated_resources.h"
#include "net/base/mime_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/strings/grit/ui_strings.h"
namespace {
std::u16string 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_AUDIO_FILES},
{"image", IDS_IMAGE_FILES},
{"text", IDS_TEXT_FILES},
{"video", IDS_VIDEO_FILES},
};
for (size_t i = 0; i < std::size(kWildCardMimeTypes); ++i) {
if (mime_type == std::string(kWildCardMimeTypes[i].mime_type) + "/*")
return l10n_util::GetStringUTF16(kWildCardMimeTypes[i].string_id);
}
return std::u16string();
}
void AddFilters(NSPopUpButton* button,
const std::vector<std::u16string>& accept_filters,
bool include_all_files,
std::vector<std::vector<std::u16string>>* all_extensions) {
for (size_t i = 0; i < accept_filters.size(); ++i) {
const std::u16string& filter = accept_filters[i];
if (filter.empty())
continue;
std::vector<std::u16string> extensions;
std::u16string 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);
const std::vector<std::u16string>& ext =
base::SplitString(filter.substr(sep_index + 1), u";",
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (size_t x = 0; x < ext.size(); ++x) {
const std::u16string& 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(u"." + 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;
std::u16string ext_str;
for (size_t x = 0; x < std::min(kMaxExtensions, extensions.size()); ++x) {
const std::u16string& pattern = u"*" + extensions[x];
if (x != 0)
ext_str += u";";
ext_str += pattern;
}
if (extensions.size() > kMaxExtensions)
ext_str += u";...";
if (description.empty()) {
description = ext_str;
} else {
description += u" (" + ext_str + u")";
}
[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<std::u16string>());
}
}
} // namespace
// Used to manage the file type filter in the NSSavePanel/NSOpenPanel.
@interface CefFilterDelegate : NSObject {
@private
NSSavePanel* panel_;
std::vector<std::vector<std::u16string>> extensions_;
int selected_index_;
}
- (id)initWithPanel:(NSSavePanel*)panel
andAcceptFilters:(const std::vector<std::u16string>&)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<std::u16string>&)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<std::u16string>& 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.
std::u16string 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
CefFileDialogRunnerMac::CefFileDialogRunnerMac() : weak_ptr_factory_(this) {}
void CefFileDialogRunnerMac::Run(AlloyBrowserHostImpl* browser,
const FileChooserParams& params,
RunFileChooserCallback callback) {
callback_ = std::move(callback);
int filter_index = params.selected_accept_filter;
NSView* owner = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(browser->GetWindowHandle());
auto weak_this = weak_ptr_factory_.GetWeakPtr();
if (params.mode == blink::mojom::FileChooserParams::Mode::kOpen ||
params.mode == blink::mojom::FileChooserParams::Mode::kOpenMultiple ||
params.mode == blink::mojom::FileChooserParams::Mode::kUploadFolder) {
RunOpenFileDialog(weak_this, params, owner, filter_index);
} else if (params.mode == blink::mojom::FileChooserParams::Mode::kSave) {
RunSaveFileDialog(weak_this, params, owner, filter_index);
} else {
NOTIMPLEMENTED();
}
}
// static
void CefFileDialogRunnerMac::RunOpenFileDialog(
base::WeakPtr<CefFileDialogRunnerMac> weak_this,
const CefFileDialogRunner::FileChooserParams& params,
NSView* view,
int filter_index) {
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
std::u16string title;
if (!params.title.empty()) {
title = params.title;
} else {
title = l10n_util::GetStringUTF16(
params.mode == blink::mojom::FileChooserParams::Mode::kOpen
? IDS_OPEN_FILE_DIALOG_TITLE
: (params.mode ==
blink::mojom::FileChooserParams::Mode::kOpenMultiple
? IDS_OPEN_FILES_DIALOG_TITLE
: IDS_SELECT_FOLDER_DIALOG_TITLE));
}
[openPanel setTitle:base::SysUTF16ToNSString(title)];
std::string filename, directory;
if (!params.default_file_name.empty()) {
if (params.mode == blink::mojom::FileChooserParams::Mode::kUploadFolder ||
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)]];
}
CefFilterDelegate* filter_delegate = nil;
if (params.mode != blink::mojom::FileChooserParams::Mode::kUploadFolder &&
!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 ==
blink::mojom::FileChooserParams::Mode::kOpenMultiple)];
[openPanel
setCanChooseFiles:(params.mode !=
blink::mojom::FileChooserParams::Mode::kUploadFolder)];
[openPanel
setCanChooseDirectories:(params.mode == blink::mojom::FileChooserParams::
Mode::kUploadFolder)];
[openPanel setShowsHiddenFiles:!params.hidereadonly];
// Show panel.
[openPanel
beginSheetModalForWindow:[view window]
completionHandler:^(NSInteger returnCode) {
int filter_index_to_use = (filter_delegate != nil)
? [filter_delegate filter]
: filter_index;
if (returnCode == NSFileHandlingPanelOKButton) {
std::vector<base::FilePath> files;
files.reserve(openPanel.URLs.count);
for (NSURL* url in openPanel.URLs) {
if (url.isFileURL)
files.push_back(base::FilePath(url.path.UTF8String));
}
std::move(weak_this->callback_)
.Run(filter_index_to_use, files);
} else {
std::move(weak_this->callback_)
.Run(filter_index_to_use, std::vector<base::FilePath>());
}
}];
}
// static
void CefFileDialogRunnerMac::RunSaveFileDialog(
base::WeakPtr<CefFileDialogRunnerMac> weak_this,
const CefFileDialogRunner::FileChooserParams& params,
NSView* view,
int filter_index) {
NSSavePanel* savePanel = [NSSavePanel savePanel];
std::u16string title;
if (!params.title.empty())
title = params.title;
else
title = l10n_util::GetStringUTF16(IDS_SAVE_AS_DIALOG_TITLE);
[savePanel setTitle:base::SysUTF16ToNSString(title)];
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)]];
}
CefFilterDelegate* filter_delegate = nil;
if (!params.accept_types.empty()) {
// 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];
// Show panel.
[savePanel
beginSheetModalForWindow:view.window
completionHandler:^(NSInteger resultCode) {
int filter_index_to_use = (filter_delegate != nil)
? [filter_delegate filter]
: filter_index;
if (resultCode == NSFileHandlingPanelOKButton) {
NSURL* url = savePanel.URL;
const char* path = url.path.UTF8String;
std::vector<base::FilePath> files(1, base::FilePath(path));
std::move(weak_this->callback_)
.Run(filter_index_to_use, files);
} else {
std::move(weak_this->callback_)
.Run(filter_index_to_use, std::vector<base::FilePath>());
}
}];
}

View File

@@ -1,560 +0,0 @@
// 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/native/file_dialog_runner_win.h"
#include <windows.h>
#include <commdlg.h>
#include <shlobj.h>
#include <wrl/client.h>
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
#include "base/files/file_util.h"
#include "base/stl_util.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 "cef/grit/cef_strings.h"
#include "chrome/grit/generated_resources.h"
#include "net/base/mime_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/win/shell.h"
#include "ui/strings/grit/ui_strings.h"
namespace {
// From ui/base/dialogs/select_file_dialog_win.cc.
// Get the file type description from the registry. This will be "Text Document"
// for .txt files, "JPEG Image" for .jpg files, etc. If the registry doesn't
// have an entry for the file type, we return false, true if the description was
// found. 'file_ext' must be in form ".txt".
static bool GetRegistryDescriptionFromExtension(const std::wstring& file_ext,
std::wstring* reg_description) {
DCHECK(reg_description);
base::win::RegKey reg_ext(HKEY_CLASSES_ROOT, file_ext.c_str(), KEY_READ);
std::wstring reg_app;
if (reg_ext.ReadValue(NULL, &reg_app) == ERROR_SUCCESS && !reg_app.empty()) {
base::win::RegKey reg_link(HKEY_CLASSES_ROOT, reg_app.c_str(), KEY_READ);
if (reg_link.ReadValue(NULL, reg_description) == ERROR_SUCCESS)
return true;
}
return false;
}
// Set up a filter for a Save/Open dialog, which will consist of |file_ext| file
// extensions (internally separated by semicolons), |ext_desc| as the text
// descriptions of the |file_ext| types (optional), and (optionally) the default
// 'All Files' view. The purpose of the filter is to show only files of a
// particular type in a Windows Save/Open dialog box. The resulting filter is
// returned. The filters created here are:
// 1. only files that have 'file_ext' as their extension
// 2. all files (only added if 'include_all_files' is true)
// Example:
// file_ext: { "*.txt", "*.htm;*.html" }
// ext_desc: { "Text Document" }
// returned: "Text Document\0*.txt\0HTML Document\0*.htm;*.html\0"
// "All Files\0*.*\0\0" (in one big string)
// If a description is not provided for a file extension, it will be retrieved
// from the registry. If the file extension does not exist in the registry, it
// will be omitted from the filter, as it is likely a bogus extension.
std::wstring FormatFilterForExtensions(
const std::vector<std::wstring>& file_ext,
const std::vector<std::wstring>& ext_desc,
bool include_all_files) {
const std::wstring all_ext = L"*.*";
const std::wstring all_desc =
base::UTF16ToWide(l10n_util::GetStringUTF16(IDS_APP_SAVEAS_ALL_FILES)) +
L" (" + all_ext + L")";
DCHECK(file_ext.size() >= ext_desc.size());
if (file_ext.empty())
include_all_files = true;
std::wstring result;
// Create all supported .ext filter if more than one filter.
if (file_ext.size() > 1) {
std::set<base::WStringPiece> unique_exts;
for (const auto& exts : file_ext) {
for (const auto& ext : base::SplitStringPiece(
exts, L";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
unique_exts.insert(ext);
}
}
if (unique_exts.size() > 1) {
std::wstring ext;
auto it = unique_exts.cbegin();
ext = std::wstring(*it);
for (++it; it != unique_exts.cend(); ++it) {
ext += L";" + std::wstring(*it);
}
std::wstring desc =
base::UTF16ToWide(l10n_util::GetStringUTF16(IDS_CUSTOM_FILES)) +
L" (" + ext + L")";
result.append(desc.c_str(), desc.size() + 1); // Append NULL too.
result.append(ext.c_str(), ext.size() + 1);
}
}
for (size_t i = 0; i < file_ext.size(); ++i) {
std::wstring ext = file_ext[i];
std::wstring desc;
if (i < ext_desc.size())
desc = ext_desc[i];
if (ext.empty()) {
// Force something reasonable to appear in the dialog box if there is no
// extension provided.
include_all_files = true;
continue;
}
if (desc.empty()) {
DCHECK(ext.find(L'.') != std::wstring::npos);
std::wstring first_extension = ext.substr(ext.find(L'.'));
size_t first_separator_index = first_extension.find(L';');
if (first_separator_index != std::wstring::npos)
first_extension = first_extension.substr(0, first_separator_index);
// Find the extension name without the preceeding '.' character.
std::wstring ext_name = first_extension;
size_t ext_index = ext_name.find_first_not_of(L'.');
if (ext_index != std::wstring::npos)
ext_name = ext_name.substr(ext_index);
if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) {
// The extension doesn't exist in the registry.
include_all_files = true;
}
}
if (!desc.empty())
desc += L" (" + ext + L")";
else
desc = ext;
result.append(desc.c_str(), desc.size() + 1); // Append NULL too.
result.append(ext.c_str(), ext.size() + 1);
}
if (include_all_files) {
result.append(all_desc.c_str(), all_desc.size() + 1);
result.append(all_ext.c_str(), all_ext.size() + 1);
}
result.append(1, '\0'); // Double NULL required.
return result;
}
std::wstring 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_AUDIO_FILES},
{"image", IDS_IMAGE_FILES},
{"text", IDS_TEXT_FILES},
{"video", IDS_VIDEO_FILES},
};
for (size_t i = 0; i < std::size(kWildCardMimeTypes); ++i) {
if (mime_type == std::string(kWildCardMimeTypes[i].mime_type) + "/*") {
return base::UTF16ToWide(
l10n_util::GetStringUTF16(kWildCardMimeTypes[i].string_id));
}
}
return std::wstring();
}
std::wstring GetFilterString(
const std::vector<std::u16string>& accept_filters) {
std::vector<std::wstring> extensions;
std::vector<std::wstring> descriptions;
for (size_t i = 0; i < accept_filters.size(); ++i) {
const std::wstring& filter = base::UTF16ToWide(accept_filters[i]);
if (filter.empty())
continue;
size_t sep_index = filter.find(L'|');
if (sep_index != std::wstring::npos) {
// Treat as a filter of the form "Filter Name|.ext1;.ext2;.ext3".
const std::wstring& desc = filter.substr(0, sep_index);
const std::vector<std::u16string>& ext = base::SplitString(
base::WideToUTF16(filter.substr(sep_index + 1)), u";",
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
std::wstring ext_str;
for (size_t x = 0; x < ext.size(); ++x) {
const std::wstring& file_ext = base::UTF16ToWide(ext[x]);
if (!file_ext.empty() && file_ext[0] == L'.') {
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::WideToASCII(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));
}
}
}
return FormatFilterForExtensions(extensions, descriptions, true);
}
// From chrome/browser/views/shell_dialogs_win.cc
bool RunOpenFileDialog(const CefFileDialogRunner::FileChooserParams& params,
HWND owner,
int* filter_index,
base::FilePath* path) {
OPENFILENAME ofn;
// We must do this otherwise the ofn's FlagsEx may be initialized to random
// junk in release builds which can cause the Places Bar not to show up!
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = owner;
wchar_t filename[MAX_PATH] = {0};
ofn.lpstrFile = filename;
ofn.nMaxFile = MAX_PATH;
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.
base::wcslcpy(filename, params.default_file_name.value().c_str(),
std::size(filename));
directory = params.default_file_name.DirName().value();
}
}
if (!directory.empty())
ofn.lpstrInitialDir = directory.c_str();
std::wstring title;
if (!params.title.empty()) {
title = base::UTF16ToWide(params.title);
} else {
title = base::UTF16ToWide(
l10n_util::GetStringUTF16(IDS_OPEN_FILE_DIALOG_TITLE));
}
if (!title.empty())
ofn.lpstrTitle = title.c_str();
// We use OFN_NOCHANGEDIR so that the user can rename or delete the directory
// without having to close Chrome first.
ofn.Flags =
OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_ENABLESIZING;
if (params.hidereadonly)
ofn.Flags |= OFN_HIDEREADONLY;
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) {
*filter_index = ofn.nFilterIndex == 0 ? 0 : ofn.nFilterIndex - 1;
*path = base::FilePath(filename);
}
return success;
}
bool RunOpenMultiFileDialog(
const CefFileDialogRunner::FileChooserParams& params,
HWND owner,
int* filter_index,
std::vector<base::FilePath>* paths) {
OPENFILENAME ofn;
// We must do this otherwise the ofn's FlagsEx may be initialized to random
// junk in release builds which can cause the Places Bar not to show up!
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = owner;
std::unique_ptr<wchar_t[]> filename(new wchar_t[UNICODE_STRING_MAX_CHARS]);
filename[0] = 0;
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 = base::UTF16ToWide(params.title);
} else {
title = base::UTF16ToWide(
l10n_util::GetStringUTF16(IDS_OPEN_FILES_DIALOG_TITLE));
}
if (!title.empty())
ofn.lpstrTitle = title.c_str();
// 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_ALLOWMULTISELECT | OFN_ENABLESIZING;
if (params.hidereadonly)
ofn.Flags |= OFN_HIDEREADONLY;
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) {
std::vector<base::FilePath> files;
const wchar_t* selection = ofn.lpstrFile;
while (*selection) { // Empty string indicates end of list.
files.push_back(base::FilePath(selection));
// Skip over filename and null-terminator.
selection += files.back().value().length() + 1;
}
if (files.empty()) {
success = false;
} else if (files.size() == 1) {
// When there is one file, it contains the path and filename.
paths->swap(files);
} else {
// Otherwise, the first string is the path, and the remainder are
// filenames.
std::vector<base::FilePath>::iterator path = files.begin();
for (std::vector<base::FilePath>::iterator file = path + 1;
file != files.end(); ++file) {
paths->push_back(path->Append(*file));
}
}
}
if (success)
*filter_index = ofn.nFilterIndex == 0 ? 0 : ofn.nFilterIndex - 1;
return success;
}
// 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 CefFileDialogRunner::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 = base::UTF16ToWide(params.title);
} else {
title = base::UTF16ToWide(
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;
Microsoft::WRL::ComPtr<IShellFolder> shell_folder;
if (SHGetDesktopFolder(shell_folder.GetAddressOf()) == 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 CefFileDialogRunner::FileChooserParams& params,
HWND owner,
int* filter_index,
base::FilePath* path) {
OPENFILENAME ofn;
// We must do this otherwise the ofn's FlagsEx may be initialized to random
// junk in release builds which can cause the Places Bar not to show up!
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = owner;
wchar_t filename[MAX_PATH] = {0};
ofn.lpstrFile = filename;
ofn.nMaxFile = MAX_PATH;
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.
base::wcslcpy(filename, params.default_file_name.value().c_str(),
std::size(filename));
directory = params.default_file_name.DirName().value();
}
}
if (!directory.empty())
ofn.lpstrInitialDir = directory.c_str();
std::wstring title;
if (!params.title.empty()) {
title = base::UTF16ToWide(params.title);
} else {
title =
base::UTF16ToWide(l10n_util::GetStringUTF16(IDS_SAVE_AS_DIALOG_TITLE));
}
if (!title.empty())
ofn.lpstrTitle = title.c_str();
// We use OFN_NOCHANGEDIR so that the user can rename or delete the directory
// without having to close Chrome first.
ofn.Flags =
OFN_EXPLORER | OFN_ENABLESIZING | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST;
if (params.hidereadonly)
ofn.Flags |= OFN_HIDEREADONLY;
if (params.overwriteprompt)
ofn.Flags |= OFN_OVERWRITEPROMPT;
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;
// If a filter is specified and the default file name is changed then append
// a file extension to the new name.
ofn.lpstrDefExt = L"";
}
bool success = !!GetSaveFileName(&ofn);
if (success) {
*filter_index = ofn.nFilterIndex == 0 ? 0 : ofn.nFilterIndex - 1;
*path = base::FilePath(filename);
}
return success;
}
} // namespace
CefFileDialogRunnerWin::CefFileDialogRunnerWin() {}
void CefFileDialogRunnerWin::Run(AlloyBrowserHostImpl* browser,
const FileChooserParams& params,
RunFileChooserCallback callback) {
int filter_index = params.selected_accept_filter;
std::vector<base::FilePath> files;
HWND owner = browser->GetWindowHandle();
if (params.mode == blink::mojom::FileChooserParams::Mode::kOpen) {
base::FilePath file;
if (RunOpenFileDialog(params, owner, &filter_index, &file))
files.push_back(file);
} else if (params.mode ==
blink::mojom::FileChooserParams::Mode::kOpenMultiple) {
RunOpenMultiFileDialog(params, owner, &filter_index, &files);
} else if (params.mode ==
blink::mojom::FileChooserParams::Mode::kUploadFolder) {
base::FilePath file;
if (RunOpenFolderDialog(params, owner, &file))
files.push_back(file);
} else if (params.mode == blink::mojom::FileChooserParams::Mode::kSave) {
base::FilePath file;
if (RunSaveFileDialog(params, owner, &filter_index, &file))
files.push_back(file);
} else {
NOTIMPLEMENTED();
}
std::move(callback).Run(filter_index, files);
}

View File

@@ -1,22 +0,0 @@
// 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.
#ifndef CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_WIN_H_
#define CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_WIN_H_
#pragma once
#include "libcef/browser/file_dialog_runner.h"
class CefFileDialogRunnerWin : public CefFileDialogRunner {
public:
CefFileDialogRunnerWin();
// CefFileDialogRunner methods:
void Run(AlloyBrowserHostImpl* browser,
const FileChooserParams& params,
RunFileChooserCallback callback) override;
};
#endif // CEF_LIBCEF_BROWSER_NATIVE_FILE_DIALOG_RUNNER_WIN_H_

View File

@@ -194,11 +194,6 @@ CefEventHandle CefBrowserPlatformDelegateOsr::GetEventHandle(
return native_delegate_->GetEventHandle(event);
}
std::unique_ptr<CefFileDialogRunner>
CefBrowserPlatformDelegateOsr::CreateFileDialogRunner() {
return native_delegate_->CreateFileDialogRunner();
}
std::unique_ptr<CefJavaScriptDialogRunner>
CefBrowserPlatformDelegateOsr::CreateJavaScriptDialogRunner() {
return native_delegate_->CreateJavaScriptDialogRunner();

View File

@@ -49,7 +49,6 @@ class CefBrowserPlatformDelegateOsr
const content::NativeWebKeyboardEvent& event) override;
CefEventHandle GetEventHandle(
const content::NativeWebKeyboardEvent& event) const override;
std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner() override;
std::unique_ptr<CefJavaScriptDialogRunner> CreateJavaScriptDialogRunner()
override;
std::unique_ptr<CefMenuRunner> CreateMenuRunner() override;

View File

@@ -30,6 +30,33 @@ using content::BrowserThread;
using printing::PageRanges;
using printing::PrintSettings;
namespace {
printing::PrintingContextLinux::CreatePrintDialogFunctionPtr
g_default_create_print_dialog_func = nullptr;
printing::PrintingContextLinux::PdfPaperSizeFunctionPtr
g_default_pdf_paper_size_func = nullptr;
CefRefPtr<CefBrowserHostBase> GetBrowserForContext(
printing::PrintingContextLinux* context) {
return extensions::GetOwnerBrowserForGlobalId(
frame_util::MakeGlobalId(context->render_process_id(),
context->render_frame_id()),
nullptr);
}
CefRefPtr<CefPrintHandler> GetPrintHandlerForBrowser(
CefRefPtr<CefBrowserHostBase> browser) {
if (browser) {
if (auto client = browser->GetClient()) {
return client->GetPrintHandler();
}
}
return nullptr;
}
} // namespace
class CefPrintDialogCallbackImpl : public CefPrintDialogCallback {
public:
explicit CefPrintDialogCallbackImpl(CefRefPtr<CefPrintDialogLinux> dialog)
@@ -104,7 +131,30 @@ class CefPrintJobCallbackImpl : public CefPrintJobCallback {
printing::PrintDialogGtkInterface* CefPrintDialogLinux::CreatePrintDialog(
PrintingContextLinux* context) {
CEF_REQUIRE_UIT();
return new CefPrintDialogLinux(context);
printing::PrintDialogGtkInterface* interface = nullptr;
auto browser = GetBrowserForContext(context);
if (!browser) {
LOG(ERROR) << "No associated browser in CreatePrintDialog; using default "
"printing implementation.";
}
auto handler = GetPrintHandlerForBrowser(browser);
if (!handler) {
if (g_default_create_print_dialog_func) {
interface = g_default_create_print_dialog_func(context);
DCHECK(interface);
}
} else {
interface = new CefPrintDialogLinux(context, browser, handler);
}
if (!interface) {
LOG(ERROR) << "Null interface in CreatePrintDialog; printing will fail.";
}
return interface;
}
// static
@@ -114,27 +164,42 @@ gfx::Size CefPrintDialogLinux::GetPdfPaperSize(
gfx::Size size;
auto browser = extensions::GetOwnerBrowserForGlobalId(
frame_util::MakeGlobalId(context->render_process_id(),
context->render_frame_id()),
nullptr);
DCHECK(browser);
if (browser && browser->GetClient()) {
if (auto handler = browser->GetClient()->GetPrintHandler()) {
const printing::PrintSettings& settings = context->settings();
CefSize cef_size = handler->GetPdfPaperSize(
browser.get(), settings.device_units_per_inch());
size.SetSize(cef_size.width, cef_size.height);
auto browser = GetBrowserForContext(context);
if (!browser) {
LOG(ERROR) << "No associated browser in GetPdfPaperSize; using default "
"printing implementation.";
}
auto handler = GetPrintHandlerForBrowser(browser);
if (!handler) {
if (g_default_pdf_paper_size_func) {
size = g_default_pdf_paper_size_func(context);
DCHECK(!size.IsEmpty());
}
} else {
const printing::PrintSettings& settings = context->settings();
CefSize cef_size = handler->GetPdfPaperSize(
browser.get(), settings.device_units_per_inch());
size.SetSize(cef_size.width, cef_size.height);
}
if (size.IsEmpty()) {
LOG(ERROR) << "Empty size value returned in GetPdfPaperSize; "
"PDF printing will fail.";
LOG(ERROR) << "Empty size value returned in GetPdfPaperSize; PDF printing "
"will fail.";
}
return size;
}
// static
void CefPrintDialogLinux::SetDefaultPrintingContextFuncs(
printing::PrintingContextLinux::CreatePrintDialogFunctionPtr
create_print_dialog_func,
printing::PrintingContextLinux::PdfPaperSizeFunctionPtr
pdf_paper_size_func) {
g_default_create_print_dialog_func = create_print_dialog_func;
g_default_pdf_paper_size_func = pdf_paper_size_func;
}
// static
void CefPrintDialogLinux::OnPrintStart(CefRefPtr<CefBrowserHostBase> browser) {
CEF_REQUIRE_UIT();
@@ -146,14 +211,13 @@ void CefPrintDialogLinux::OnPrintStart(CefRefPtr<CefBrowserHostBase> browser) {
}
}
CefPrintDialogLinux::CefPrintDialogLinux(PrintingContextLinux* context)
: context_(context) {
CefPrintDialogLinux::CefPrintDialogLinux(PrintingContextLinux* context,
CefRefPtr<CefBrowserHostBase> browser,
CefRefPtr<CefPrintHandler> handler)
: context_(context), browser_(browser), handler_(handler) {
DCHECK(context_);
browser_ = extensions::GetOwnerBrowserForGlobalId(
frame_util::MakeGlobalId(context_->render_process_id(),
context_->render_frame_id()),
nullptr);
DCHECK(browser_);
DCHECK(handler_);
}
CefPrintDialogLinux::~CefPrintDialogLinux() {
@@ -161,7 +225,7 @@ CefPrintDialogLinux::~CefPrintDialogLinux() {
// object because the PrintJobWorker which owns |context_| may already have
// been deleted.
CEF_REQUIRE_UIT();
ReleaseHandler();
handler_->OnPrintReset(browser_.get());
}
void CefPrintDialogLinux::UseDefaultSettings() {
@@ -179,12 +243,6 @@ void CefPrintDialogLinux::ShowDialog(
PrintingContextLinux::PrintSettingsCallback callback) {
CEF_REQUIRE_UIT();
SetHandler();
if (!handler_.get()) {
std::move(callback).Run(printing::mojom::ResultCode::kCanceled);
return;
}
callback_ = std::move(callback);
CefRefPtr<CefPrintDialogCallbackImpl> callback_impl(
@@ -240,31 +298,11 @@ void CefPrintDialogLinux::ReleaseDialog() {
Release();
}
void CefPrintDialogLinux::SetHandler() {
if (handler_.get())
return;
if (browser_ && browser_->GetClient()) {
handler_ = browser_->GetClient()->GetPrintHandler();
}
}
void CefPrintDialogLinux::ReleaseHandler() {
if (handler_.get()) {
handler_->OnPrintReset(browser_.get());
handler_ = nullptr;
}
}
bool CefPrintDialogLinux::UpdateSettings(
std::unique_ptr<PrintSettings> settings,
bool get_defaults) {
CEF_REQUIRE_UIT();
SetHandler();
if (!handler_.get())
return false;
CefRefPtr<CefPrintSettingsImpl> settings_impl(
new CefPrintSettingsImpl(std::move(settings), false));
handler_->OnPrintSettings(browser_.get(), settings_impl.get(), get_defaults);

View File

@@ -39,6 +39,13 @@ class CefPrintDialogLinux : public printing::PrintDialogGtkInterface,
// Returns the paper size in device units.
static gfx::Size GetPdfPaperSize(printing::PrintingContextLinux* context);
// Used for calling into the default GTK implementation.
static void SetDefaultPrintingContextFuncs(
printing::PrintingContextLinux::CreatePrintDialogFunctionPtr
create_print_dialog_func,
printing::PrintingContextLinux::PdfPaperSizeFunctionPtr
pdf_paper_size_func);
// Notify the client when printing has started.
static void OnPrintStart(CefRefPtr<CefBrowserHostBase> browser);
@@ -65,12 +72,11 @@ class CefPrintDialogLinux : public printing::PrintDialogGtkInterface,
friend class CefPrintDialogCallbackImpl;
friend class CefPrintJobCallbackImpl;
explicit CefPrintDialogLinux(PrintingContextLinux* context);
CefPrintDialogLinux(PrintingContextLinux* context,
CefRefPtr<CefBrowserHostBase> browser,
CefRefPtr<CefPrintHandler> handler);
~CefPrintDialogLinux() override;
void SetHandler();
void ReleaseHandler();
bool UpdateSettings(std::unique_ptr<printing::PrintSettings> settings,
bool get_defaults);
@@ -84,12 +90,12 @@ class CefPrintDialogLinux : public printing::PrintDialogGtkInterface,
// Handles print job response.
void OnJobCompleted();
CefRefPtr<CefPrintHandler> handler_;
// Printing dialog callback.
PrintingContextLinux::PrintSettingsCallback callback_;
PrintingContextLinux* context_;
CefRefPtr<CefBrowserHostBase> browser_;
CefRefPtr<CefPrintHandler> handler_;
base::FilePath path_to_pdf_;
};

View File

@@ -257,11 +257,6 @@ CefEventHandle CefBrowserPlatformDelegateViews::GetEventHandle(
return native_delegate_->GetEventHandle(event);
}
std::unique_ptr<CefFileDialogRunner>
CefBrowserPlatformDelegateViews::CreateFileDialogRunner() {
return native_delegate_->CreateFileDialogRunner();
}
std::unique_ptr<CefJavaScriptDialogRunner>
CefBrowserPlatformDelegateViews::CreateJavaScriptDialogRunner() {
return native_delegate_->CreateJavaScriptDialogRunner();

View File

@@ -60,7 +60,6 @@ class CefBrowserPlatformDelegateViews
const content::NativeWebKeyboardEvent& event) override;
CefEventHandle GetEventHandle(
const content::NativeWebKeyboardEvent& event) const override;
std::unique_ptr<CefFileDialogRunner> CreateFileDialogRunner() override;
std::unique_ptr<CefJavaScriptDialogRunner> CreateJavaScriptDialogRunner()
override;
std::unique_ptr<CefMenuRunner> CreateMenuRunner() override;