From c1b07a71bf594054a014f533153a8d112009c63e Mon Sep 17 00:00:00 2001 From: Szabolcs David Date: Fri, 11 Jun 2021 16:25:20 +0200 Subject: Implement dialogs of File System Access API Dialogs triggered by window.showOpenFilePicker() or window.showDirectoryPicker() needed different implementation than regular file picker dialogs. Since the end-users can't distinguish between HTML file picker dialogs and these file system access dialogs, we can just use the existing file picker WebEngine API and UI delegates. Task-number: QTBUG-92519 Change-Id: Ib1624f80603c4042803303274ba45d864ecb371c Reviewed-by: Allan Sandfeld Jensen (cherry picked from commit 46a2f0f3137dcb46f2752130bbee6438742c27ac) Reviewed-by: Qt Cherry-pick Bot --- src/core/CMakeLists.txt | 1 + src/core/browser_main_parts_qt.cpp | 3 + src/core/content_browser_client_qt.cpp | 7 ++ src/core/content_browser_client_qt.h | 2 + src/core/file_picker_controller.cpp | 66 ++++++---- src/core/select_file_dialog_factory_qt.cpp | 185 +++++++++++++++++++++++++++++ src/core/select_file_dialog_factory_qt.h | 87 ++++++++++++++ 7 files changed, 331 insertions(+), 20 deletions(-) create mode 100644 src/core/select_file_dialog_factory_qt.cpp create mode 100644 src/core/select_file_dialog_factory_qt.h (limited to 'src/core') 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 +ContentBrowserClientQt::CreateSelectFilePolicy(content::WebContents *web_contents) +{ + return std::make_unique(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* additional_schemes) override; void GetAdditionalAllowedSchemesForFileSystem(std::vector* additional_schemes) override; + std::unique_ptr + 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 #include @@ -57,7 +58,8 @@ namespace QtWebEngineCore { class FilePickerControllerPrivate { public: FilePickerController::FileChooserMode mode; - scoped_refptr listener; + scoped_refptr fileDialogListener; + ui::SelectFileDialog::Listener *fileSystemAccessDialogListener; QString defaultFileName; QStringList acceptedMimeTypes; }; @@ -66,7 +68,19 @@ FilePickerController *createFilePickerController( FilePickerController::FileChooserMode mode, scoped_refptr 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 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 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(d_ptr->mode)); + if (files.isEmpty()) + d_ptr->fileDialogListener->FileSelectionCanceled(); + else + d_ptr->fileDialogListener->FileSelected( + std::move(chooser_files), baseDir, + static_cast(d_ptr->mode)); + } else if (d_ptr->fileSystemAccessDialogListener) { + std::vector 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 +#include + +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, + 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 m_filePickerController; + + DISALLOW_COPY_AND_ASSIGN(SelectFileDialogQt); +}; + +SelectFileDialogQt::SelectFileDialogQt(Listener *listener, + std::unique_ptr 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 policy) +{ + content::WebContents *webContents = + static_cast(policy.get())->webContents(); + WebContentsAdapterClient *client = + WebContentsViewQt::from(static_cast(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 policy) override; + +private: + DISALLOW_COPY_AND_ASSIGN(SelectFileDialogFactoryQt); +}; + +} // namespace QtWebEngineCore + +#endif // SELECT_FILE_DIALOG_FACTORY_QT_H_ -- cgit v1.2.3