166 lines
5.8 KiB
C++
166 lines
5.8 KiB
C++
// 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/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); }
|
|
};
|
|
|
|
// 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,
|
|
const GURL* caller) 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_ =
|
|
CefBrowserHostBase::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_ = CefBrowserHostBase::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
|