diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h
index 659096b6e91dbb312edc66f6d66dedae14995aef..a1c39581f72a9f79c978d006cde302c6db62f348 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 <typename T>
+inline T fileListingHelper(const QString &) {qFatal("Specialization missing for %s.", Q_FUNC_INFO);}
+
+template <>
+inline ui::SelectedFileInfo fileListingHelper<ui::SelectedFileInfo>(const QString &file)
+{
+    base::FilePath fp(toFilePathString(file));
+    return ui::SelectedFileInfo(fp, fp);
+}
+
+template <>
+inline base::FilePath fileListingHelper<base::FilePath>(const QString &file)
+{
+    return base::FilePath(toFilePathString(file));
+}
+
+
+template <typename T>
+inline std::vector<T> toVector(const QStringList &fileList)
+{
+    std::vector<T> selectedFiles;
+    selectedFiles.reserve(fileList.size());
+    Q_FOREACH (const QString &file, fileList)
+        selectedFiles.push_back(fileListingHelper<T>(file));
+    return selectedFiles;
+}
+
 #endif // TYPE_CONVERSION_H
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index f3caa747816e585821416293fce7cc72441e5244..7542f35bd56c86ad65411f293c4ae5f73f20a49d 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 <QGuiApplication>
+#include <QStringList>
 #include <QStyleHints>
 #include <QVariant>
 
@@ -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<ui::SelectedFileInfo>(files), static_cast<content::FileChooserParams::Mode>(mode));
+}
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index 4bad8aa9b617c1fc28a6845678d4a4fcd5ae81a6..5379afb702544d11483f3963f0dad5c44b0d4a76 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 72754e9dc6b25a80b94d2fc2c7cd1a490d2d5064..2231fd46b0f09358a2a934d42e825ba6eb292923 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -45,6 +45,7 @@
 
 #include <QRect>
 #include <QString>
+#include <QStringList>
 #include <QUrl>
 
 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 5c5026f78f6d2f03b7a14e8fc1c98303759e84ab..6dd8121dd39d9194ff494264680bfc19a60dbd4c 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<int>(WebContentsAdapterClient::Open) == static_cast<int>(content::FileChooserParams::Open), "Enums out of sync");
+Q_STATIC_ASSERT_X(static_cast<int>(WebContentsAdapterClient::Save) == static_cast<int>(content::FileChooserParams::Save), "Enums out of sync");
+
+void WebContentsDelegateQt::RunFileChooser(content::WebContents *web_contents, const content::FileChooserParams &params)
+{
+    Q_UNUSED(web_contents)
+    QStringList acceptedMimeTypes;
+    acceptedMimeTypes.reserve(params.accept_types.size());
+    for (std::vector<base::string16>::const_iterator it = params.accept_types.begin(); it < params.accept_types.end(); ++it)
+        acceptedMimeTypes.append(toQt(*it));
+
+    m_viewClient->runFileChooser(static_cast<WebContentsAdapterClient::FileChooserMode>(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 b0cfd895264093fd7706049644659c29b9e8f6e6..e6d481469aa684da3e3ae583b0dffba7e94f678e 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 <QtCore/qcompilerdetection.h>
 
 namespace content {
     class BrowserContext;
@@ -73,6 +74,7 @@ public:
     virtual void DidUpdateFaviconURL(int32 page_id, const std::vector<content::FaviconURL>& 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 &params) 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 f43bb17faf1bcd0b0c3beb2bc617a526b79b2e32..110a1b9b607eb1f3a41deb03e5c7412034c4a740 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 <QScopedPointer>
+#include <QSharedData>
+#include <QtCore/qcompilerdetection.h>
 #include <QtQuick/private/qquickitem_p.h>
 
 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 6214c0f7823d735c13af81511ff8eac50c839554..3f4287a02c2dbb6012a9bcaf21bb98d495ac813b 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -33,11 +33,13 @@
 #include <QAction>
 #include <QApplication>
 #include <QClipboard>
+#include <QFileDialog>
 #include <QIcon>
 #include <QInputDialog>
 #include <QLayout>
 #include <QMenu>
 #include <QMessageBox>
+#include <QStandardPaths>
 #include <QUrl>
 
 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<QWebEnginePage::FileSelectionMode>(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<int>(WebContentsAdapterClient::Open) == static_cast<int>(QWebEnginePage::FileSelectOpen), "Enums out of sync");
+Q_STATIC_ASSERT_X(static_cast<int>(WebContentsAdapterClient::OpenMultiple) == static_cast<int>(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<WebContentsAdapterClient::FileChooserMode>(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 a613f4a2786c686ba3c17ff09cf0205ac87616b4..4d6a72fc40fa8eb0ed688dd50049740edcab1c15 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 &paramNames, const QStringList &paramValues) { 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 a8df3ba6a18873041f472737febe5c359b60cb55..8901f5ec46a5ccb03add69d01dfe6164f7947bd4 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 <QtCore/private/qobject_p.h>
+#include <QtCore/qcompilerdetection.h>
 #include <QSharedData>
 
 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();