diff options
-rw-r--r-- | src/core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/core/browser_main_parts_qt.cpp | 3 | ||||
-rw-r--r-- | src/core/content_browser_client_qt.cpp | 7 | ||||
-rw-r--r-- | src/core/content_browser_client_qt.h | 2 | ||||
-rw-r--r-- | src/core/file_picker_controller.cpp | 66 | ||||
-rw-r--r-- | src/core/select_file_dialog_factory_qt.cpp | 185 | ||||
-rw-r--r-- | src/core/select_file_dialog_factory_qt.h | 87 | ||||
-rw-r--r-- | tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp | 29 |
8 files changed, 360 insertions, 20 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6c73e694f..e9d34adfb 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -155,6 +155,7 @@ foreach(config ${configs}) request_controller.h resource_bundle_qt.cpp resource_context_qt.cpp resource_context_qt.h + select_file_dialog_factory_qt.cpp select_file_dialog_factory_qt.h touch_handle_drawable_client.h touch_handle_drawable_qt.cpp touch_handle_drawable_qt.h touch_selection_controller_client_qt.cpp touch_selection_controller_client_qt.h diff --git a/src/core/browser_main_parts_qt.cpp b/src/core/browser_main_parts_qt.cpp index bc0499bca..864e96c89 100644 --- a/src/core/browser_main_parts_qt.cpp +++ b/src/core/browser_main_parts_qt.cpp @@ -67,6 +67,7 @@ #include "extensions/plugin_service_filter_qt.h" #include "common/extensions/extensions_client_qt.h" #endif //BUILDFLAG(ENABLE_EXTENSIONS) +#include "select_file_dialog_factory_qt.h" #include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/service.h" #include "ui/display/screen.h" @@ -248,6 +249,8 @@ void BrowserMainPartsQt::PreMainMessageLoopStart() void BrowserMainPartsQt::PreMainMessageLoopRun() { + ui::SelectFileDialog::SetFactory(new SelectFileDialogFactoryQt()); + #if BUILDFLAG(ENABLE_EXTENSIONS) extensions::ExtensionsClient::Set(new extensions::ExtensionsClientQt()); extensions::ExtensionsBrowserClient::Set(new extensions::ExtensionsBrowserClientQt()); diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index fdcd98024..f17bc5025 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -106,6 +106,7 @@ #include "profile_io_data_qt.h" #include "quota_permission_context_qt.h" #include "renderer_host/user_resource_controller_host.h" +#include "select_file_dialog_factory_qt.h" #include "type_conversion.h" #include "web_contents_adapter_client.h" #include "web_contents_adapter.h" @@ -391,6 +392,12 @@ void ContentBrowserClientQt::GetAdditionalAllowedSchemesForFileSystem(std::vecto additional_schemes->push_back(content::kChromeUIScheme); } +std::unique_ptr<ui::SelectFilePolicy> +ContentBrowserClientQt::CreateSelectFilePolicy(content::WebContents *web_contents) +{ + return std::make_unique<SelectFilePolicyQt>(web_contents); +} + #if defined(Q_OS_LINUX) void ContentBrowserClientQt::GetAdditionalMappedFilesForChildProcess(const base::CommandLine& command_line, int child_process_id, content::PosixFileDescriptorInfo* mappings) { diff --git a/src/core/content_browser_client_qt.h b/src/core/content_browser_client_qt.h index bac155c8c..1abb4a69f 100644 --- a/src/core/content_browser_client_qt.h +++ b/src/core/content_browser_client_qt.h @@ -103,6 +103,8 @@ public: void GetAdditionalWebUISchemes(std::vector<std::string>* additional_schemes) override; void GetAdditionalAllowedSchemesForFileSystem(std::vector<std::string>* additional_schemes) override; + std::unique_ptr<ui::SelectFilePolicy> + CreateSelectFilePolicy(content::WebContents *web_contents) override; void BindHostReceiverForRenderer(content::RenderProcessHost *render_process_host, mojo::GenericPendingReceiver receiver) override; void RegisterBrowserInterfaceBindersForFrame(content::RenderFrameHost *render_frame_host, diff --git a/src/core/file_picker_controller.cpp b/src/core/file_picker_controller.cpp index 1494d8aae..537a01508 100644 --- a/src/core/file_picker_controller.cpp +++ b/src/core/file_picker_controller.cpp @@ -44,6 +44,7 @@ #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/file_select_listener.h" +#include "ui/shell_dialogs/select_file_dialog.h" #include <QtCore/qcoreapplication.h> #include <QDir> @@ -57,7 +58,8 @@ namespace QtWebEngineCore { class FilePickerControllerPrivate { public: FilePickerController::FileChooserMode mode; - scoped_refptr<content::FileSelectListener> listener; + scoped_refptr<content::FileSelectListener> fileDialogListener; + ui::SelectFileDialog::Listener *fileSystemAccessDialogListener; QString defaultFileName; QStringList acceptedMimeTypes; }; @@ -66,7 +68,19 @@ FilePickerController *createFilePickerController( FilePickerController::FileChooserMode mode, scoped_refptr<content::FileSelectListener> listener, const QString &defaultFileName, const QStringList &acceptedMimeTypes, QObject *parent = nullptr) { - auto priv = new FilePickerControllerPrivate{mode, listener, defaultFileName, acceptedMimeTypes}; + auto priv = new FilePickerControllerPrivate { mode, listener, nullptr, defaultFileName, + acceptedMimeTypes }; + return new FilePickerController(priv, parent); +} + +FilePickerController *createFilePickerController(FilePickerController::FileChooserMode mode, + ui::SelectFileDialog::Listener *listener, + const QString &defaultFileName, + const QStringList &acceptedMimeTypes, + QObject *parent = nullptr) +{ + auto priv = new FilePickerControllerPrivate { mode, nullptr, listener, defaultFileName, + acceptedMimeTypes }; return new FilePickerController(priv, parent); } @@ -194,27 +208,39 @@ ASSERT_ENUMS_MATCH(FilePickerController::Save, blink::mojom::FileChooserParams_M void FilePickerController::filesSelectedInChooser(const QStringList &filesList) { - QStringList files(filesList); - base::FilePath baseDir; - if (d_ptr->mode == UploadFolder && !filesList.isEmpty() + if (d_ptr->fileDialogListener) { + QStringList files(filesList); + base::FilePath baseDir; + if (d_ptr->mode == UploadFolder && !filesList.isEmpty() && QFileInfo(filesList.first()).isDir()) { - // Enumerate the directory - files = listRecursively(QDir(filesList.first())); - baseDir = toFilePath(filesList.first()); - } + // Enumerate the directory + files = listRecursively(QDir(filesList.first())); + baseDir = toFilePath(filesList.first()); + } - std::vector<blink::mojom::FileChooserFileInfoPtr> chooser_files; - for (const auto &file : qAsConst(files)) { - chooser_files.push_back(blink::mojom::FileChooserFileInfo::NewNativeFile( - blink::mojom::NativeFileInfo::New(toFilePath(file), base::string16()))); - } + std::vector<blink::mojom::FileChooserFileInfoPtr> chooser_files; + for (const auto &file : qAsConst(files)) { + chooser_files.push_back(blink::mojom::FileChooserFileInfo::NewNativeFile( + blink::mojom::NativeFileInfo::New(toFilePath(file), base::string16()))); + } - if (files.isEmpty()) - d_ptr->listener->FileSelectionCanceled(); - else - d_ptr->listener->FileSelected(std::move(chooser_files), - baseDir, - static_cast<blink::mojom::FileChooserParams::Mode>(d_ptr->mode)); + if (files.isEmpty()) + d_ptr->fileDialogListener->FileSelectionCanceled(); + else + d_ptr->fileDialogListener->FileSelected( + std::move(chooser_files), baseDir, + static_cast<blink::mojom::FileChooserParams::Mode>(d_ptr->mode)); + } else if (d_ptr->fileSystemAccessDialogListener) { + std::vector<base::FilePath> files; + for (const auto &file : qAsConst(filesList)) { + files.push_back(toFilePath(file)); + } + + if (files.empty()) + d_ptr->fileSystemAccessDialogListener->FileSelectionCanceled(nullptr); + else + d_ptr->fileSystemAccessDialogListener->MultiFilesSelected(files, nullptr); + } } QStringList FilePickerController::acceptedMimeTypes() const diff --git a/src/core/select_file_dialog_factory_qt.cpp b/src/core/select_file_dialog_factory_qt.cpp new file mode 100644 index 000000000..baad07b24 --- /dev/null +++ b/src/core/select_file_dialog_factory_qt.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "select_file_dialog_factory_qt.h" + +#include "content/browser/web_contents/web_contents_impl.h" +#include "file_picker_controller.h" +#include "type_conversion.h" +#include "ui/shell_dialogs/select_file_dialog.h" +#include "web_contents_adapter_client.h" +#include "web_contents_view_qt.h" + +#include <QSharedPointer> +#include <QTimer> + +namespace QtWebEngineCore { + +SelectFilePolicyQt::SelectFilePolicyQt(content::WebContents *source_contents) + : m_webContents(source_contents) +{ +} + +SelectFilePolicyQt::~SelectFilePolicyQt() { } + +bool SelectFilePolicyQt::CanOpenSelectFileDialog() +{ + return true; +} + +void SelectFilePolicyQt::SelectFileDenied() { } + +content::WebContents *SelectFilePolicyQt::webContents() +{ + return m_webContents; +} + +static FilePickerController::FileChooserMode toFileChooserMode(ui::SelectFileDialog::Type type) +{ + switch (type) { + case ui::SelectFileDialog::Type::SELECT_FOLDER: + case ui::SelectFileDialog::Type::SELECT_UPLOAD_FOLDER: + case ui::SelectFileDialog::Type::SELECT_EXISTING_FOLDER: + return FilePickerController::FileChooserMode::UploadFolder; + case ui::SelectFileDialog::Type::SELECT_SAVEAS_FILE: + return FilePickerController::FileChooserMode::Save; + case ui::SelectFileDialog::Type::SELECT_OPEN_FILE: + return FilePickerController::FileChooserMode::Open; + case ui::SelectFileDialog::Type::SELECT_OPEN_MULTI_FILE: + case ui::SelectFileDialog::Type::SELECT_NONE: + default: + return FilePickerController::FileChooserMode::OpenMultiple; + break; + } +} + +class SelectFileDialogQt : public ui::SelectFileDialog +{ +public: + SelectFileDialogQt(Listener *, std::unique_ptr<ui::SelectFilePolicy>, + WebContentsAdapterClient *); + + // ui::SelectFileDialog implementation: + bool IsRunning(gfx::NativeWindow) const override; + void ListenerDestroyed() override; + void SelectFileImpl(Type type, const base::string16 &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; + bool HasMultipleFileTypeChoicesImpl() override; + +private: + WebContentsAdapterClient *m_client; + QSharedPointer<FilePickerController> m_filePickerController; + + DISALLOW_COPY_AND_ASSIGN(SelectFileDialogQt); +}; + +SelectFileDialogQt::SelectFileDialogQt(Listener *listener, + std::unique_ptr<ui::SelectFilePolicy> policy, + WebContentsAdapterClient *client) + : SelectFileDialog(listener, std::move(policy)), m_client(client) +{ +} + +bool SelectFileDialogQt::IsRunning(gfx::NativeWindow) const +{ + return listener_; +} + +void SelectFileDialogQt::ListenerDestroyed() +{ + listener_ = nullptr; +} + +bool SelectFileDialogQt::HasMultipleFileTypeChoicesImpl() +{ + return false; +} + +extern FilePickerController *createFilePickerController(FilePickerController::FileChooserMode mode, + ui::SelectFileDialog::Listener *listener, + const QString &defaultFileName, + const QStringList &acceptedMimeTypes, + QObject *parent = nullptr); + +void SelectFileDialogQt::SelectFileImpl(Type type, const base::string16 &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) +{ + Q_UNUSED(title); + Q_UNUSED(file_type_index); + Q_UNUSED(default_extension); + Q_UNUSED(owning_window); + Q_UNUSED(params); + + QStringList acceptedSuffixes; + if (file_types) { + for (const auto &type : file_types->extensions) { + for (const auto &extension : type) + acceptedSuffixes.append("." + toQt(extension)); + } + } + + m_filePickerController.reset(createFilePickerController( + toFileChooserMode(type), listener_, toQt(default_path.value()), acceptedSuffixes)); + QTimer::singleShot(0, [this]() { m_client->runFileChooser(m_filePickerController); }); +} + +SelectFileDialogFactoryQt::SelectFileDialogFactoryQt() = default; + +SelectFileDialogFactoryQt::~SelectFileDialogFactoryQt() = default; + +ui::SelectFileDialog * +SelectFileDialogFactoryQt::Create(ui::SelectFileDialog::Listener *listener, + std::unique_ptr<ui::SelectFilePolicy> policy) +{ + content::WebContents *webContents = + static_cast<SelectFilePolicyQt *>(policy.get())->webContents(); + WebContentsAdapterClient *client = + WebContentsViewQt::from(static_cast<content::WebContentsImpl *>(webContents)->GetView()) + ->client(); + if (!client) + return nullptr; + return new SelectFileDialogQt(listener, std::move(policy), client); +} + +} // namespace QtWebEngineCore diff --git a/src/core/select_file_dialog_factory_qt.h b/src/core/select_file_dialog_factory_qt.h new file mode 100644 index 000000000..572bf217f --- /dev/null +++ b/src/core/select_file_dialog_factory_qt.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SELECT_FILE_DIALOG_FACTORY_QT_H_ +#define SELECT_FILE_DIALOG_FACTORY_QT_H_ + +#include "ui/shell_dialogs/select_file_dialog_factory.h" +#include "ui/shell_dialogs/select_file_policy.h" + +namespace content { +class WebContents; +} + +namespace QtWebEngineCore { + +class SelectFilePolicyQt : public ui::SelectFilePolicy +{ +public: + explicit SelectFilePolicyQt(content::WebContents *source_contents); + ~SelectFilePolicyQt() override; + + // Overridden from ui::SelectFilePolicy: + bool CanOpenSelectFileDialog() override; + void SelectFileDenied() override; + + content::WebContents *webContents(); + +private: + content::WebContents *m_webContents; + + DISALLOW_COPY_AND_ASSIGN(SelectFilePolicyQt); +}; + +// Implements a file Open / Save dialog for File System Access API. +class SelectFileDialogFactoryQt : public ui::SelectFileDialogFactory +{ +public: + SelectFileDialogFactoryQt(); + ~SelectFileDialogFactoryQt() override; + + // Overridden from ui::SelectFileDialogFactory: + ui::SelectFileDialog *Create(ui::SelectFileDialog::Listener *listener, + std::unique_ptr<ui::SelectFilePolicy> policy) override; + +private: + DISALLOW_COPY_AND_ASSIGN(SelectFileDialogFactoryQt); +}; + +} // namespace QtWebEngineCore + +#endif // SELECT_FILE_DIALOG_FACTORY_QT_H_ diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 797aef335..2cb0843db 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -240,6 +240,7 @@ private Q_SLOTS: void testChooseFilesParameters_data(); void testChooseFilesParameters(); + void fileSystemAccessDialog(); private: static QPoint elementCenter(QWebEnginePage *page, const QString &id); @@ -4728,6 +4729,34 @@ void tst_QWebEnginePage::testChooseFilesParameters() QTRY_COMPARE(page.chosenAcceptedMimeTypes, expectedMimeType); } +void tst_QWebEnginePage::fileSystemAccessDialog() +{ + FileSelectionTestPage page; + QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); + + QWebEngineView view; + view.resize(500, 500); + view.setPage(&page); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + page.setHtml(QString("<html><body>" + "<button id='triggerDialog' value='trigger' " + "onclick='window.showDirectoryPicker()'>" + "</body></html>"), + QString("qrc:/")); + QVERIFY(spyFinished.wait()); + QTRY_COMPARE(spyFinished.count(), 1); + + evaluateJavaScriptSync(view.page(), "document.getElementById('triggerDialog').focus()"); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), + QStringLiteral("triggerDialog")); + QTest::keyClick(view.focusProxy(), Qt::Key_Enter); + + QTRY_COMPARE(page.chosenFileSelectionMode, QWebEnginePage::FileSelectUploadFolder); + QTRY_COMPARE(page.chosenAcceptedMimeTypes, QStringList()); +} + void tst_QWebEnginePage::backgroundColor() { QWebEngineProfile profile; |