From c34b72183e85e7c615ca65381d8591cdca23faf2 Mon Sep 17 00:00:00 2001 From: Pierre Rossi Date: Mon, 25 Nov 2013 15:38:24 +0100 Subject: [Widgets] wire the file pickers Introduce a new version of chooseFiles in QWebEnginePage. The existing API in WebKit1 seemed a bit dusty in any case (multiple only supported via extensions). Changes are: * oldFile becomes oldFiles, so that we could at a later stage expose the already selected files in the "multiple" case. * a type is introduced, for now limited to multiple selection, but over time, we might consider additions such as directory upload. Change-Id: I14cfea64ce95e892a0a1877c8cb914c5a421409f Reviewed-by: Jocelyn Turcotte --- src/core/type_conversion.h | 28 +++++++++++++++++++ src/core/web_contents_adapter.cpp | 27 ++++++++++++++++++ src/core/web_contents_adapter.h | 1 + src/core/web_contents_adapter_client.h | 10 +++++++ src/core/web_contents_delegate_qt.cpp | 15 ++++++++++ src/core/web_contents_delegate_qt.h | 2 ++ src/webengine/api/qquickwebengineview_p_p.h | 3 ++ src/webenginewidgets/api/qwebenginepage.cpp | 43 +++++++++++++++++++++++++++++ src/webenginewidgets/api/qwebenginepage.h | 19 ++++--------- src/webenginewidgets/api/qwebenginepage_p.h | 2 ++ 10 files changed, 137 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h index 659096b6e..a1c39581f 100644 --- a/src/core/type_conversion.h +++ b/src/core/type_conversion.h @@ -51,6 +51,7 @@ #include "third_party/skia/include/utils/SkMatrix44.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/rect.h" +#include "ui/shell_dialogs/selected_file_info.h" #include "url/gurl.h" inline QString toQt(const base::string16 &string) @@ -134,4 +135,31 @@ inline base::FilePath::StringType toFilePathString(const QString &str) #endif } +template +inline T fileListingHelper(const QString &) {qFatal("Specialization missing for %s.", Q_FUNC_INFO);} + +template <> +inline ui::SelectedFileInfo fileListingHelper(const QString &file) +{ + base::FilePath fp(toFilePathString(file)); + return ui::SelectedFileInfo(fp, fp); +} + +template <> +inline base::FilePath fileListingHelper(const QString &file) +{ + return base::FilePath(toFilePathString(file)); +} + + +template +inline std::vector toVector(const QStringList &fileList) +{ + std::vector selectedFiles; + selectedFiles.reserve(fileList.size()); + Q_FOREACH (const QString &file, fileList) + selectedFiles.push_back(fileListingHelper(file)); + return selectedFiles; +} + #endif // TYPE_CONVERSION_H diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index f3caa7478..7542f35bd 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -55,8 +55,10 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/page_zoom.h" #include "content/public/common/renderer_preferences.h" +#include "ui/shell_dialogs/selected_file_info.h" #include +#include #include #include @@ -146,6 +148,19 @@ static void callbackOnEvaluateJS(JSCallbackBase *callback, const base::Value *re delete callback; } +static QStringList listRecursively(const QDir& dir) { + QStringList ret; + QFileInfoList infoList(dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot |QDir::Hidden)); + Q_FOREACH (const QFileInfo &fileInfo, infoList) { + if (fileInfo.isDir()) { + ret.append(fileInfo.absolutePath() + QStringLiteral("/.")); // Match chromium's behavior. See chrome/browser/file_select_helper.cc + ret.append(listRecursively(QDir(fileInfo.absoluteFilePath()))); + } else + ret.append(fileInfo.absoluteFilePath()); + } + return ret; +} + class WebContentsAdapterPrivate { public: WebContentsAdapterPrivate(WebContentsAdapterClient::RenderingMode renderingMode); @@ -355,3 +370,15 @@ void WebContentsAdapter::dpiScaleChanged() if (impl) impl->NotifyScreenInfoChanged(); } + +void WebContentsAdapter::filesSelectedInChooser(const QStringList &fileList, WebContentsAdapterClient::FileChooserMode mode) +{ + Q_D(WebContentsAdapter); + content::RenderViewHost *rvh = d->webContents->GetRenderViewHost(); + Q_ASSERT(rvh); + QStringList files(fileList); + if (mode == WebContentsAdapterClient::UploadFolder && !fileList.isEmpty() + && QFileInfo(fileList.first()).isDir()) // Enumerate the directory + files = listRecursively(QDir(fileList.first())); + rvh->FilesSelectedInChooser(toVector(files), static_cast(mode)); +} diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index 4bad8aa9b..5379afb70 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -88,6 +88,7 @@ public: qreal currentZoomFactor() const; void enableInspector(bool); void runJavaScript(const QString &javaScript, const QString &xPath = QString(), JSCallbackBase * = 0); + void filesSelectedInChooser(const QStringList &fileList, WebContentsAdapterClient::FileChooserMode); void dpiScaleChanged(); diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index 72754e9dc..2231fd46b 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -45,6 +45,7 @@ #include #include +#include #include class RenderWidgetHostViewQt; @@ -99,6 +100,14 @@ public: PromptDialog }; + // Must match the ones in file_chooser_params.h + enum FileChooserMode { + Open, + OpenMultiple, + UploadFolder, + Save + }; + virtual ~WebContentsAdapterClient() { } virtual RenderWidgetHostViewQtDelegate* CreateRenderWidgetHostViewQtDelegate(RenderWidgetHostViewQtDelegateClient *client, RenderingMode mode) = 0; @@ -115,6 +124,7 @@ public: virtual void close() = 0; virtual bool contextMenuRequested(const WebEngineContextMenuData&) = 0; virtual bool javascriptDialog(JavascriptDialogType type, const QString &message, const QString &defaultValue = QString(), QString *result = 0) = 0; + virtual void runFileChooser(FileChooserMode, const QString &defaultFileName, const QStringList &acceptedMimeTypes) = 0; }; #endif // WEB_CONTENTS_ADAPTER_CLIENT_H diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp index 5c5026f78..6dd8121dd 100644 --- a/src/core/web_contents_delegate_qt.cpp +++ b/src/core/web_contents_delegate_qt.cpp @@ -50,6 +50,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/browser/invalidate_type.h" #include "content/public/common/favicon_url.h" +#include "content/public/common/file_chooser_params.h" WebContentsDelegateQt::WebContentsDelegateQt(content::WebContents *webContents, WebContentsAdapterClient *adapterClient) : m_viewClient(adapterClient) @@ -130,3 +131,17 @@ content::JavaScriptDialogManager *WebContentsDelegateQt::GetJavaScriptDialogMana { return JavaScriptDialogManagerQt::GetInstance(); } + +Q_STATIC_ASSERT_X(static_cast(WebContentsAdapterClient::Open) == static_cast(content::FileChooserParams::Open), "Enums out of sync"); +Q_STATIC_ASSERT_X(static_cast(WebContentsAdapterClient::Save) == static_cast(content::FileChooserParams::Save), "Enums out of sync"); + +void WebContentsDelegateQt::RunFileChooser(content::WebContents *web_contents, const content::FileChooserParams ¶ms) +{ + Q_UNUSED(web_contents) + QStringList acceptedMimeTypes; + acceptedMimeTypes.reserve(params.accept_types.size()); + for (std::vector::const_iterator it = params.accept_types.begin(); it < params.accept_types.end(); ++it) + acceptedMimeTypes.append(toQt(*it)); + + m_viewClient->runFileChooser(static_cast(params.mode), toQt(params.default_file_name.value()), acceptedMimeTypes); +} diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h index b0cfd8952..e6d481469 100644 --- a/src/core/web_contents_delegate_qt.h +++ b/src/core/web_contents_delegate_qt.h @@ -46,6 +46,7 @@ #include "content/public/browser/web_contents_observer.h" #include "javascript_dialog_manager_qt.h" +#include namespace content { class BrowserContext; @@ -73,6 +74,7 @@ public: virtual void DidUpdateFaviconURL(int32 page_id, const std::vector& candidates) Q_DECL_OVERRIDE; virtual void DidFailProvisionalLoad(int64 frame_id, bool is_main_frame, const GURL& validated_url, int error_code, const string16& error_description, content::RenderViewHost* render_view_host) Q_DECL_OVERRIDE; virtual content::JavaScriptDialogManager *GetJavaScriptDialogManager() Q_DECL_OVERRIDE; + virtual void RunFileChooser(content::WebContents *, const content::FileChooserParams ¶ms) Q_DECL_OVERRIDE; private: WebContentsAdapterClient *m_viewClient; diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index f43bb17fa..110a1b9b6 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -46,6 +46,8 @@ #include "web_contents_adapter_client.h" #include +#include +#include #include class WebContentsAdapter; @@ -125,6 +127,7 @@ public: virtual void close() Q_DECL_OVERRIDE; virtual bool contextMenuRequested(const WebEngineContextMenuData &) Q_DECL_OVERRIDE { return false;} virtual bool javascriptDialog(JavascriptDialogType type, const QString &message, const QString &defaultValue = QString(), QString *result = 0) Q_DECL_OVERRIDE { return false; } + virtual void runFileChooser(FileChooserMode, const QString &defaultFileName, const QStringList &acceptedMimeTypes) { Q_UNUSED(defaultFileName); Q_UNUSED(acceptedMimeTypes);} void setDevicePixelRatio(qreal); diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 6214c0f78..3f4287a02 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -33,11 +33,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include QT_BEGIN_NAMESPACE @@ -408,6 +410,19 @@ QMenu *QWebEnginePage::createStandardContextMenu() return menu; } +static inline QWebEnginePage::FileSelectionMode toPublic(WebContentsAdapterClient::FileChooserMode mode) +{ + // Should the underlying values change, we'll need a switch here. + return static_cast(mode); +} + +void QWebEnginePagePrivate::runFileChooser(WebContentsAdapterClient::FileChooserMode mode, const QString &defaultFileName, const QStringList &acceptedMimeTypes) +{ + Q_Q(QWebEnginePage); + QStringList selectedFileNames = q->chooseFiles(toPublic(mode), (QStringList() << defaultFileName), acceptedMimeTypes); + adapter->filesSelectedInChooser(selectedFileNames, mode); +} + void QWebEnginePage::load(const QUrl& url) { Q_D(QWebEnginePage); @@ -476,6 +491,34 @@ QWebEnginePage *QWebEnginePage::createWindow(WebWindowType type) return 0; } +Q_STATIC_ASSERT_X(static_cast(WebContentsAdapterClient::Open) == static_cast(QWebEnginePage::FileSelectOpen), "Enums out of sync"); +Q_STATIC_ASSERT_X(static_cast(WebContentsAdapterClient::OpenMultiple) == static_cast(QWebEnginePage::FileSelectOpenMultiple), "Enums out of sync"); + +QStringList QWebEnginePage::chooseFiles(FileSelectionMode mode, const QStringList &oldFiles, const QStringList &acceptedMimeTypes) +{ + // 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); + QStringList ret; + switch (static_cast(mode)) { + case WebContentsAdapterClient::OpenMultiple: + ret = QFileDialog::getOpenFileNames(view(), QString()); + break; + // Chromium extension, not exposed as part of the public API for now. + case WebContentsAdapterClient::UploadFolder: + ret << QFileDialog::getExistingDirectory(view(), tr("Select folder to upload")) + QLatin1Char('/'); + break; + case WebContentsAdapterClient::Save: + ret << QFileDialog::getSaveFileName(view(), QString(), (QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + oldFiles.first())); + break; + default: + case WebContentsAdapterClient::Open: + ret << QFileDialog::getOpenFileName(view(), QString(), oldFiles.first()); + break; + } + return ret; +} + void QWebEnginePage::javaScriptAlert(QWebEngineFrame *originatingFrame, const QString &msg) { Q_UNUSED(originatingFrame); diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h index a613f4a27..4d6a72fc4 100644 --- a/src/webenginewidgets/api/qwebenginepage.h +++ b/src/webenginewidgets/api/qwebenginepage.h @@ -269,6 +269,11 @@ public: AutoOwnership }; + enum FileSelectionMode { + FileSelectOpen, + FileSelectOpenMultiple, + }; + class QWEBENGINEWIDGETS_EXPORT ViewportAttributes { public: ViewportAttributes(); @@ -375,7 +380,6 @@ public: bool supportsContentType(const QString& mimeType) const; enum Extension { - ChooseMultipleFilesExtension, ErrorPageExtension }; class ExtensionOption @@ -383,17 +387,6 @@ public: class ExtensionReturn {}; - class ChooseMultipleFilesExtensionOption : public ExtensionOption { - public: - QWebEngineFrame *parentFrame; - QStringList suggestedFileNames; - }; - - class ChooseMultipleFilesExtensionReturn : public ExtensionReturn { - public: - QStringList fileNames; - }; - enum ErrorDomain { QtNetwork, Http, WebKit }; class ErrorPageExtensionOption : public ExtensionOption { public: @@ -558,7 +551,7 @@ protected: virtual QObject *createPlugin(const QString &classid, const QUrl &url, const QStringList ¶mNames, const QStringList ¶mValues) { Q_UNUSED(classid); Q_UNUSED(url); Q_UNUSED(paramNames); Q_UNUSED(paramValues); Q_UNREACHABLE(); return 0; } virtual bool acceptNavigationRequest(QWebEngineFrame *frame, const QNetworkRequest &request, NavigationType type) { Q_UNUSED(frame); Q_UNUSED(request); Q_UNUSED(type); Q_UNREACHABLE(); return false; } - virtual QString chooseFile(QWebEngineFrame *originatingFrame, const QString& oldFile) { Q_UNUSED(originatingFrame); Q_UNUSED(oldFile); Q_UNREACHABLE(); return QString(); } + virtual QStringList chooseFiles(FileSelectionMode mode, const QStringList &oldFiles, const QStringList &acceptedMimeTypes); virtual void javaScriptAlert(QWebEngineFrame *originatingFrame, const QString& msg); virtual bool javaScriptConfirm(QWebEngineFrame *originatingFrame, const QString& msg); virtual bool javaScriptPrompt(QWebEngineFrame *originatingFrame, const QString& msg, const QString& defaultValue, QString* result); diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index a8df3ba6a..8901f5ec4 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -46,6 +46,7 @@ #include "web_contents_adapter_client.h" #include +#include #include class RenderWidgetHostViewQtDelegate; @@ -78,6 +79,7 @@ public: virtual void close() Q_DECL_OVERRIDE; virtual bool contextMenuRequested(const WebEngineContextMenuData &data) Q_DECL_OVERRIDE; virtual bool javascriptDialog(JavascriptDialogType type, const QString &message, const QString &defaultValue = QString(), QString *result = 0) Q_DECL_OVERRIDE; + virtual void runFileChooser(FileChooserMode, const QString &defaultFileName, const QStringList &acceptedMimeTypes) Q_DECL_OVERRIDE; void updateAction(QWebEnginePage::WebAction) const; void updateNavigationActions(); -- cgit v1.2.3