diff options
9 files changed, 111 insertions, 7 deletions
diff --git a/src/core/file_picker_controller.cpp b/src/core/file_picker_controller.cpp index 63b93c502..13e9437d2 100644 --- a/src/core/file_picker_controller.cpp +++ b/src/core/file_picker_controller.cpp @@ -46,10 +46,11 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/file_select_listener.h" -#include <QFileInfo> #include <QDir> -#include <QVariant> +#include <QFileInfo> +#include <QMimeDatabase> #include <QStringList> +#include <QVariant> namespace QtWebEngineCore { @@ -165,4 +166,51 @@ QString FilePickerController::defaultFileName() const return m_defaultFileName; } +QStringList FilePickerController::nameFilters(const QStringList &acceptedMimeTypes) +{ + QStringList nameFilters; + QStringList acceptedGlobs; + QMimeDatabase mimeDatabase; + + for (QString type : acceptedMimeTypes) { + if (type.startsWith(".")) { + // A single suffix + // Filename.type doesn't have to exist and mimeTypeForFile() supports + // custom suffixes as valid (but unknown) MIME types. + const QMimeType &mimeType = mimeDatabase.mimeTypeForFile("filename" + type); + if (mimeType.isValid()) { + QString glob = "*" + type; + acceptedGlobs.append(glob); + nameFilters.append(mimeType.comment() + " (" + glob + ")"); + } + } else if (type.contains("/") && !type.endsWith("*")) { + // All suffixes for a given MIME type + const QMimeType &mimeType = mimeDatabase.mimeTypeForName(type); + if (mimeType.isValid() && !mimeType.globPatterns().isEmpty()) { + QString globs = mimeType.globPatterns().join(" "); + acceptedGlobs.append(globs); + nameFilters.append(mimeType.comment() + " (" + globs + ")"); + } + } else if (type.endsWith("/*")) { + // All MIME types for audio/*, image/* or video/* + // as separate filters as Chrome does + static const QList<QMimeType> &allMimeTypes = mimeDatabase.allMimeTypes(); + type = type.remove("/*"); + for (const QMimeType &m : allMimeTypes) { + if (m.name().startsWith(type) && !m.globPatterns().isEmpty()) { + QString globs = m.globPatterns().join(" "); + acceptedGlobs.append(globs); + nameFilters.append(m.comment() + " (" + globs + ")"); + } + } + } else { + NOTREACHED(); + } + } + + nameFilters.prepend(QObject::tr("Accepted types") + " (" + acceptedGlobs.join(" ") + ")"); + + return nameFilters; +} + } // namespace diff --git a/src/core/file_picker_controller.h b/src/core/file_picker_controller.h index dc8c0eddf..0b680161a 100644 --- a/src/core/file_picker_controller.h +++ b/src/core/file_picker_controller.h @@ -80,6 +80,8 @@ public: QString defaultFileName() const; FileChooserMode mode() const; + static QStringList nameFilters(const QStringList &acceptedMimeTypes); + public Q_SLOTS: void accepted(const QStringList &files); void accepted(const QVariant &files); diff --git a/src/webengine/ui_delegates_manager.cpp b/src/webengine/ui_delegates_manager.cpp index 19274bedf..756b3429e 100644 --- a/src/webengine/ui_delegates_manager.cpp +++ b/src/webengine/ui_delegates_manager.cpp @@ -486,6 +486,8 @@ void UIDelegatesManager::showFilePicker(QSharedPointer<FilePickerController> con Q_UNREACHABLE(); } + filePicker->setProperty("nameFilters", FilePickerController::nameFilters(controller->acceptedMimeTypes())); + QQmlProperty filesPickedSignal(filePicker, QStringLiteral("onFilesSelected")); CHECK_QML_SIGNAL_PROPERTY(filesPickedSignal, filePickerComponent->url()); QQmlProperty rejectSignal(filePicker, QStringLiteral("onRejected")); diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 524df0425..1a8eae0b9 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -2292,14 +2292,12 @@ ASSERT_ENUMS_MATCH(FilePickerController::OpenMultiple, QWebEnginePage::FileSelec QStringList QWebEnginePage::chooseFiles(FileSelectionMode mode, const QStringList &oldFiles, const QStringList &acceptedMimeTypes) { #if QT_CONFIG(filedialog) - // FIXME: Should we expose this in QWebPage's API ? Right now it is very open and can contain a mix and match of file extensions (which QFileDialog - // can work with) and mimetypes ranging from text/plain or images/* to application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - Q_UNUSED(acceptedMimeTypes); + const QStringList &filter = FilePickerController::nameFilters(acceptedMimeTypes); QStringList ret; QString str; switch (static_cast<FilePickerController::FileChooserMode>(mode)) { case FilePickerController::OpenMultiple: - ret = QFileDialog::getOpenFileNames(view(), QString()); + ret = QFileDialog::getOpenFileNames(view(), QString(), QString(), filter.join(";;"), nullptr, QFileDialog::HideNameFilterDetails); break; // Chromium extension, not exposed as part of the public API for now. case FilePickerController::UploadFolder: @@ -2313,7 +2311,7 @@ QStringList QWebEnginePage::chooseFiles(FileSelectionMode mode, const QStringLis ret << str; break; case FilePickerController::Open: - str = QFileDialog::getOpenFileName(view(), QString(), oldFiles.first()); + str = QFileDialog::getOpenFileName(view(), QString(), oldFiles.first(), filter.join(";;"), nullptr, QFileDialog::HideNameFilterDetails); if (!str.isNull()) ret << str; break; diff --git a/tests/auto/quick/qmltests/data/accepttypes.html b/tests/auto/quick/qmltests/data/accepttypes.html new file mode 100644 index 000000000..aff39f96e --- /dev/null +++ b/tests/auto/quick/qmltests/data/accepttypes.html @@ -0,0 +1,21 @@ +<html> +<head> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<title>Default title</title> +</head> + +<body> +<input type="file" name="file" id="upfile" accept=""> + +<script> +window.onload = function() { + document.getElementById("upfile").focus(); +} + +function setAcceptType(acceptType) { + document.getElementById("upfile").accept = acceptType; + document.title = acceptType; +} +</script> +</body> +</html> diff --git a/tests/auto/quick/qmltests/data/tst_filePicker.qml b/tests/auto/quick/qmltests/data/tst_filePicker.qml index fad81273c..c9572224e 100644 --- a/tests/auto/quick/qmltests/data/tst_filePicker.qml +++ b/tests/auto/quick/qmltests/data/tst_filePicker.qml @@ -61,6 +61,7 @@ TestWebEngineView { FilePickerParams.filePickerOpened = false FilePickerParams.selectFiles = false FilePickerParams.selectedFilesUrl = [] + FilePickerParams.nameFilters = [] titleSpy.clear() terminationSpy.clear() } @@ -260,5 +261,33 @@ TestWebEngineView { tryCompare(webEngineView, "title", row.expected); webEngineView.fileDialogRequested.disconnect(acceptedFileHandler); } + + function test_acceptFileTypes_data() { + return [ + { tag: "CustomSuffix", input: ".pug", expected: ".pug", exactMatch: false}, + { tag: "CustomMime", input: "dog/pug", expected: "Accepted types ()", exactMatch: true}, + { tag: "CustomGlob", input: "dog/*", expected: "Accepted types ()", exactMatch: true}, + { tag: "Invalid", input: "---", expected: "Accepted types ()", exactMatch: true}, + { tag: "Jpeg", input: "image/jpeg", expected: ".jpeg", exactMatch: false} + ]; + } + + function test_acceptFileTypes(row) { + var expectedFileName; + + webEngineView.url = Qt.resolvedUrl("accepttypes.html"); + verify(webEngineView.waitForLoadSucceeded()); + + webEngineView.runJavaScript("setAcceptType('" + row.input + "');"); + tryCompare(webEngineView, "title", row.input); + + keyClick(Qt.Key_Enter); // Focus is on the button. Open FileDialog. + tryCompare(FilePickerParams, "filePickerOpened", true); + + if (row.exactMatch) + compare(FilePickerParams.nameFilters[0], row.expected); + else + verify(FilePickerParams.nameFilters[0].includes(row.expected)); + } } } diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/Controls1Delegates/FilePicker.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/Controls1Delegates/FilePicker.qml index 5d78807df..745f533f5 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/Controls1Delegates/FilePicker.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/Controls1Delegates/FilePicker.qml @@ -33,12 +33,14 @@ QtObject { property bool selectMultiple: false; property bool selectExisting: false; property bool selectFolder: false; + property var nameFilters: []; signal filesSelected(var fileList); signal rejected(); function open() { FilePickerParams.filePickerOpened = true; + FilePickerParams.nameFilters = nameFilters; if (FilePickerParams.selectFiles) filesSelected(FilePickerParams.selectedFilesUrl) else diff --git a/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml b/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml index 83ac8a66e..02b0da1d4 100644 --- a/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml +++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml @@ -33,4 +33,5 @@ QtObject { property var selectedFilesUrl: []; property bool selectFiles: false; property bool filePickerOpened: false; + property var nameFilters: []; } diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro index 00e884e11..071795d96 100644 --- a/tests/auto/quick/qmltests/qmltests.pro +++ b/tests/auto/quick/qmltests/qmltests.pro @@ -6,6 +6,7 @@ IMPORTPATH += $$PWD/data OTHER_FILES += \ $$PWD/data/TestWebEngineView.qml \ + $$PWD/data/accepttypes.html \ $$PWD/data/alert.html \ $$PWD/data/append-document-title.js \ $$PWD/data/big-user-script.js \ |